Skip to content

Commit d8b34d6

Browse files
jsitnickiKernel Patches Daemon
authored andcommitted
selftests/bpf: Expect unclone to preserve skb metadata
Since pskb_expand_head() no longer clears metadata on unclone, update tests for cloned packets to expect metadata to remain intact. Also simplify the clone_dynptr_kept_on_{data,meta}_slice_write tests. Creating an r/w dynptr slice is sufficient to trigger an unclone in the prologue, so remove the extraneous writes to the data/meta slice. Signed-off-by: Jakub Sitnicki <[email protected]>
1 parent 704022a commit d8b34d6

File tree

2 files changed

+79
-63
lines changed

2 files changed

+79
-63
lines changed

tools/testing/selftests/bpf/prog_tests/xdp_context_test_run.c

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -454,29 +454,29 @@ void test_xdp_context_tuntap(void)
454454
skel->progs.ing_cls_dynptr_offset_oob,
455455
skel->progs.ing_cls,
456456
&skel->bss->test_pass);
457-
if (test__start_subtest("clone_data_meta_empty_on_data_write"))
457+
if (test__start_subtest("clone_data_meta_survives_data_write"))
458458
test_tuntap_mirred(skel->progs.ing_xdp,
459-
skel->progs.clone_data_meta_empty_on_data_write,
459+
skel->progs.clone_data_meta_survives_data_write,
460460
&skel->bss->test_pass);
461-
if (test__start_subtest("clone_data_meta_empty_on_meta_write"))
461+
if (test__start_subtest("clone_data_meta_survives_meta_write"))
462462
test_tuntap_mirred(skel->progs.ing_xdp,
463-
skel->progs.clone_data_meta_empty_on_meta_write,
463+
skel->progs.clone_data_meta_survives_meta_write,
464464
&skel->bss->test_pass);
465-
if (test__start_subtest("clone_dynptr_empty_on_data_slice_write"))
465+
if (test__start_subtest("clone_meta_dynptr_survives_data_slice_write"))
466466
test_tuntap_mirred(skel->progs.ing_xdp,
467-
skel->progs.clone_dynptr_empty_on_data_slice_write,
467+
skel->progs.clone_meta_dynptr_survives_data_slice_write,
468468
&skel->bss->test_pass);
469-
if (test__start_subtest("clone_dynptr_empty_on_meta_slice_write"))
469+
if (test__start_subtest("clone_meta_dynptr_survives_meta_slice_write"))
470470
test_tuntap_mirred(skel->progs.ing_xdp,
471-
skel->progs.clone_dynptr_empty_on_meta_slice_write,
471+
skel->progs.clone_meta_dynptr_survives_meta_slice_write,
472472
&skel->bss->test_pass);
473-
if (test__start_subtest("clone_dynptr_rdonly_before_data_dynptr_write"))
473+
if (test__start_subtest("clone_meta_dynptr_rw_before_data_dynptr_write"))
474474
test_tuntap_mirred(skel->progs.ing_xdp,
475-
skel->progs.clone_dynptr_rdonly_before_data_dynptr_write,
475+
skel->progs.clone_meta_dynptr_rw_before_data_dynptr_write,
476476
&skel->bss->test_pass);
477-
if (test__start_subtest("clone_dynptr_rdonly_before_meta_dynptr_write"))
477+
if (test__start_subtest("clone_meta_dynptr_rw_before_meta_dynptr_write"))
478478
test_tuntap_mirred(skel->progs.ing_xdp,
479-
skel->progs.clone_dynptr_rdonly_before_meta_dynptr_write,
479+
skel->progs.clone_meta_dynptr_rw_before_meta_dynptr_write,
480480
&skel->bss->test_pass);
481481

482482
test_xdp_meta__destroy(skel);

tools/testing/selftests/bpf/progs/test_xdp_meta.c

Lines changed: 67 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -321,12 +321,13 @@ int ing_xdp(struct xdp_md *ctx)
321321
}
322322

323323
/*
324-
* Check that skb->data_meta..skb->data is empty if prog writes to packet
325-
* _payload_ using packet pointers. Applies only to cloned skbs.
324+
* Check that, when operating on a cloned packet, skb->data_meta..skb->data is
325+
* kept intact if prog writes to packet _payload_ using packet pointers.
326326
*/
327327
SEC("tc")
328-
int clone_data_meta_empty_on_data_write(struct __sk_buff *ctx)
328+
int clone_data_meta_survives_data_write(struct __sk_buff *ctx)
329329
{
330+
__u8 *meta_have = ctx_ptr(ctx, data_meta);
330331
struct ethhdr *eth = ctx_ptr(ctx, data);
331332

332333
if (eth + 1 > ctx_ptr(ctx, data_end))
@@ -335,8 +336,10 @@ int clone_data_meta_empty_on_data_write(struct __sk_buff *ctx)
335336
if (eth->h_proto != 0)
336337
goto out;
337338

338-
/* Expect no metadata */
339-
if (ctx->data_meta != ctx->data)
339+
if (meta_have + META_SIZE > eth)
340+
goto out;
341+
342+
if (!check_metadata(meta_have))
340343
goto out;
341344

342345
/* Packet write to trigger unclone in prologue */
@@ -348,40 +351,44 @@ int clone_data_meta_empty_on_data_write(struct __sk_buff *ctx)
348351
}
349352

