Commit 0e74eb4d authored by Jakub Sitnicki's avatar Jakub Sitnicki Committed by Martin KaFai Lau
Browse files

selftests/bpf: Cover verifier checks for skb_meta dynptr type



dynptr for skb metadata behaves the same way as the dynptr for skb data
with one exception - writes to skb_meta dynptr don't invalidate existing
skb and skb_meta slices.

Duplicate those the skb dynptr tests which we can, since
bpf_dynptr_from_skb_meta kfunc can be called only from TC BPF, to cover the
skb_meta dynptr verifier checks.

Also add a couple of new tests (skb_data_valid_*) to ensure we don't
invalidate the slices in the mentioned case, which are specific to skb_meta
dynptr.

Signed-off-by: default avatarJakub Sitnicki <jakub@cloudflare.com>
Signed-off-by: default avatarMartin KaFai Lau <martin.lau@kernel.org>
Reviewed-by: default avatarJesse Brandeburg <jbrandeburg@cloudflare.com>
Link: https://patch.msgid.link/20250814-skb-metadata-thru-dynptr-v7-3-8a39e636e0fb@cloudflare.com
parent 6877cd39
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -32,6 +32,8 @@ static struct {
	{"test_ringbuf", SETUP_SYSCALL_SLEEP},
	{"test_skb_readonly", SETUP_SKB_PROG},
	{"test_dynptr_skb_data", SETUP_SKB_PROG},
	{"test_dynptr_skb_meta_data", SETUP_SKB_PROG},
	{"test_dynptr_skb_meta_flags", SETUP_SKB_PROG},
	{"test_adjust", SETUP_SYSCALL_SLEEP},
	{"test_adjust_err", SETUP_SYSCALL_SLEEP},
	{"test_zero_size_dynptr", SETUP_SYSCALL_SLEEP},
+258 −0
Original line number Diff line number Diff line
@@ -269,6 +269,26 @@ int data_slice_out_of_bounds_skb(struct __sk_buff *skb)
	return SK_PASS;
}

/* A metadata slice can't be accessed out of bounds */
SEC("?tc")
__failure __msg("value is outside of the allowed memory range")
int data_slice_out_of_bounds_skb_meta(struct __sk_buff *skb)
{
	struct bpf_dynptr meta;
	__u8 *md;

	bpf_dynptr_from_skb_meta(skb, 0, &meta);

	md = bpf_dynptr_slice_rdwr(&meta, 0, NULL, sizeof(*md));
	if (!md)
		return SK_DROP;

	/* this should fail */
	*(md + 1) = 42;

	return SK_PASS;
}

SEC("?raw_tp")
__failure __msg("value is outside of the allowed memory range")
int data_slice_out_of_bounds_map_value(void *ctx)
@@ -1089,6 +1109,26 @@ int skb_invalid_slice_write(struct __sk_buff *skb)
	return SK_PASS;
}

/* bpf_dynptr_slice()s are read-only and cannot be written to */
SEC("?tc")
__failure __msg("R{{[0-9]+}} cannot write into rdonly_mem")
int skb_meta_invalid_slice_write(struct __sk_buff *skb)
{
	struct bpf_dynptr meta;
	__u8 *md;

	bpf_dynptr_from_skb_meta(skb, 0, &meta);

	md = bpf_dynptr_slice(&meta, 0, NULL, sizeof(*md));
	if (!md)
		return SK_DROP;

	/* this should fail */
	*md = 42;

	return SK_PASS;
}

/* The read-only data slice is invalidated whenever a helper changes packet data */
SEC("?tc")
__failure __msg("invalid mem access 'scalar'")
@@ -1192,6 +1232,188 @@ int skb_invalid_data_slice4(struct __sk_buff *skb)
	return SK_PASS;
}

/* Read-only skb data slice is invalidated on write to skb metadata */
SEC("?tc")
__failure __msg("invalid mem access 'scalar'")
int ro_skb_slice_invalid_after_metadata_write(struct __sk_buff *skb)
{
	struct bpf_dynptr data, meta;
	__u8 *d;

	bpf_dynptr_from_skb(skb, 0, &data);
	bpf_dynptr_from_skb_meta(skb, 0, &meta);

	d = bpf_dynptr_slice(&data, 0, NULL, sizeof(*d));
	if (!d)
		return SK_DROP;

	bpf_dynptr_write(&meta, 0, "x", 1, 0);

	/* this should fail */
	val = *d;

	return SK_PASS;
}

