Skip to content

Commit e3c9abd

Browse files
mykyta5anakryiko
authored andcommitted
selftests/bpf: Implement setting global variables in veristat
To better verify some complex BPF programs we'd like to preset global variables. This patch introduces CLI argument `--set-global-vars` or `-G` to veristat, that allows presetting values to global variables defined in BPF program. For example: prog.c: ``` enum Enum { ELEMENT1 = 0, ELEMENT2 = 5 }; const volatile __s64 a = 5; const volatile __u8 b = 5; const volatile enum Enum c = ELEMENT2; const volatile bool d = false; char arr[4] = {0}; SEC("tp_btf/sched_switch") int BPF_PROG(...) { bpf_printk("%c\n", arr[a]); bpf_printk("%c\n", arr[b]); bpf_printk("%c\n", arr[c]); bpf_printk("%c\n", arr[d]); return 0; } ``` By default verification of the program fails: ``` ./veristat prog.bpf.o ``` By presetting global variables, we can make verification pass: ``` ./veristat wq.bpf.o -G "a = 0" -G "b = 1" -G "c = 2" -G "d = 3" ``` Signed-off-by: Mykyta Yatsenko <[email protected]> Signed-off-by: Andrii Nakryiko <[email protected]> Acked-by: Eduard Zingerman <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent 0ba0ef0 commit e3c9abd

File tree

1 file changed

+300
-1
lines changed

1 file changed

+300
-1
lines changed

tools/testing/selftests/bpf/veristat.c

Lines changed: 300 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#define _GNU_SOURCE
44
#include <argp.h>
55
#include <libgen.h>
6+
#include <ctype.h>
67
#include <string.h>
78
#include <stdlib.h>
89
#include <sched.h>
@@ -154,6 +155,16 @@ struct filter {
154155
bool abs;
155156
};
156157

158+
struct var_preset {
159+
char *name;
160+
enum { INTEGRAL, ENUMERATOR } type;
161+
union {
162+
long long ivalue;
163+
char *svalue;
164+
};
165+
bool applied;
166+
};
167+
157168
static struct env {
158169
char **filenames;
159170
int filename_cnt;
@@ -195,6 +206,8 @@ static struct env {
195206
int progs_processed;
196207
int progs_skipped;
197208
int top_src_lines;
209+
struct var_preset *presets;
210+
int npresets;
198211
} env;
199212

200213
static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
@@ -246,12 +259,15 @@ static const struct argp_option opts[] = {
246259
{ "test-reg-invariants", 'r', NULL, 0,
247260
"Force BPF verifier failure on register invariant violation (BPF_F_TEST_REG_INVARIANTS program flag)" },
248261
{ "top-src-lines", 'S', "N", 0, "Emit N most frequent source code lines" },
262+
{ "set-global-vars", 'G', "GLOBAL", 0, "Set global variables provided in the expression, for example \"var1 = 1\"" },
249263
{},
250264
};
251265

252266
static int parse_stats(const char *stats_str, struct stat_specs *specs);
253267
static int append_filter(struct filter **filters, int *cnt, const char *str);
254268
static int append_filter_file(const char *path);
269+
static int append_var_preset(struct var_preset **presets, int *cnt, const char *expr);
270+
static int append_var_preset_file(const char *filename);
255271

256272
static error_t parse_arg(int key, char *arg, struct argp_state *state)
257273
{
@@ -353,6 +369,17 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state)
353369
argp_usage(state);
354370
}
355371
break;
372+
case 'G': {
373+
if (arg[0] == '@')
374+
err = append_var_preset_file(arg + 1);
375+
else
376+
err = append_var_preset(&env.presets, &env.npresets, arg);
377+
if (err) {
378+
fprintf(stderr, "Failed to parse global variable presets: %s\n", arg);
379+
return err;
380+
}
381+
break;
382+
}
356383
case ARGP_KEY_ARG:
357384
tmp = realloc(env.filenames, (env.filename_cnt + 1) * sizeof(*env.filenames));
358385
if (!tmp)
@@ -632,7 +659,7 @@ static int append_filter_file(const char *path)
632659
f = fopen(path, "r");
633660
if (!f) {
634661
err = -errno;
635-
fprintf(stderr, "Failed to open filters in '%s': %d\n", path, err);
662+
fprintf(stderr, "Failed to open filters in '%s': %s\n", path, strerror(err));
636663
return err;
637664
}
638665