350353
/*
351-
* Check that skb->data_meta..skb->data is empty if prog writes to packet
352-
* _metadata_ using packet pointers. Applies only to cloned skbs.
354+
* Check that, when operating on a cloned packet, skb->data_meta..skb->data is
355+
* kept intact if prog writes to packet _metadata_ using packet pointers.
353356
*/
354357
SEC("tc")
355-
int clone_data_meta_empty_on_meta_write(struct __sk_buff *ctx)
358+
int clone_data_meta_survives_meta_write(struct __sk_buff *ctx)
356359
{
360+
__u8 *meta_have = ctx_ptr(ctx, data_meta);
357361
struct ethhdr *eth = ctx_ptr(ctx, data);
358-
__u8 *md = ctx_ptr(ctx, data_meta);
359362

360363
if (eth + 1 > ctx_ptr(ctx, data_end))
361364
goto out;
362365
/* Ignore non-test packets */
363366
if (eth->h_proto != 0)
364367
goto out;
365368

366-
if (md + 1 > ctx_ptr(ctx, data)) {
367-
/* Expect no metadata */
368-
test_pass = true;
369-
} else {
370-
/* Metadata write to trigger unclone in prologue */
371-
*md = 42;
372-
}
369+
if (meta_have + META_SIZE > eth)
370+
goto out;
371+
372+
if (!check_metadata(meta_have))
373+
goto out;
374+
375+
/* Metadata write to trigger unclone in prologue */
376+
*meta_have = 42;
377+
378+
test_pass = true;
373379
out:
374380
return TC_ACT_SHOT;
375381
}
376382

377383
/*
378-
* Check that skb_meta dynptr is writable but empty if prog writes to packet
379-
* _payload_ using a dynptr slice. Applies only to cloned skbs.
384+
* Check that, when operating on a cloned packet, metadata remains intact if
385+
* prog creates a r/w slice to packet _payload_.
380386
*/
381387
SEC("tc")
382-
int clone_dynptr_empty_on_data_slice_write(struct __sk_buff *ctx)
388+
int clone_meta_dynptr_survives_data_slice_write(struct __sk_buff *ctx)
383389
{
384390
struct bpf_dynptr data, meta;
391+
__u8 meta_have[META_SIZE];
385392
struct ethhdr *eth;
386393

387394
bpf_dynptr_from_skb(ctx, 0, &data);
@@ -392,29 +399,26 @@ int clone_dynptr_empty_on_data_slice_write(struct __sk_buff *ctx)
392399
if (eth->h_proto != 0)
393400
goto out;
394401

395-
/* Expect no metadata */
396402
bpf_dynptr_from_skb_meta(ctx, 0, &meta);
397-
if (bpf_dynptr_is_rdonly(&meta) || bpf_dynptr_size(&meta) > 0)
403+
bpf_dynptr_read(meta_have, META_SIZE, &meta, 0, 0);
404+
if (!check_metadata(meta_have))
398405
goto out;
399406

400-
/* Packet write to trigger unclone in prologue */
401-
eth->h_proto = 42;
402-
403407
test_pass = true;
404408
out:
405409
return TC_ACT_SHOT;
406410
}
407411

408412
/*
409-
* Check that skb_meta dynptr is writable but empty if prog writes to packet
410-
* _metadata_ using a dynptr slice. Applies only to cloned skbs.
413+
* Check that, when operating on a cloned packet, metadata remains intact if
414+
* prog creates an r/w slice to packet _metadata_.
411415
*/
412416
SEC("tc")
413-
int clone_dynptr_empty_on_meta_slice_write(struct __sk_buff *ctx)
417+
int clone_meta_dynptr_survives_meta_slice_write(struct __sk_buff *ctx)
414418
{
415419
struct bpf_dynptr data, meta;
416420
const struct ethhdr *eth;
417-
__u8 *md;
421+
__u8 *meta_have;
418422

419423
bpf_dynptr_from_skb(ctx, 0, &data);
420424
eth = bpf_dynptr_slice(&data, 0, NULL, sizeof(*eth));
@@ -424,31 +428,31 @@ int clone_dynptr_empty_on_meta_slice_write(struct __sk_buff *ctx)
424428
if (eth->h_proto != 0)
425429
goto out;
426430

427-
/* Expect no metadata */
428431
bpf_dynptr_from_skb_meta(ctx, 0, &meta);
429-
if (bpf_dynptr_is_rdonly(&meta) || bpf_dynptr_size(&meta) > 0)
432+
meta_have = bpf_dynptr_slice_rdwr(&meta, 0, NULL, META_SIZE);
433+
if (!meta_have)
430434
goto out;
431435

432-
/* Metadata write to trigger unclone in prologue */
433-
bpf_dynptr_from_skb_meta(ctx, 0, &meta);
434-
md = bpf_dynptr_slice_rdwr(&meta, 0, NULL, sizeof(*md));
435-
if (md)
436-
*md = 42;
436+
if (!check_metadata(meta_have))
437+
goto out;
437438

438439
test_pass = true;
439440
out:
440441
return TC_ACT_SHOT;
441442
}
442443