/* Read-write skb data slice is invalidated on write to skb metadata */
SEC("?tc")
__failure __msg("invalid mem access 'scalar'")
int rw_skb_slice_invalid_after_metadata_write(struct __sk_buff *skb)
{
	struct bpf_dynptr data, meta;
	__u8 *d;

	bpf_dynptr_from_skb(skb, 0, &data);
	bpf_dynptr_from_skb_meta(skb, 0, &meta);

	d = bpf_dynptr_slice_rdwr(&data, 0, NULL, sizeof(*d));
	if (!d)
		return SK_DROP;

	bpf_dynptr_write(&meta, 0, "x", 1, 0);

	/* this should fail */
	*d = 42;

	return SK_PASS;
}

/* Read-only skb metadata slice is invalidated on write to skb data */
SEC("?tc")
__failure __msg("invalid mem access 'scalar'")
int ro_skb_meta_slice_invalid_after_payload_write(struct __sk_buff *skb)
{
	struct bpf_dynptr data, meta;
	__u8 *md;

	bpf_dynptr_from_skb(skb, 0, &data);
	bpf_dynptr_from_skb_meta(skb, 0, &meta);

	md = bpf_dynptr_slice(&meta, 0, NULL, sizeof(*md));
	if (!md)
		return SK_DROP;

	bpf_dynptr_write(&data, 0, "x", 1, 0);

	/* this should fail */
	val = *md;

	return SK_PASS;
}

/* Read-write skb metadata slice is invalidated on write to skb data slice */
SEC("?tc")
__failure __msg("invalid mem access 'scalar'")
int rw_skb_meta_slice_invalid_after_payload_write(struct __sk_buff *skb)
{
	struct bpf_dynptr data, meta;
	__u8 *md;

	bpf_dynptr_from_skb(skb, 0, &data);
	bpf_dynptr_from_skb_meta(skb, 0, &meta);

	md = bpf_dynptr_slice_rdwr(&meta, 0, NULL, sizeof(*md));
	if (!md)
		return SK_DROP;

	bpf_dynptr_write(&data, 0, "x", 1, 0);

	/* this should fail */
	*md = 42;

	return SK_PASS;
}

/* Read-only skb metadata slice is invalidated whenever a helper changes packet data */
SEC("?tc")
__failure __msg("invalid mem access 'scalar'")
int ro_skb_meta_slice_invalid_after_payload_helper(struct __sk_buff *skb)
{
	struct bpf_dynptr meta;
	__u8 *md;

	bpf_dynptr_from_skb_meta(skb, 0, &meta);

	md = bpf_dynptr_slice(&meta, 0, NULL, sizeof(*md));
	if (!md)
		return SK_DROP;

	if (bpf_skb_pull_data(skb, skb->len))
		return SK_DROP;

	/* this should fail */
	val = *md;

	return SK_PASS;
}

/* Read-write skb metadata slice is invalidated whenever a helper changes packet data */
SEC("?tc")
__failure __msg("invalid mem access 'scalar'")
int rw_skb_meta_slice_invalid_after_payload_helper(struct __sk_buff *skb)
{
	struct bpf_dynptr meta;
	__u8 *md;

	bpf_dynptr_from_skb_meta(skb, 0, &meta);

	md = bpf_dynptr_slice_rdwr(&meta, 0, NULL, sizeof(*md));
	if (!md)
		return SK_DROP;

	if (bpf_skb_pull_data(skb, skb->len))
		return SK_DROP;

	/* this should fail */
	*md = 42;

	return SK_PASS;
}

/* Read-only skb metadata slice is invalidated on write to skb metadata */
SEC("?tc")
__failure __msg("invalid mem access 'scalar'")
int ro_skb_meta_slice_invalid_after_metadata_write(struct __sk_buff *skb)
{
	struct bpf_dynptr meta;
	__u8 *md;

	bpf_dynptr_from_skb_meta(skb, 0, &meta);

	md = bpf_dynptr_slice(&meta, 0, NULL, sizeof(*md));
	if (!md)
		return SK_DROP;

	bpf_dynptr_write(&meta, 0, "x", 1, 0);

	/* this should fail */
	val = *md;

	return SK_PASS;
}