@@ -1292,6 +1319,261 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf
12921319
return 0;
12931320
};
12941321

1322+
static int append_var_preset(struct var_preset **presets, int *cnt, const char *expr)
1323+
{
1324+
void *tmp;
1325+
struct var_preset *cur;
1326+
char var[256], val[256], *val_end;
1327+
long long value;
1328+
int n;
1329+
1330+
tmp = realloc(*presets, (*cnt + 1) * sizeof(**presets));
1331+
if (!tmp)
1332+
return -ENOMEM;
1333+
*presets = tmp;
1334+
cur = &(*presets)[*cnt];
1335+
memset(cur, 0, sizeof(*cur));
1336+
(*cnt)++;
1337+
1338+
if (sscanf(expr, "%s = %s %n", var, val, &n) != 2 || n != strlen(expr)) {
1339+
fprintf(stderr, "Failed to parse expression '%s'\n", expr);
1340+
return -EINVAL;
1341+
}
1342+
1343+
if (val[0] == '-' || isdigit(val[0])) {
1344+
/* must be a number */
1345+
errno = 0;
1346+
value = strtoll(val, &val_end, 0);
1347+
if (errno == ERANGE) {
1348+
errno = 0;
1349+
value = strtoull(val, &val_end, 0);
1350+
}
1351+
if (errno || *val_end != '\0') {
1352+
fprintf(stderr, "Failed to parse value '%s'\n", val);
1353+
return -EINVAL;
1354+
}
1355+
cur->ivalue = value;
1356+
cur->type = INTEGRAL;
1357+
} else {
1358+
/* if not a number, consider it enum value */
1359+
cur->svalue = strdup(val);
1360+
if (!cur->svalue)
1361+
return -ENOMEM;
1362+
cur->type = ENUMERATOR;
1363+
}
1364+
1365+
cur->name = strdup(var);
1366+
if (!cur->name)
1367+
return -ENOMEM;
1368+
1369+
return 0;
1370+
}
1371+
1372+
static int append_var_preset_file(const char *filename)
1373+
{
1374+
char buf[1024];
1375+
FILE *f;
1376+
int err = 0;
1377+
1378+
f = fopen(filename, "rt");
1379+
if (!f) {
1380+
err = -errno;
1381+
fprintf(stderr, "Failed to open presets in '%s': %s\n", filename, strerror(err));
1382+
return -EINVAL;
1383+
}
1384+
1385+
while (fscanf(f, " %1023[^\n]\n", buf) == 1) {
1386+
if (buf[0] == '\0' || buf[0] == '#')
1387+
continue;
1388+
1389+
err = append_var_preset(&env.presets, &env.npresets, buf);
1390+
if (err)
1391+
goto cleanup;
1392+
}
1393+
1394+
cleanup:
1395+
fclose(f);
1396+
return err;
1397+
}
1398+
1399+
static bool is_signed_type(const struct btf_type *t)
1400+
{
1401+
if (btf_is_int(t))
1402+
return btf_int_encoding(t) & BTF_INT_SIGNED;
1403+
if (btf_is_any_enum(t))
1404+
return btf_kflag(t);
1405+
return true;
1406+
}
1407+
1408+
static int enum_value_from_name(const struct btf *btf, const struct btf_type *t,
1409+
const char *evalue, long long *retval)
1410+
{
1411+
if (btf_is_enum(t)) {
1412+
struct btf_enum *e = btf_enum(t);
1413+
int i, n = btf_vlen(t);
1414+
1415+
for (i = 0; i < n; ++i, ++e) {
1416+
const char *cur_name = btf__name_by_offset(btf, e->name_off);
1417+
1418+
if (strcmp(cur_name, evalue) == 0) {
1419+
*retval = e->val;
1420+
return 0;
1421+
}
1422+
}
1423+
} else if (btf_is_enum64(t)) {
1424+
struct btf_enum64 *e = btf_enum64(t);
1425+
int i, n = btf_vlen(t);
1426+
1427+
for (i = 0; i < n; ++i, ++e) {
1428+
const char *cur_name = btf__name_by_offset(btf, e->name_off);
1429+
__u64 value = btf_enum64_value(e);
1430+
1431+
if (strcmp(cur_name, evalue) == 0) {
1432+
*retval = value;
1433+
return 0;
1434+
}
1435+
}
1436+
}
1437+
return -EINVAL;
1438+
}
1439+
1440+
static bool is_preset_supported(const struct btf_type *t)
1441+
{
1442+
return btf_is_int(t) || btf_is_enum(t) || btf_is_enum64(t);
1443+
}
1444+
1445+
static int set_global_var(struct bpf_object *obj, struct btf *btf, const struct btf_type *t,
1446+
struct bpf_map *map, struct btf_var_secinfo *sinfo,
1447+
struct var_preset *preset)
1448+
{
1449+
const struct btf_type *base_type;
1450+
void *ptr;
1451+
long long value = preset->ivalue;
1452+
size_t size;
1453+
1454+
base_type = btf__type_by_id(btf, btf__resolve_type(btf, t->type));
1455+
if (!base_type) {
1456+
fprintf(stderr, "Failed to resolve type %d\n", t->type);
1457+
return -EINVAL;
1458+
}
1459+
if (!is_preset_supported(base_type)) {
1460+
fprintf(stderr, "Setting value for type %s is not supported\n",
1461+
btf__name_by_offset(btf, base_type->name_off));
1462+
return -EINVAL;
1463+
}
1464+
1465+
if (preset->type == ENUMERATOR) {
1466+
if (btf_is_any_enum(base_type)) {
1467+
if (enum_value_from_name(btf, base_type, preset->svalue, &value)) {
1468+
fprintf(stderr,
1469+
"Failed to find integer value for enum element %s\n",
1470+
preset->svalue);
1471+
return -EINVAL;
1472+
}
1473+
} else {
1474+
fprintf(stderr, "Value %s is not supported for type %s\n",
1475+
preset->svalue, btf__name_by_offset(btf, base_type->name_off));
1476+
return -EINVAL;
1477+
}
1478+
}
1479+
1480+
/* Check if value fits into the target variable size */
1481+
if (sinfo->size < sizeof(value)) {
1482+
bool is_signed = is_signed_type(base_type);
1483+
__u32 unsigned_bits = sinfo->size * 8 - (is_signed ? 1 : 0);
1484+
long long max_val = 1ll << unsigned_bits;
1485+
1486+
if (value >= max_val || value < -max_val) {
1487+
fprintf(stderr,
1488+
"Variable %s value %lld is out of range [%lld; %lld]\n",
1489+
btf__name_by_offset(btf, t->name_off), value,
1490+
is_signed ? -max_val : 0, max_val - 1);
1491+
return -EINVAL;
1492+
}
1493+
}
1494+
1495+
ptr = bpf_map__initial_value(map, &size);
1496+
if (!ptr || sinfo->offset + sinfo->size > size)
1497+
return -EINVAL;
1498+
1499+
if (__BYTE_ORDER == __LITTLE_ENDIAN) {
1500+
memcpy(ptr + sinfo->offset, &value, sinfo->size);
1501+
} else { /* __BYTE_ORDER == __BIG_ENDIAN */
1502+
__u8 src_offset = sizeof(value) - sinfo->size;
1503+
1504+
memcpy(ptr + sinfo->offset, (void *)&value + src_offset, sinfo->size);
1505+
}
1506+
return 0;
1507+
}
1508+
1509+
static int set_global_vars(struct bpf_object *obj, struct var_preset *presets, int npresets)
1510+
{
1511+
struct btf_var_secinfo *sinfo;
1512+
const char *sec_name;
1513+
const struct btf_type *t;
1514+
struct bpf_map *map;
1515+
struct btf *btf;
1516+
int i, j, k, n, cnt, err = 0;
1517+
1518+
if (npresets == 0)
1519+
return 0;
1520+
1521+
btf = bpf_object__btf(obj);
1522+
if (!btf)
1523+
return -EINVAL;
1524+
1525+
cnt = btf__type_cnt(btf);
1526+
for (i = 1; i != cnt; ++i) {
1527+
t = btf__type_by_id(btf, i);
1528+
1529+
if (!btf_is_datasec(t))
1530+
continue;
1531+
1532+
sinfo = btf_var_secinfos(t);
1533+
sec_name = btf__name_by_offset(btf, t->name_off);
1534+
map = bpf_object__find_map_by_name(obj, sec_name);
1535+
if (!map)
1536+
continue;
1537+
1538+
n = btf_vlen(t);
1539+
for (j = 0; j < n; ++j, ++sinfo) {
1540+
const struct btf_type *var_type = btf__type_by_id(btf, sinfo->type);
1541+
const char *var_name;
1542+
1543+
if (!btf_is_var(var_type))
1544+
continue;
1545+
1546+
var_name = btf__name_by_offset(btf, var_type->name_off);
1547+
1548+
for (k = 0; k < npresets; ++k) {
1549+
if (strcmp(var_name, presets[k].name) != 0)
1550+
continue;
1551+
1552+
if (presets[k].applied) {
1553+
fprintf(stderr, "Variable %s is set more than once",
1554+
var_name);
1555+
return -EINVAL;
1556+
}
1557+
1558+
err = set_global_var(obj, btf, var_type, map, sinfo, presets + k);
1559+
if (err)
1560+
return err;
1561+
1562+
presets[k].applied = true;
1563+
break;
1564+
}
1565+
}
1566+
}
1567+
for (i = 0; i < npresets; ++i) {
1568+
if (!presets[i].applied) {
1569+
fprintf(stderr, "Global variable preset %s has not been applied\n",
1570+
presets[i].name);
1571+
}
1572+
presets[i].applied = false;
1573+
}
1574+
return err;
1575+
}
1576+
12951577
static int process_obj(const char *filename)
12961578
{
12971579
const char *base_filename = basename(strdupa(filename));
@@ -1341,6 +1623,11 @@ static int process_obj(const char *filename)
13411623
if (prog_cnt == 1) {
13421624
prog = bpf_object__next_program(obj, NULL);
13431625
bpf_program__set_autoload(prog, true);
1626+
err = set_global_vars(obj, env.presets, env.npresets);
1627+
if (err) {
1628+
fprintf(stderr, "Failed to set global variables %d\n", err);
1629+
goto cleanup;
1630+
}
13441631
process_prog(filename, obj, prog);
13451632
goto cleanup;
13461633
}
@@ -1355,6 +1642,12 @@ static int process_obj(const char *filename)
13551642
goto cleanup;
13561643
}
13571644

1645+
err = set_global_vars(tobj, env.presets, env.npresets);
1646+
if (err) {
1647+
fprintf(stderr, "Failed to set global variables %d\n", err);
1648+
goto cleanup;
1649+
}
1650+
13581651
lprog = NULL;
13591652
bpf_object__for_each_program(tprog, tobj) {
13601653
const char *tprog_name = bpf_program__name(tprog);
@@ -2460,5 +2753,11 @@ int main(int argc, char **argv)
24602753
free(env.deny_filters[i].prog_glob);
24612754
}
24622755
free(env.deny_filters);
2756+
for (i = 0; i < env.npresets; ++i) {
2757+
free(env.presets[i].name);
2758+
if (env.presets[i].type == ENUMERATOR)
2759+
free(env.presets[i].svalue);
2760+
}
2761+
free(env.presets);
24632762
return -err;
24642763
}

0 commit comments

Comments
 (0)