diff options
| author | Dmitry Torokhov <[email protected]> | 2023-05-01 15:20:08 -0700 | 
|---|---|---|
| committer | Dmitry Torokhov <[email protected]> | 2023-05-01 15:20:08 -0700 | 
| commit | 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e (patch) | |
| tree | d57f3a63479a07b4e0cece029886e76e04feb984 /tools/testing/selftests/bpf/progs/dynptr_fail.c | |
| parent | 5dc63e56a9cf8df0b59c234a505a1653f1bdf885 (diff) | |
| parent | 53bea86b5712c7491bb3dae12e271666df0a308c (diff) | |
Merge branch 'next' into for-linus
Prepare input updates for 6.4 merge window.
Diffstat (limited to 'tools/testing/selftests/bpf/progs/dynptr_fail.c')
| -rw-r--r-- | tools/testing/selftests/bpf/progs/dynptr_fail.c | 455 | 
1 files changed, 447 insertions, 8 deletions
diff --git a/tools/testing/selftests/bpf/progs/dynptr_fail.c b/tools/testing/selftests/bpf/progs/dynptr_fail.c index 78debc1b3820..aa5b69354b91 100644 --- a/tools/testing/selftests/bpf/progs/dynptr_fail.c +++ b/tools/testing/selftests/bpf/progs/dynptr_fail.c @@ -35,6 +35,13 @@ struct {  	__type(value, __u32);  } array_map3 SEC(".maps"); +struct { +	__uint(type, BPF_MAP_TYPE_ARRAY); +	__uint(max_entries, 1); +	__type(key, __u32); +	__type(value, __u64); +} array_map4 SEC(".maps"); +  struct sample {  	int pid;  	long value; @@ -67,7 +74,7 @@ static int get_map_val_dynptr(struct bpf_dynptr *ptr)   * bpf_ringbuf_submit/discard_dynptr call   */  SEC("?raw_tp") -__failure __msg("Unreleased reference id=1") +__failure __msg("Unreleased reference id=2")  int ringbuf_missing_release1(void *ctx)  {  	struct bpf_dynptr ptr; @@ -80,7 +87,7 @@ int ringbuf_missing_release1(void *ctx)  }  SEC("?raw_tp") -__failure __msg("Unreleased reference id=2") +__failure __msg("Unreleased reference id=4")  int ringbuf_missing_release2(void *ctx)  {  	struct bpf_dynptr ptr1, ptr2; @@ -382,7 +389,7 @@ int invalid_helper1(void *ctx)  /* A dynptr can't be passed into a helper function at a non-zero offset */  SEC("?raw_tp") -__failure __msg("Expected an initialized dynptr as arg #3") +__failure __msg("cannot pass in dynptr at an offset=-8")  int invalid_helper2(void *ctx)  {  	struct bpf_dynptr ptr; @@ -420,7 +427,7 @@ int invalid_write1(void *ctx)   * offset   */  SEC("?raw_tp") -__failure __msg("Expected an initialized dynptr as arg #3") +__failure __msg("cannot overwrite referenced dynptr")  int invalid_write2(void *ctx)  {  	struct bpf_dynptr ptr; @@ -444,7 +451,7 @@ int invalid_write2(void *ctx)   * non-const offset   */  SEC("?raw_tp") -__failure __msg("Expected an initialized dynptr as arg #1") +__failure __msg("cannot overwrite referenced dynptr")  int invalid_write3(void *ctx)  {  	struct bpf_dynptr ptr; @@ -476,7 +483,7 @@ static int invalid_write4_callback(__u32 index, void *data)   * be invalidated as a dynptr   */  SEC("?raw_tp") -__failure __msg("arg 1 is an unacquired reference") +__failure __msg("cannot overwrite referenced dynptr")  int invalid_write4(void *ctx)  {  	struct bpf_dynptr ptr; @@ -584,7 +591,7 @@ int invalid_read4(void *ctx)  /* Initializing a dynptr on an offset should fail */  SEC("?raw_tp") -__failure __msg("invalid write to stack") +__failure __msg("cannot pass in dynptr at an offset=0")  int invalid_offset(void *ctx)  {  	struct bpf_dynptr ptr; @@ -623,7 +630,7 @@ static int release_twice_callback_fn(__u32 index, void *data)  }  /* Test that releasing a dynptr twice, where one of the releases happens - * within a calback function, fails + * within a callback function, fails   */  SEC("?raw_tp")  __failure __msg("arg 1 is an unacquired reference") @@ -653,3 +660,435 @@ int dynptr_from_mem_invalid_api(void *ctx)  	return 0;  } + +SEC("?tc") +__failure __msg("cannot overwrite referenced dynptr") __log_level(2) +int dynptr_pruning_overwrite(struct __sk_buff *ctx) +{ +	asm volatile ( +		"r9 = 0xeB9F;				\ +		 r6 = %[ringbuf] ll;			\ +		 r1 = r6;				\ +		 r2 = 8;				\ +		 r3 = 0;				\ +		 r4 = r10;				\ +		 r4 += -16;				\ +		 call %[bpf_ringbuf_reserve_dynptr];	\ +		 if r0 == 0 goto pjmp1;			\ +		 goto pjmp2;				\ +	pjmp1:						\ +		 *(u64 *)(r10 - 16) = r9;		\ +	pjmp2:						\ +		 r1 = r10;				\ +		 r1 += -16;				\ +		 r2 = 0;				\ +		 call %[bpf_ringbuf_discard_dynptr];	" +		: +		: __imm(bpf_ringbuf_reserve_dynptr), +		  __imm(bpf_ringbuf_discard_dynptr), +		  __imm_addr(ringbuf) +		: __clobber_all +	); +	return 0; +} + +SEC("?tc") +__success __msg("12: safe") __log_level(2) +int dynptr_pruning_stacksafe(struct __sk_buff *ctx) +{ +	asm volatile ( +		"r9 = 0xeB9F;				\ +		 r6 = %[ringbuf] ll;			\ +		 r1 = r6;				\ +		 r2 = 8;				\ +		 r3 = 0;				\ +		 r4 = r10;				\ +		 r4 += -16;				\ +		 call %[bpf_ringbuf_reserve_dynptr];	\ +		 if r0 == 0 goto stjmp1;		\ +		 goto stjmp2;				\ +	stjmp1:						\ +		 r9 = r9;				\ +	stjmp2:						\ +		 r1 = r10;				\ +		 r1 += -16;				\ +		 r2 = 0;				\ +		 call %[bpf_ringbuf_discard_dynptr];	" +		: +		: __imm(bpf_ringbuf_reserve_dynptr), +		  __imm(bpf_ringbuf_discard_dynptr), +		  __imm_addr(ringbuf) +		: __clobber_all +	); +	return 0; +} + +SEC("?tc") +__failure __msg("cannot overwrite referenced dynptr") __log_level(2) +int dynptr_pruning_type_confusion(struct __sk_buff *ctx) +{ +	asm volatile ( +		"r6 = %[array_map4] ll;			\ +		 r7 = %[ringbuf] ll;			\ +		 r1 = r6;				\ +		 r2 = r10;				\ +		 r2 += -8;				\ +		 r9 = 0;				\ +		 *(u64 *)(r2 + 0) = r9;			\ +		 r3 = r10;				\ +		 r3 += -24;				\ +		 r9 = 0xeB9FeB9F;			\ +		 *(u64 *)(r10 - 16) = r9;		\ +		 *(u64 *)(r10 - 24) = r9;		\ +		 r9 = 0;				\ +		 r4 = 0;				\ +		 r8 = r2;				\ +		 call %[bpf_map_update_elem];		\ +		 r1 = r6;				\ +		 r2 = r8;				\ +		 call %[bpf_map_lookup_elem];		\ +		 if r0 != 0 goto tjmp1;			\ +		 exit;					\ +	tjmp1:						\ +		 r8 = r0;				\ +		 r1 = r7;				\ +		 r2 = 8;				\ +		 r3 = 0;				\ +		 r4 = r10;				\ +		 r4 += -16;				\ +		 r0 = *(u64 *)(r0 + 0);			\ +		 call %[bpf_ringbuf_reserve_dynptr];	\ +		 if r0 == 0 goto tjmp2;			\ +		 r8 = r8;				\ +		 r8 = r8;				\ +		 r8 = r8;				\ +		 r8 = r8;				\ +		 r8 = r8;				\ +		 r8 = r8;				\ +		 r8 = r8;				\ +		 goto tjmp3;				\ +	tjmp2:						\ +		 *(u64 *)(r10 - 8) = r9;		\ +		 *(u64 *)(r10 - 16) = r9;		\ +		 r1 = r8;				\ +		 r1 += 8;				\ +		 r2 = 0;				\ +		 r3 = 0;				\ +		 r4 = r10;				\ +		 r4 += -16;				\ +		 call %[bpf_dynptr_from_mem];		\ +	tjmp3:						\ +		 r1 = r10;				\ +		 r1 += -16;				\ +		 r2 = 0;				\ +		 call %[bpf_ringbuf_discard_dynptr];	" +		: +		: __imm(bpf_map_update_elem), +		  __imm(bpf_map_lookup_elem), +		  __imm(bpf_ringbuf_reserve_dynptr), +		  __imm(bpf_dynptr_from_mem), +		  __imm(bpf_ringbuf_discard_dynptr), +		  __imm_addr(array_map4), +		  __imm_addr(ringbuf) +		: __clobber_all +	); +	return 0; +} + +SEC("?tc") +__failure __msg("dynptr has to be at a constant offset") __log_level(2) +int dynptr_var_off_overwrite(struct __sk_buff *ctx) +{ +	asm volatile ( +		"r9 = 16;				\ +		 *(u32 *)(r10 - 4) = r9;		\ +		 r8 = *(u32 *)(r10 - 4);		\ +		 if r8 >= 0 goto vjmp1;			\ +		 r0 = 1;				\ +		 exit;					\ +	vjmp1:						\ +		 if r8 <= 16 goto vjmp2;		\ +		 r0 = 1;				\ +		 exit;					\ +	vjmp2:						\ +		 r8 &= 16;				\ +		 r1 = %[ringbuf] ll;			\ +		 r2 = 8;				\ +		 r3 = 0;				\ +		 r4 = r10;				\ +		 r4 += -32;				\ +		 r4 += r8;				\ +		 call %[bpf_ringbuf_reserve_dynptr];	\ +		 r9 = 0xeB9F;				\ +		 *(u64 *)(r10 - 16) = r9;		\ +		 r1 = r10;				\ +		 r1 += -32;				\ +		 r1 += r8;				\ +		 r2 = 0;				\ +		 call %[bpf_ringbuf_discard_dynptr];	" +		: +		: __imm(bpf_ringbuf_reserve_dynptr), +		  __imm(bpf_ringbuf_discard_dynptr), +		  __imm_addr(ringbuf) +		: __clobber_all +	); +	return 0; +} + +SEC("?tc") +__failure __msg("cannot overwrite referenced dynptr") __log_level(2) +int dynptr_partial_slot_invalidate(struct __sk_buff *ctx) +{ +	asm volatile ( +		"r6 = %[ringbuf] ll;			\ +		 r7 = %[array_map4] ll;			\ +		 r1 = r7;				\ +		 r2 = r10;				\ +		 r2 += -8;				\ +		 r9 = 0;				\ +		 *(u64 *)(r2 + 0) = r9;			\ +		 r3 = r2;				\ +		 r4 = 0;				\ +		 r8 = r2;				\ +		 call %[bpf_map_update_elem];		\ +		 r1 = r7;				\ +		 r2 = r8;				\ +		 call %[bpf_map_lookup_elem];		\ +		 if r0 != 0 goto sjmp1;			\ +		 exit;					\ +	sjmp1:						\ +		 r7 = r0;				\ +		 r1 = r6;				\ +		 r2 = 8;				\ +		 r3 = 0;				\ +		 r4 = r10;				\ +		 r4 += -24;				\ +		 call %[bpf_ringbuf_reserve_dynptr];	\ +		 *(u64 *)(r10 - 16) = r9;		\ +		 r1 = r7;				\ +		 r2 = 8;				\ +		 r3 = 0;				\ +		 r4 = r10;				\ +		 r4 += -16;				\ +		 call %[bpf_dynptr_from_mem];		\ +		 r1 = r10;				\ +		 r1 += -512;				\ +		 r2 = 488;				\ +		 r3 = r10;				\ +		 r3 += -24;				\ +		 r4 = 0;				\ +		 r5 = 0;				\ +		 call %[bpf_dynptr_read];		\ +		 r8 = 1;				\ +		 if r0 != 0 goto sjmp2;			\ +		 r8 = 0;				\ +	sjmp2:						\ +		 r1 = r10;				\ +		 r1 += -24;				\ +		 r2 = 0;				\ +		 call %[bpf_ringbuf_discard_dynptr];	" +		: +		: __imm(bpf_map_update_elem), +		  __imm(bpf_map_lookup_elem), +		  __imm(bpf_ringbuf_reserve_dynptr), +		  __imm(bpf_ringbuf_discard_dynptr), +		  __imm(bpf_dynptr_from_mem), +		  __imm(bpf_dynptr_read), +		  __imm_addr(ringbuf), +		  __imm_addr(array_map4) +		: __clobber_all +	); +	return 0; +} + +/* Test that it is allowed to overwrite unreferenced dynptr. */ +SEC("?raw_tp") +__success +int dynptr_overwrite_unref(void *ctx) +{ +	struct bpf_dynptr ptr; + +	if (get_map_val_dynptr(&ptr)) +		return 0; +	if (get_map_val_dynptr(&ptr)) +		return 0; +	if (get_map_val_dynptr(&ptr)) +		return 0; + +	return 0; +} + +/* Test that slices are invalidated on reinitializing a dynptr. */ +SEC("?raw_tp") +__failure __msg("invalid mem access 'scalar'") +int dynptr_invalidate_slice_reinit(void *ctx) +{ +	struct bpf_dynptr ptr; +	__u8 *p; + +	if (get_map_val_dynptr(&ptr)) +		return 0; +	p = bpf_dynptr_data(&ptr, 0, 1); +	if (!p) +		return 0; +	if (get_map_val_dynptr(&ptr)) +		return 0; +	/* this should fail */ +	return *p; +} + +/* Invalidation of dynptr slices on destruction of dynptr should not miss + * mem_or_null pointers. + */ +SEC("?raw_tp") +__failure __msg("R1 type=scalar expected=percpu_ptr_") +int dynptr_invalidate_slice_or_null(void *ctx) +{ +	struct bpf_dynptr ptr; +	__u8 *p; + +	if (get_map_val_dynptr(&ptr)) +		return 0; + +	p = bpf_dynptr_data(&ptr, 0, 1); +	*(__u8 *)&ptr = 0; +	/* this should fail */ +	bpf_this_cpu_ptr(p); +	return 0; +} + +/* Destruction of dynptr should also any slices obtained from it */ +SEC("?raw_tp") +__failure __msg("R7 invalid mem access 'scalar'") +int dynptr_invalidate_slice_failure(void *ctx) +{ +	struct bpf_dynptr ptr1; +	struct bpf_dynptr ptr2; +	__u8 *p1, *p2; + +	if (get_map_val_dynptr(&ptr1)) +		return 0; +	if (get_map_val_dynptr(&ptr2)) +		return 0; + +	p1 = bpf_dynptr_data(&ptr1, 0, 1); +	if (!p1) +		return 0; +	p2 = bpf_dynptr_data(&ptr2, 0, 1); +	if (!p2) +		return 0; + +	*(__u8 *)&ptr1 = 0; +	/* this should fail */ +	return *p1; +} + +/* Invalidation of slices should be scoped and should not prevent dereferencing + * slices of another dynptr after destroying unrelated dynptr + */ +SEC("?raw_tp") +__success +int dynptr_invalidate_slice_success(void *ctx) +{ +	struct bpf_dynptr ptr1; +	struct bpf_dynptr ptr2; +	__u8 *p1, *p2; + +	if (get_map_val_dynptr(&ptr1)) +		return 1; +	if (get_map_val_dynptr(&ptr2)) +		return 1; + +	p1 = bpf_dynptr_data(&ptr1, 0, 1); +	if (!p1) +		return 1; +	p2 = bpf_dynptr_data(&ptr2, 0, 1); +	if (!p2) +		return 1; + +	*(__u8 *)&ptr1 = 0; +	return *p2; +} + +/* Overwriting referenced dynptr should be rejected */ +SEC("?raw_tp") +__failure __msg("cannot overwrite referenced dynptr") +int dynptr_overwrite_ref(void *ctx) +{ +	struct bpf_dynptr ptr; + +	bpf_ringbuf_reserve_dynptr(&ringbuf, 64, 0, &ptr); +	/* this should fail */ +	if (get_map_val_dynptr(&ptr)) +		bpf_ringbuf_discard_dynptr(&ptr, 0); +	return 0; +} + +/* Reject writes to dynptr slot from bpf_dynptr_read */ +SEC("?raw_tp") +__failure __msg("potential write to dynptr at off=-16") +int dynptr_read_into_slot(void *ctx) +{ +	union { +		struct { +			char _pad[48]; +			struct bpf_dynptr ptr; +		}; +		char buf[64]; +	} data; + +	bpf_ringbuf_reserve_dynptr(&ringbuf, 64, 0, &data.ptr); +	/* this should fail */ +	bpf_dynptr_read(data.buf, sizeof(data.buf), &data.ptr, 0, 0); + +	return 0; +} + +/* Reject writes to dynptr slot for uninit arg */ +SEC("?raw_tp") +__failure __msg("potential write to dynptr at off=-16") +int uninit_write_into_slot(void *ctx) +{ +	struct { +		char buf[64]; +		struct bpf_dynptr ptr; +	} data; + +	bpf_ringbuf_reserve_dynptr(&ringbuf, 80, 0, &data.ptr); +	/* this should fail */ +	bpf_get_current_comm(data.buf, 80); + +	return 0; +} + +static int callback(__u32 index, void *data) +{ +        *(__u32 *)data = 123; + +        return 0; +} + +/* If the dynptr is written into in a callback function, its data + * slices should be invalidated as well. + */ +SEC("?raw_tp") +__failure __msg("invalid mem access 'scalar'") +int invalid_data_slices(void *ctx) +{ +	struct bpf_dynptr ptr; +	__u32 *slice; + +	if (get_map_val_dynptr(&ptr)) +		return 0; + +	slice = bpf_dynptr_data(&ptr, 0, sizeof(__u32)); +	if (!slice) +		return 0; + +	bpf_loop(10, callback, &ptr, 0); + +	/* this should fail */ +	*slice = 1; + +	return 0; +}  |