12
12
#include <linux/debugfs.h>
13
13
#include <linux/percpu.h>
14
14
#include <linux/smp.h>
15
+ #include <linux/kfifo.h>
16
+ #include <linux/slab.h>
15
17
#include <asm/tdx.h>
16
18
#include <asm/trace/tdx.h>
17
19
@@ -21,13 +23,90 @@ static bool fuzz_tdcall;
21
23
static bool fuzz_errors ;
22
24
static u16 fuzz_num_bits = 2 ;
23
25
static bool fuzz_early_seed ;
26
+ static bool fallback_enabled = true;
27
+ static unsigned long inject_active ;
28
+
29
+ #define FUZZ_FIFO_LEN 4096
30
+ #define FALLBACK_FIFO_LEN 256
31
+ #define FALLBACK_FIFO_USED 199
32
+
33
+ /*
34
+ * This all is not very cache line friendly.
35
+ * Assume fuzzed TDX guests don't have many vcpus for now.
36
+ *
37
+ * Maintain a fallback fifo to loop existing data in case
38
+ * the input doesn't feed enough.
39
+ */
40
+ struct fuzz_fifo {
41
+ DECLARE_KFIFO (inject , u64 , FUZZ_FIFO_LEN );
42
+ DECLARE_KFIFO (fallback , u64 , FALLBACK_FIFO_LEN );
43
+ };
44
+ static struct fuzz_fifo * inject_fifo ;
45
+ /* protect fifos for multiple readers and most stats */
46
+ static DEFINE_SPINLOCK (inject_lock );
47
+ /* Statistics */
48
+ static u64 inject_success ;
49
+ static u64 inject_fallback ;
50
+ static u64 inject_miss_no_fallback = ATOMIC_INIT (0 );
51
+ static atomic_t inject_nmi_conflict ;
52
+
53
+ static bool get_inject_val (enum tdx_fuzz_loc loc , u64 * valp )
54
+ {
55
+ struct fuzz_fifo * f = & inject_fifo [loc ];
56
+
57
+ if (kfifo_out (& f -> inject , valp , 1 ) != 1 ) {
58
+ if (!fallback_enabled )
59
+ return false;
60
+ if (!kfifo_out (& f -> fallback , valp , 1 )) {
61
+ inject_miss_no_fallback ++ ;
62
+ return false;
63
+ }
64
+ inject_fallback ++ ;
65
+ } else {
66
+ inject_success ++ ;
67
+ if (kfifo_size (& f -> fallback ) == FALLBACK_FIFO_USED )
68
+ kfifo_skip (& f -> fallback );
69
+ }
70
+ kfifo_in (& f -> fallback , valp , 1 );
71
+ return true;
72
+ }
73
+
74
+ static bool fuzz_inject (enum tdx_fuzz_loc loc , u64 * valp )
75
+ {
76
+ bool ok ;
77
+
78
+ if (in_nmi ()) {
79
+ if (!spin_trylock (& inject_lock )) {
80
+ atomic_inc (& inject_nmi_conflict );
81
+ return false;
82
+ }
83
+ ok = get_inject_val (loc , valp );
84
+ spin_unlock (& inject_lock );
85
+ } else {
86
+ unsigned long flags ;
87
+ spin_lock_irqsave (& inject_lock , flags );
88
+ ok = get_inject_val (loc , valp );
89
+ spin_unlock_irqrestore (& inject_lock , flags );
90
+ }
91
+ return ok ;
92
+ }
24
93
25
94
static u64 __tdx_fuzz (u64 var , int bits , enum tdx_fuzz_loc loc )
26
95
{
27
96
struct rnd_state * rndstate ;
28
97
unsigned num_bits ;
29
98
u64 oldvar = var ;
30
99
100
+ if (READ_ONCE (inject_active )) {
101
+ u64 newvar ;
102
+ if (fuzz_inject (loc , & newvar )) {
103
+ var = newvar ;
104
+ trace_tdx_fuzz ((u64 )__builtin_return_address (0 ),
105
+ bits , oldvar , newvar , loc );
106
+ }
107
+ return var ;
108
+ }
109
+
31
110
get_cpu ();
32
111
rndstate = this_cpu_ptr (& fuzz_rndstate );
33
112
num_bits = READ_ONCE (fuzz_num_bits );
@@ -58,6 +137,11 @@ bool tdx_fuzz_err(enum tdx_fuzz_loc loc)
58
137
if (!fuzz_errors || !should_fail (& tdx_fault , 1 ))
59
138
return false;
60
139
140
+ if (READ_ONCE (inject_active )) {
141
+ u64 res = 0 ;
142
+ return fuzz_inject (loc , & res );
143
+ }
144
+
61
145
trace_tdx_fuzz ((u64 )__builtin_return_address (0 ), 1 , 0 , 1 , loc );
62
146
return true;
63
147
}
@@ -111,9 +195,76 @@ static int __init tdx_fuzz_setup(char *str)
111
195
}
112
196
early_param ("fail_tdx" , tdx_fuzz_setup );
113
197
198
+ /*
199
+ * Injection allows a feedbacker fuzzer to provide values.
200
+ */
201
+
202
+ static int inject_open (struct inode * inode , struct file * file )
203
+ {
204
+ if (test_and_set_bit (0 , & inject_active ))
205
+ return - EBUSY ;
206
+ if (inject_fifo == NULL ) {
207
+ struct fuzz_fifo * infifo ;
208
+ int i ;
209
+
210
+ infifo = kvmalloc (TDX_FUZZ_MAX * sizeof (struct fuzz_fifo ),
211
+ GFP_KERNEL );
212
+ if (!infifo ) {
213
+ clear_bit (0 , & inject_active );
214
+ return - ENOMEM ;
215
+ }
216
+ for (i = 0 ; i < TDX_FUZZ_MAX ; i ++ ) {
217
+ INIT_KFIFO (infifo [i ].inject );
218
+ INIT_KFIFO (infifo [i ].fallback );
219
+ }
220
+ WRITE_ONCE (inject_fifo , infifo );
221
+ }
222
+ return 0 ;
223
+ }
224
+
225
+ static int inject_release (struct inode * inode , struct file * file )
226
+ {
227
+ clear_bit (0 , & inject_active );
228
+ /* Never free the fifos */
229
+ return 0 ;
230
+ }
231
+
232
+ static ssize_t inject_write (struct file * f , const char __user * buf ,
233
+ size_t len , loff_t * off )
234
+ {
235
+ int num , i ;
236
+ unsigned copied ;
237
+
238
+ if (len % 16 )
239
+ return - EINVAL ;
240
+ num = len / 16 ;
241
+ for (i = 0 ; i < num ; i ++ ) {
242
+ u64 loc ;
243
+ if (get_user (loc , buf ))
244
+ return - EFAULT ;
245
+ if (loc >= TDX_FUZZ_MAX )
246
+ return - EINVAL ;
247
+ buf += 8 ;
248
+ if (kfifo_from_user (& inject_fifo [loc ].inject , buf , 8 , & copied ))
249
+ return - EFAULT ;
250
+ if (copied != 8 )
251
+ return i * 16 ;
252
+ buf += 8 ;
253
+ }
254
+ return len ;
255
+ }
256
+
257
+ static struct file_operations inject_fops = {
258
+ .owner = THIS_MODULE ,
259
+ .open = inject_open ,
260
+ .release = inject_release ,
261
+ .write = inject_write ,
262
+ .llseek = no_llseek ,
263
+ };
264
+
114
265
static int __init tdx_fuzz_init (void )
115
266
{
116
- struct dentry * dbp ;
267
+ struct dentry * dbp , * statp ;
117
268
118
269
dbp = fault_create_debugfs_attr ("fail_tdx" , NULL , & tdx_fault );
119
270
if (!dbp )
@@ -128,6 +279,16 @@ static int __init tdx_fuzz_init(void)
128
279
debugfs_create_u16 ("num_change_bits" , 0600 , dbp , & fuzz_num_bits );
129
280
debugfs_create_file ("seed" , 0200 , dbp , NULL , & fuzz_seed_fops );
130
281
282
+ debugfs_create_bool ("fallback" , 0600 , dbp , & fallback_enabled );
283
+ debugfs_create_file ("inject" , 0600 , dbp , NULL , & inject_fops );
284
+ statp = debugfs_create_dir ("stats" , dbp );
285
+ debugfs_create_u64 ("inject_success" , 0600 , statp , & inject_success );
286
+ debugfs_create_u64 ("inject_fallback" , 0600 , statp , & inject_fallback );
287
+ debugfs_create_u64 ("inject_miss_no_fallback" , 0600 , statp ,
288
+ & inject_miss_no_fallback );
289
+ debugfs_create_atomic_t ("inject_nmi_conflict" , 0600 , statp ,
290
+ & inject_nmi_conflict );
291
+
131
292
if (!fuzz_early_seed )
132
293
fuzz_init_seed (get_random_u64 ());
133
294
return 0 ;
0 commit comments