Skip to content

Commit 87eb015

Browse files
eddyz87Alexei Starovoitov
authored andcommitted
selftests/bpf: track string payload offset as scalar in strobemeta
This change prepares strobemeta for update in callbacks verification logic. To allow bpf_loop() verification converge when multiple callback iterations are considered: - track offset inside strobemeta_payload->payload directly as scalar value; - at each iteration make sure that remaining strobemeta_payload->payload capacity is sufficient for execution of read_{map,str}_var functions; - make sure that offset is tracked as unbound scalar between iterations, otherwise verifier won't be able infer that bpf_loop callback reaches identical states. Acked-by: Andrii Nakryiko <andrii@kernel.org> Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Link: https://lore.kernel.org/r/20231121020701.26440-3-eddyz87@gmail.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
1 parent 977bc14 commit 87eb015

File tree

1 file changed

+48
-30
lines changed

1 file changed

+48
-30
lines changed

tools/testing/selftests/bpf/progs/strobemeta.h

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@ struct task_struct {};
2424
#define STACK_TABLE_EPOCH_SHIFT 20
2525
#define STROBE_MAX_STR_LEN 1
2626
#define STROBE_MAX_CFGS 32
27+
#define READ_MAP_VAR_PAYLOAD_CAP \
28+
((1 + STROBE_MAX_MAP_ENTRIES * 2) * STROBE_MAX_STR_LEN)
2729
#define STROBE_MAX_PAYLOAD \
2830
(STROBE_MAX_STRS * STROBE_MAX_STR_LEN + \
29-
STROBE_MAX_MAPS * (1 + STROBE_MAX_MAP_ENTRIES * 2) * STROBE_MAX_STR_LEN)
31+
STROBE_MAX_MAPS * READ_MAP_VAR_PAYLOAD_CAP)
3032

3133
struct strobe_value_header {
3234
/*
@@ -355,7 +357,7 @@ static __always_inline uint64_t read_str_var(struct strobemeta_cfg *cfg,
355357
size_t idx, void *tls_base,
356358
struct strobe_value_generic *value,
357359
struct strobemeta_payload *data,
358-
void *payload)
360+
size_t off)
359361
{
360362
void *location;
361363
uint64_t len;
@@ -366,7 +368,7 @@ static __always_inline uint64_t read_str_var(struct strobemeta_cfg *cfg,
366368
return 0;
367369

368370
bpf_probe_read_user(value, sizeof(struct strobe_value_generic), location);
369-
len = bpf_probe_read_user_str(payload, STROBE_MAX_STR_LEN, value->ptr);
371+
len = bpf_probe_read_user_str(&data->payload[off], STROBE_MAX_STR_LEN, value->ptr);
370372
/*
371373
* if bpf_probe_read_user_str returns error (<0), due to casting to
372374
* unsinged int, it will become big number, so next check is
@@ -378,14 +380,14 @@ static __always_inline uint64_t read_str_var(struct strobemeta_cfg *cfg,
378380
return 0;
379381

380382
data->str_lens[idx] = len;
381-
return len;
383+
return off + len;
382384
}
383385

384-
static __always_inline void *read_map_var(struct strobemeta_cfg *cfg,
385-
size_t idx, void *tls_base,
386-
struct strobe_value_generic *value,
387-
struct strobemeta_payload *data,
388-
void *payload)
386+
static __always_inline uint64_t read_map_var(struct strobemeta_cfg *cfg,
387+
size_t idx, void *tls_base,
388+
struct strobe_value_generic *value,
389+
struct strobemeta_payload *data,
390+
size_t off)
389391
{
390392
struct strobe_map_descr* descr = &data->map_descrs[idx];
391393
struct strobe_map_raw map;
@@ -397,11 +399,11 @@ static __always_inline void *read_map_var(struct strobemeta_cfg *cfg,
397399

398400
location = calc_location(&cfg->map_locs[idx], tls_base);
399401
if (!location)
400-
return payload;
402+
return off;
401403

402404
bpf_probe_read_user(value, sizeof(struct strobe_value_generic), location);
403405
if (bpf_probe_read_user(&map, sizeof(struct strobe_map_raw), value->ptr))
404-
return payload;
406+
return off;
405407

406408
descr->id = map.id;
407409
descr->cnt = map.cnt;
@@ -410,10 +412,10 @@ static __always_inline void *read_map_var(struct strobemeta_cfg *cfg,
410412
data->req_meta_valid = 1;
411413
}
412414

413-
len = bpf_probe_read_user_str(payload, STROBE_MAX_STR_LEN, map.tag);
415+
len = bpf_probe_read_user_str(&data->payload[off], STROBE_MAX_STR_LEN, map.tag);
414416
if (len <= STROBE_MAX_STR_LEN) {
415417
descr->tag_len = len;
416-
payload += len;
418+
off += len;
417419
}
418420

419421
#ifdef NO_UNROLL
@@ -426,22 +428,22 @@ static __always_inline void *read_map_var(struct strobemeta_cfg *cfg,
426428
break;
427429

428430
descr->key_lens[i] = 0;
429-
len = bpf_probe_read_user_str(payload, STROBE_MAX_STR_LEN,
431+
len = bpf_probe_read_user_str(&data->payload[off], STROBE_MAX_STR_LEN,
430432
map.entries[i].key);
431433
if (len <= STROBE_MAX_STR_LEN) {
432434
descr->key_lens[i] = len;
433-
payload += len;
435+
off += len;
434436
}
435437
descr->val_lens[i] = 0;
436-
len = bpf_probe_read_user_str(payload, STROBE_MAX_STR_LEN,
438+
len = bpf_probe_read_user_str(&data->payload[off], STROBE_MAX_STR_LEN,
437439
map.entries[i].val);
438440
if (len <= STROBE_MAX_STR_LEN) {
439441
descr->val_lens[i] = len;
440-
payload += len;
442+
off += len;
441443
}
442444
}
443445

444-
return payload;
446+
return off;
445447
}
446448

447449
#ifdef USE_BPF_LOOP
@@ -455,14 +457,20 @@ struct read_var_ctx {
455457
struct strobemeta_payload *data;
456458
void *tls_base;
457459
struct strobemeta_cfg *cfg;
458-
void *payload;
460+
size_t payload_off;
459461
/* value gets mutated */
460462
struct strobe_value_generic *value;
461463
enum read_type type;
462464
};
463465

