Skip to content

Commit a4241ab

Browse files
Andi KleenKuppuswamy Sathyanarayanan
authored andcommitted
x86/tdx: Injection interface for fuzzer
Allow a user space fuzzer inject values to fuzz for the TDCALLs. Add a new inject interface in debugfs. A user can write pairs of u64. The first is the tdx location (see enum tdx_fuzz_location), the second is the value to inject. When the injector runs out of values it will loop a small portion of the previous data. That's called a fallback. Only one fuzz injector can be active at a time. Also add some statistics in debugfs to count various conditions, e.g. how often fallbacks occur . Signed-off-by: Andi Kleen <[email protected]>
1 parent 64ad614 commit a4241ab

File tree

2 files changed

+185
-1
lines changed

2 files changed

+185
-1
lines changed

Documentation/fault-injection/fault-injection.rst

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,29 @@ configuration of fault-injection capabilities.
218218
when set to 64 the whole value is replaced with a randon number.
219219
otherwise the number of bits specified are randomly flipped.
220220

221+
- /sys/kernel/debug/fail_tdx/inject
222+
223+
a file that allows to write values to inject as output values
224+
of TDCALL operations. This replaces the random bit flipping
225+
when the file is open. Each write must be 16 bytes, with the
226+
first 8 bytes being a location of the TDCALL (see
227+
arch/x86/include/asm/tdx.h:tdx_fuzz_loc), and the second 8 bytes
228+
the injected output value. This is intended for feedback fuzzers
229+
to inject targetted values.
230+
231+
- /sys/kernel/debug/fail_tdx/fallback
232+
233+
when set to true loop over the last inject values when the
234+
inject input buffer runs out.
235+
236+
- /sys/kernel/debug/fail_tdx/stats/*
237+
238+
some statistic counts on the value injection:
239+
inject_success: Number of successfull injections
240+
inject_miss_no_fallback: Injection failed with no input data
241+
inject_fallback: Injection ran out of data and failed back to previous values.
242+
inject_nmi_conflict: Injection race with NMIs.
243+
221244
Boot option
222245
^^^^^^^^^^^
223246

arch/x86/kernel/tdx-fuzz.c

Lines changed: 162 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
#include <linux/debugfs.h>
1313
#include <linux/percpu.h>
1414
#include <linux/smp.h>
15+
#include <linux/kfifo.h>
16+
#include <linux/slab.h>
1517
#include <asm/tdx.h>
1618
#include <asm/trace/tdx.h>
1719

@@ -21,13 +23,90 @@ static bool fuzz_tdcall;
2123
static bool fuzz_errors;
2224
static u16 fuzz_num_bits = 2;
2325
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+
}
2493

2594
static u64 __tdx_fuzz(u64 var, int bits, enum tdx_fuzz_loc loc)
2695
{
2796
struct rnd_state *rndstate;
2897
unsigned num_bits;
2998
u64 oldvar = var;
3099

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+
31110
get_cpu();
32111
rndstate = this_cpu_ptr(&fuzz_rndstate);
33112
num_bits = READ_ONCE(fuzz_num_bits);
@@ -58,6 +137,11 @@ bool tdx_fuzz_err(enum tdx_fuzz_loc loc)
58137
if (!fuzz_errors || !should_fail(&tdx_fault, 1))
59138
return false;
60139

140+
if (READ_ONCE(inject_active)) {
141+
u64 res = 0;
142+
return fuzz_inject(loc, &res);
143+
}
144+
61145
trace_tdx_fuzz((u64)__builtin_return_address(0), 1, 0, 1, loc);
62146
return true;
63147
}
@@ -111,9 +195,76 @@ static int __init tdx_fuzz_setup(char *str)
111195
}
112196
early_param("fail_tdx", tdx_fuzz_setup);
113197

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+
114265
static int __init tdx_fuzz_init(void)
115266
{
116-
struct dentry *dbp;
267+
struct dentry *dbp, *statp;
117268

118269
dbp = fault_create_debugfs_attr("fail_tdx", NULL, &tdx_fault);
119270
if (!dbp)
@@ -128,6 +279,16 @@ static int __init tdx_fuzz_init(void)
128279
debugfs_create_u16("num_change_bits", 0600, dbp, &fuzz_num_bits);
129280
debugfs_create_file("seed", 0200, dbp, NULL, &fuzz_seed_fops);
130281

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+
131292
if (!fuzz_early_seed)
132293
fuzz_init_seed(get_random_u64());
133294
return 0;

0 commit comments

Comments
 (0)