/* Read-write skb metadata slice is invalidated on write to skb metadata */
SEC("?tc")
__failure __msg("invalid mem access 'scalar'")
int rw_skb_meta_slice_invalid_after_metadata_write(struct __sk_buff *skb)
{
	struct bpf_dynptr meta;
	__u8 *md;

	bpf_dynptr_from_skb_meta(skb, 0, &meta);

	md = bpf_dynptr_slice_rdwr(&meta, 0, NULL, sizeof(*md));
	if (!md)
		return SK_DROP;

	bpf_dynptr_write(&meta, 0, "x", 1, 0);

	/* this should fail */
	*md = 42;

	return SK_PASS;
}

/* The read-only data slice is invalidated whenever a helper changes packet data */
SEC("?xdp")
__failure __msg("invalid mem access 'scalar'")
@@ -1255,6 +1477,19 @@ int skb_invalid_ctx(void *ctx)
	return 0;
}

/* Only supported prog type can create skb_meta-type dynptrs */
SEC("?raw_tp")
__failure __msg("calling kernel function bpf_dynptr_from_skb_meta is not allowed")
int skb_meta_invalid_ctx(void *ctx)
{
	struct bpf_dynptr meta;

	/* this should fail */
	bpf_dynptr_from_skb_meta(ctx, 0, &meta);

	return 0;
}

SEC("fentry/skb_tx_error")
__failure __msg("must be referenced or trusted")
int BPF_PROG(skb_invalid_ctx_fentry, void *skb)
@@ -1665,6 +1900,29 @@ int clone_skb_packet_data(struct __sk_buff *skb)
	return 0;
}

/* A skb clone's metadata slice becomes invalid anytime packet data changes */
SEC("?tc")
__failure __msg("invalid mem access 'scalar'")
int clone_skb_packet_meta(struct __sk_buff *skb)
{
	struct bpf_dynptr clone, meta;
	__u8 *md;

	bpf_dynptr_from_skb_meta(skb, 0, &meta);
	bpf_dynptr_clone(&meta, &clone);
	md = bpf_dynptr_slice_rdwr(&clone, 0, NULL, sizeof(*md));
	if (!md)
		return SK_DROP;

	if (bpf_skb_pull_data(skb, skb->len))
		return SK_DROP;

	/* this should fail */
	*md = 42;

	return 0;
}

/* A xdp clone's data slices should be invalid anytime packet data changes */
SEC("?xdp")
__failure __msg("invalid mem access 'scalar'")
+55 −0
Original line number Diff line number Diff line
@@ -211,6 +211,61 @@ int test_dynptr_skb_data(struct __sk_buff *skb)
	return 1;
}

SEC("?tc")
int test_dynptr_skb_meta_data(struct __sk_buff *skb)
{
	struct bpf_dynptr meta;
	__u8 *md;
	int ret;

	err = 1;
	ret = bpf_dynptr_from_skb_meta(skb, 0, &meta);
	if (ret)
		return 1;

	/* This should return NULL. Must use bpf_dynptr_slice API */
	err = 2;
	md = bpf_dynptr_data(&meta, 0, sizeof(*md));
	if (md)
		return 1;

	err = 0;
	return 1;
}

/* Check that skb metadata dynptr ops don't accept any flags. */
SEC("?tc")
int test_dynptr_skb_meta_flags(struct __sk_buff *skb)
{
	const __u64 INVALID_FLAGS = ~0ULL;
	struct bpf_dynptr meta;
	__u8 buf;
	int ret;

	err = 1;
	ret = bpf_dynptr_from_skb_meta(skb, INVALID_FLAGS, &meta);
	if (ret != -EINVAL)
		return 1;

	err = 2;
	ret = bpf_dynptr_from_skb_meta(skb, 0, &meta);
	if (ret)
		return 1;

	err = 3;
	ret = bpf_dynptr_read(&buf, 0, &meta, 0, INVALID_FLAGS);
	if (ret != -EINVAL)
		return 1;

	err = 4;
	ret = bpf_dynptr_write(&meta, 0, &buf, 0, INVALID_FLAGS);
	if (ret != -EINVAL)
		return 1;

	err = 0;
	return 1;
}

SEC("tp/syscalls/sys_enter_nanosleep")
int test_adjust(void *ctx)
{