443444
/*
444-
* Check that skb_meta dynptr is read-only before prog writes to packet payload
445-
* using dynptr_write helper. Applies only to cloned skbs.
445+
* Check that, when operating on a cloned packet, skb_meta dynptr is read-write
446+
* before prog writes to packet _payload_ using dynptr_write helper and metadata
447+
* remains intact before and after the write.
446448
*/
447449
SEC("tc")
448-
int clone_dynptr_rdonly_before_data_dynptr_write(struct __sk_buff *ctx)
450+
int clone_meta_dynptr_rw_before_data_dynptr_write(struct __sk_buff *ctx)
449451
{
450452
struct bpf_dynptr data, meta;
453+
__u8 meta_have[META_SIZE];
451454
const struct ethhdr *eth;
455+
int err;
452456

453457
bpf_dynptr_from_skb(ctx, 0, &data);
454458
eth = bpf_dynptr_slice(&data, 0, NULL, sizeof(*eth));
@@ -458,17 +462,20 @@ int clone_dynptr_rdonly_before_data_dynptr_write(struct __sk_buff *ctx)
458462
if (eth->h_proto != 0)
459463
goto out;
460464

461-
/* Expect read-only metadata before unclone */
465+
/* Expect read-write metadata before unclone */
462466
bpf_dynptr_from_skb_meta(ctx, 0, &meta);
463-
if (!bpf_dynptr_is_rdonly(&meta) || bpf_dynptr_size(&meta) != META_SIZE)
467+
if (bpf_dynptr_is_rdonly(&meta))
468+
goto out;
469+
470+
err = bpf_dynptr_read(meta_have, META_SIZE, &meta, 0, 0);
471+
if (err || !check_metadata(meta_have))
464472
goto out;
465473

466474
/* Helper write to payload will unclone the packet */
467475
bpf_dynptr_write(&data, offsetof(struct ethhdr, h_proto), "x", 1, 0);
468476

469-
/* Expect no metadata after unclone */
470-
bpf_dynptr_from_skb_meta(ctx, 0, &meta);
471-
if (bpf_dynptr_is_rdonly(&meta) || bpf_dynptr_size(&meta) != 0)
477+
err = bpf_dynptr_read(meta_have, META_SIZE, &meta, 0, 0);
478+
if (err || !check_metadata(meta_have))
472479
goto out;
473480

474481
test_pass = true;
@@ -477,14 +484,17 @@ int clone_dynptr_rdonly_before_data_dynptr_write(struct __sk_buff *ctx)
477484
}
478485

479486
/*
480-
* Check that skb_meta dynptr is read-only if prog writes to packet
481-
* metadata using dynptr_write helper. Applies only to cloned skbs.
487+
* Check that, when operating on a cloned packet, skb_meta dynptr is read-write
488+
* before prog writes to packet _metadata_ using dynptr_write helper and
489+
* metadata remains intact before and after the write.
482490
*/
483491
SEC("tc")
484-
int clone_dynptr_rdonly_before_meta_dynptr_write(struct __sk_buff *ctx)
492+
int clone_meta_dynptr_rw_before_meta_dynptr_write(struct __sk_buff *ctx)
485493
{
486494
struct bpf_dynptr data, meta;
495+
__u8 meta_have[META_SIZE];
487496
const struct ethhdr *eth;
497+
int err;
488498

489499
bpf_dynptr_from_skb(ctx, 0, &data);
490500
eth = bpf_dynptr_slice(&data, 0, NULL, sizeof(*eth));
@@ -494,14 +504,20 @@ int clone_dynptr_rdonly_before_meta_dynptr_write(struct __sk_buff *ctx)
494504
if (eth->h_proto != 0)
495505
goto out;
496506

497-
/* Expect read-only metadata */
507+
/* Expect read-write metadata before unclone */
498508
bpf_dynptr_from_skb_meta(ctx, 0, &meta);
499-
if (!bpf_dynptr_is_rdonly(&meta) || bpf_dynptr_size(&meta) != META_SIZE)
509+
if (bpf_dynptr_is_rdonly(&meta))
500510
goto out;
501511

502-
/* Metadata write. Expect failure. */
503-
bpf_dynptr_from_skb_meta(ctx, 0, &meta);
504-
if (bpf_dynptr_write(&meta, 0, "x", 1, 0) != -EINVAL)
512+
err = bpf_dynptr_read(meta_have, META_SIZE, &meta, 0, 0);
513+
if (err || !check_metadata(meta_have))
514+
goto out;
515+
516+
/* Helper write to metadata will unclone the packet */
517+
bpf_dynptr_write(&meta, 0, &meta_have[0], 1, 0);
518+
519+
err = bpf_dynptr_read(meta_have, META_SIZE, &meta, 0, 0);
520+
if (err || !check_metadata(meta_have))
505521
goto out;
506522

507523
test_pass = true;

0 commit comments

Comments
 (0)