Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Eduard Zingerman and committed by
Alexei Starovoitov
87eb0152 977bc146

+48 -30
+48 -30
tools/testing/selftests/bpf/progs/strobemeta.h
··· 24 24 #define STACK_TABLE_EPOCH_SHIFT 20 25 25 #define STROBE_MAX_STR_LEN 1 26 26 #define STROBE_MAX_CFGS 32 27 + #define READ_MAP_VAR_PAYLOAD_CAP \ 28 + ((1 + STROBE_MAX_MAP_ENTRIES * 2) * STROBE_MAX_STR_LEN) 27 29 #define STROBE_MAX_PAYLOAD \ 28 30 (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) 30 32 31 33 struct strobe_value_header { 32 34 /* ··· 357 355 size_t idx, void *tls_base, 358 356 struct strobe_value_generic *value, 359 357 struct strobemeta_payload *data, 360 - void *payload) 358 + size_t off) 361 359 { 362 360 void *location; 363 361 uint64_t len; ··· 368 366 return 0; 369 367 370 368 bpf_probe_read_user(value, sizeof(struct strobe_value_generic), location); 371 - len = bpf_probe_read_user_str(payload, STROBE_MAX_STR_LEN, value->ptr); 369 + len = bpf_probe_read_user_str(&data->payload[off], STROBE_MAX_STR_LEN, value->ptr); 372 370 /* 373 371 * if bpf_probe_read_user_str returns error (<0), due to casting to 374 372 * unsinged int, it will become big number, so next check is ··· 380 378 return 0; 381 379 382 380 data->str_lens[idx] = len; 383 - return len; 381 + return off + len; 384 382 } 385 383 386 - static __always_inline void *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 - void *payload) 384 + static __always_inline uint64_t 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 + size_t off) 391 389 { 392 390 struct strobe_map_descr* descr = &data->map_descrs[idx]; 393 391 struct strobe_map_raw map; ··· 399 397 400 398 location = calc_location(&cfg->map_locs[idx], tls_base); 401 399 if (!location) 402 - return payload; 400 + return off; 403 401 404 402 bpf_probe_read_user(value, sizeof(struct strobe_value_generic), location); 405 403 if (bpf_probe_read_user(&map, sizeof(struct strobe_map_raw), value->ptr)) 406 - return payload; 404 + return off; 407 405 408 406 descr->id = map.id; 409 407 descr->cnt = map.cnt; ··· 412 410 data->req_meta_valid = 1; 413 411 } 414 412 415 - len = bpf_probe_read_user_str(payload, STROBE_MAX_STR_LEN, map.tag); 413 + len = bpf_probe_read_user_str(&data->payload[off], STROBE_MAX_STR_LEN, map.tag); 416 414 if (len <= STROBE_MAX_STR_LEN) { 417 415 descr->tag_len = len; 418 - payload += len; 416 + off += len; 419 417 } 420 418 421 419 #ifdef NO_UNROLL ··· 428 426 break; 429 427 430 428 descr->key_lens[i] = 0; 431 - len = bpf_probe_read_user_str(payload, STROBE_MAX_STR_LEN, 429 + len = bpf_probe_read_user_str(&data->payload[off], STROBE_MAX_STR_LEN, 432 430 map.entries[i].key); 433 431 if (len <= STROBE_MAX_STR_LEN) { 434 432 descr->key_lens[i] = len; 435 - payload += len; 433 + off += len; 436 434 } 437 435 descr->val_lens[i] = 0; 438 - len = bpf_probe_read_user_str(payload, STROBE_MAX_STR_LEN, 436 + len = bpf_probe_read_user_str(&data->payload[off], STROBE_MAX_STR_LEN, 439 437 map.entries[i].val); 440 438 if (len <= STROBE_MAX_STR_LEN) { 441 439 descr->val_lens[i] = len; 442 - payload += len; 440 + off += len; 443 441 } 444 442 } 445 443 446 - return payload; 444 + return off; 447 445 } 448 446 449 447 #ifdef USE_BPF_LOOP ··· 457 455 struct strobemeta_payload *data; 458 456 void *tls_base; 459 457 struct strobemeta_cfg *cfg; 460 - void *payload; 458 + size_t payload_off; 461 459 /* value gets mutated */ 462 460 struct strobe_value_generic *value; 463 461 enum read_type type; 464 462 }; 465 463 466 - static int read_var_callback(__u32 index, struct read_var_ctx *ctx) 464 + static int read_var_callback(__u64 index, struct read_var_ctx *ctx) 467 465 { 466 + /* lose precision info for ctx->payload_off, verifier won't track 467 + * double xor, barrier_var() is needed to force clang keep both xors. 468 + */ 469 + ctx->payload_off ^= index; 470 + barrier_var(ctx->payload_off); 471 + ctx->payload_off ^= index; 468 472 switch (ctx->type) { 469 473 case READ_INT_VAR: 470 474 if (index >= STROBE_MAX_INTS) ··· 480 472 case READ_MAP_VAR: 481 473 if (index >= STROBE_MAX_MAPS) 482 474 return 1; 483 - ctx->payload = read_map_var(ctx->cfg, index, ctx->tls_base, 484 - ctx->value, ctx->data, ctx->payload); 475 + if (ctx->payload_off > sizeof(ctx->data->payload) - READ_MAP_VAR_PAYLOAD_CAP) 476 + return 1; 477 + ctx->payload_off = read_map_var(ctx->cfg, index, ctx->tls_base, 478 + ctx->value, ctx->data, ctx->payload_off); 485 479 break; 486 480 case READ_STR_VAR: 487 481 if (index >= STROBE_MAX_STRS) 488 482 return 1; 489 - ctx->payload += read_str_var(ctx->cfg, index, ctx->tls_base, 490 - ctx->value, ctx->data, ctx->payload); 483 + if (ctx->payload_off > sizeof(ctx->data->payload) - STROBE_MAX_STR_LEN) 484 + return 1; 485 + ctx->payload_off = read_str_var(ctx->cfg, index, ctx->tls_base, 486 + ctx->value, ctx->data, ctx->payload_off); 491 487 break; 492 488 } 493 489 return 0; ··· 513 501 pid_t pid = bpf_get_current_pid_tgid() >> 32; 514 502 struct strobe_value_generic value = {0}; 515 503 struct strobemeta_cfg *cfg; 516 - void *tls_base, *payload; 504 + size_t payload_off; 505 + void *tls_base; 517 506 518 507 cfg = bpf_map_lookup_elem(&strobemeta_cfgs, &pid); 519 508 if (!cfg) ··· 522 509 523 510 data->int_vals_set_mask = 0; 524 511 data->req_meta_valid = 0; 525 - payload = data->payload; 512 + payload_off = 0; 526 513 /* 527 514 * we don't have struct task_struct definition, it should be: 528 515 * tls_base = (void *)task->thread.fsbase; ··· 535 522 .tls_base = tls_base, 536 523 .value = &value, 537 524 .data = data, 538 - .payload = payload, 525 + .payload_off = 0, 539 526 }; 540 527 int err; 541 528 ··· 553 540 err = bpf_loop(STROBE_MAX_MAPS, read_var_callback, &ctx, 0); 554 541 if (err != STROBE_MAX_MAPS) 555 542 return NULL; 543 + 544 + payload_off = ctx.payload_off; 545 + /* this should not really happen, here only to satisfy verifer */ 546 + if (payload_off > sizeof(data->payload)) 547 + payload_off = sizeof(data->payload); 556 548 #else 557 549 #ifdef NO_UNROLL 558 550 #pragma clang loop unroll(disable) ··· 573 555 #pragma unroll 574 556 #endif /* NO_UNROLL */ 575 557 for (int i = 0; i < STROBE_MAX_STRS; ++i) { 576 - payload += read_str_var(cfg, i, tls_base, &value, data, payload); 558 + payload_off = read_str_var(cfg, i, tls_base, &value, data, payload_off); 577 559 } 578 560 #ifdef NO_UNROLL 579 561 #pragma clang loop unroll(disable) ··· 581 563 #pragma unroll 582 564 #endif /* NO_UNROLL */ 583 565 for (int i = 0; i < STROBE_MAX_MAPS; ++i) { 584 - payload = read_map_var(cfg, i, tls_base, &value, data, payload); 566 + payload_off = read_map_var(cfg, i, tls_base, &value, data, payload_off); 585 567 } 586 568 #endif /* USE_BPF_LOOP */ 587 569 ··· 589 571 * return pointer right after end of payload, so it's possible to 590 572 * calculate exact amount of useful data that needs to be sent 591 573 */ 592 - return payload; 574 + return &data->payload[payload_off]; 593 575 } 594 576 595 577 SEC("raw_tracepoint/kfree_skb")