464-
static int read_var_callback(__u32 index, struct read_var_ctx *ctx)
466+
static int read_var_callback(__u64 index, struct read_var_ctx *ctx)
465467
{
468+
/* lose precision info for ctx->payload_off, verifier won't track
469+
* double xor, barrier_var() is needed to force clang keep both xors.
470+
*/
471+
ctx->payload_off ^= index;
472+
barrier_var(ctx->payload_off);
473+
ctx->payload_off ^= index;
466474
switch (ctx->type) {
467475
case READ_INT_VAR:
468476
if (index >= STROBE_MAX_INTS)
@@ -472,14 +480,18 @@ static int read_var_callback(__u32 index, struct read_var_ctx *ctx)
472480
case READ_MAP_VAR:
473481
if (index >= STROBE_MAX_MAPS)
474482
return 1;
475-
ctx->payload = read_map_var(ctx->cfg, index, ctx->tls_base,
476-
ctx->value, ctx->data, ctx->payload);
483+
if (ctx->payload_off > sizeof(ctx->data->payload) - READ_MAP_VAR_PAYLOAD_CAP)
484+
return 1;
485+
ctx->payload_off = read_map_var(ctx->cfg, index, ctx->tls_base,
486+
ctx->value, ctx->data, ctx->payload_off);
477487
break;
478488
case READ_STR_VAR:
479489
if (index >= STROBE_MAX_STRS)
480490
return 1;
481-
ctx->payload += read_str_var(ctx->cfg, index, ctx->tls_base,
482-
ctx->value, ctx->data, ctx->payload);
491+
if (ctx->payload_off > sizeof(ctx->data->payload) - STROBE_MAX_STR_LEN)
492+
return 1;
493+
ctx->payload_off = read_str_var(ctx->cfg, index, ctx->tls_base,
494+
ctx->value, ctx->data, ctx->payload_off);
483495
break;
484496
}
485497
return 0;
@@ -501,15 +513,16 @@ static void *read_strobe_meta(struct task_struct *task,
501513
pid_t pid = bpf_get_current_pid_tgid() >> 32;
502514
struct strobe_value_generic value = {0};
503515
struct strobemeta_cfg *cfg;
504-
void *tls_base, *payload;
516+
size_t payload_off;
517+
void *tls_base;
505518

506519
cfg = bpf_map_lookup_elem(&strobemeta_cfgs, &pid);
507520
if (!cfg)
508521
return NULL;
509522

510523
data->int_vals_set_mask = 0;
511524
data->req_meta_valid = 0;
512-
payload = data->payload;
525+
payload_off = 0;
513526
/*
514527
* we don't have struct task_struct definition, it should be:
515528
* tls_base = (void *)task->thread.fsbase;
@@ -522,7 +535,7 @@ static void *read_strobe_meta(struct task_struct *task,
522535
.tls_base = tls_base,
523536
.value = &value,
524537
.data = data,
525-
.payload = payload,
538+
.payload_off = 0,
526539
};
527540
int err;
528541

@@ -540,6 +553,11 @@ static void *read_strobe_meta(struct task_struct *task,
540553
err = bpf_loop(STROBE_MAX_MAPS, read_var_callback, &ctx, 0);
541554
if (err != STROBE_MAX_MAPS)
542555
return NULL;
556+
557+
payload_off = ctx.payload_off;
558+
/* this should not really happen, here only to satisfy verifer */
559+
if (payload_off > sizeof(data->payload))
560+
payload_off = sizeof(data->payload);
543561
#else
544562
#ifdef NO_UNROLL
545563
#pragma clang loop unroll(disable)
@@ -555,23 +573,23 @@ static void *read_strobe_meta(struct task_struct *task,
555573
#pragma unroll
556574
#endif /* NO_UNROLL */
557575
for (int i = 0; i < STROBE_MAX_STRS; ++i) {
558-
payload += read_str_var(cfg, i, tls_base, &value, data, payload);
576+
payload_off = read_str_var(cfg, i, tls_base, &value, data, payload_off);
559577
}
560578
#ifdef NO_UNROLL
561579
#pragma clang loop unroll(disable)
562580
#else
563581
#pragma unroll
564582
#endif /* NO_UNROLL */
565583
for (int i = 0; i < STROBE_MAX_MAPS; ++i) {
566-
payload = read_map_var(cfg, i, tls_base, &value, data, payload);
584+
payload_off = read_map_var(cfg, i, tls_base, &value, data, payload_off);
567585
}
568586
#endif /* USE_BPF_LOOP */
569587

570588
/*
571589
* return pointer right after end of payload, so it's possible to
572590
* calculate exact amount of useful data that needs to be sent
573591
*/
574-
return payload;
592+
return &data->payload[payload_off];
575593
}
576594

577595
SEC("raw_tracepoint/kfree_skb")

0 commit comments

Comments
 (0)