mirror of
https://github.com/torvalds/linux.git
synced 2026-07-28 01:55:51 +02:00
Merge branch 'allow-referenced-dynptr-to-be-overwritten-when-siblings-exists'
Amery Hung says: ==================== Allow referenced dynptr to be overwritten when siblings exists The patchset conditionally allow a referenced dynptr to be overwritten when its siblings (original dynptr or dynptr clone) exist. Do it before the verifier relation tracking refactor to mimimize verifier changes at a time. ==================== Link: https://patch.msgid.link/20260406150548.1354271-1-ameryhung@gmail.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
commit
5c662b1c17
|
|
@ -936,8 +936,27 @@ static int destroy_if_dynptr_stack_slot(struct bpf_verifier_env *env,
|
|||
spi = spi + 1;
|
||||
|
||||
if (dynptr_type_refcounted(state->stack[spi].spilled_ptr.dynptr.type)) {
|
||||
verbose(env, "cannot overwrite referenced dynptr\n");
|
||||
return -EINVAL;
|
||||
int ref_obj_id = state->stack[spi].spilled_ptr.ref_obj_id;
|
||||
int ref_cnt = 0;
|
||||
|
||||
/*
|
||||
* A referenced dynptr can be overwritten only if there is at
|
||||
* least one other dynptr sharing the same ref_obj_id,
|
||||
* ensuring the reference can still be properly released.
|
||||
*/
|
||||
for (i = 0; i < state->allocated_stack / BPF_REG_SIZE; i++) {
|
||||
if (state->stack[i].slot_type[0] != STACK_DYNPTR)
|
||||
continue;
|
||||
if (!state->stack[i].spilled_ptr.dynptr.first_slot)
|
||||
continue;
|
||||
if (state->stack[i].spilled_ptr.ref_obj_id == ref_obj_id)
|
||||
ref_cnt++;
|
||||
}
|
||||
|
||||
if (ref_cnt <= 1) {
|
||||
verbose(env, "cannot overwrite referenced dynptr\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
mark_stack_slot_scratched(env, spi);
|
||||
|
|
|
|||
|
|
@ -1993,3 +1993,118 @@ int test_dynptr_reg_type(void *ctx)
|
|||
global_call_bpf_dynptr((const struct bpf_dynptr *)current);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Overwriting a referenced dynptr is allowed if a clone still holds the ref */
|
||||
SEC("?raw_tp")
|
||||
__success
|
||||
int dynptr_overwrite_ref_with_clone(void *ctx)
|
||||
{
|
||||
struct bpf_dynptr ptr, clone;
|
||||
|
||||
bpf_ringbuf_reserve_dynptr(&ringbuf, 64, 0, &ptr);
|
||||
|
||||
bpf_dynptr_clone(&ptr, &clone);
|
||||
|
||||
/* Overwrite the original - clone still holds the ref */
|
||||
*(volatile __u8 *)&ptr = 0;
|
||||
|
||||
bpf_ringbuf_discard_dynptr(&clone, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Overwriting the last referenced dynptr should still be rejected */
|
||||
SEC("?raw_tp")
|
||||
__failure __msg("cannot overwrite referenced dynptr")
|
||||
int dynptr_overwrite_ref_last_clone(void *ctx)
|
||||
{
|
||||
struct bpf_dynptr ptr, clone;
|
||||
|
||||
bpf_ringbuf_reserve_dynptr(&ringbuf, 64, 0, &ptr);
|
||||
|
||||
bpf_dynptr_clone(&ptr, &clone);
|
||||
|
||||
/* Overwrite the original - clone still holds the ref, OK */
|
||||
*(volatile __u8 *)&ptr = 0;
|
||||
|
||||
/* Overwrite the last holder - this should fail */
|
||||
*(volatile __u8 *)&clone = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Overwriting a clone should be allowed if the original still holds the ref */
|
||||
SEC("?raw_tp")
|
||||
__success
|
||||
int dynptr_overwrite_clone_with_original(void *ctx)
|
||||
{
|
||||
struct bpf_dynptr ptr, clone;
|
||||
|
||||
bpf_ringbuf_reserve_dynptr(&ringbuf, 64, 0, &ptr);
|
||||
|
||||
bpf_dynptr_clone(&ptr, &clone);
|
||||
|
||||
/* Overwrite the clone - original still holds the ref */
|
||||
*(volatile __u8 *)&clone = 0;
|
||||
|
||||
bpf_ringbuf_discard_dynptr(&ptr, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Data slices from the destroyed dynptr should be invalidated */
|
||||
SEC("?raw_tp")
|
||||
__failure __msg("invalid mem access 'scalar'")
|
||||
int dynptr_overwrite_ref_invalidate_slice(void *ctx)
|
||||
{
|
||||
struct bpf_dynptr ptr, clone;
|
||||
int *data;
|
||||
|
||||
bpf_ringbuf_reserve_dynptr(&ringbuf, val, 0, &ptr);
|
||||
|
||||
data = bpf_dynptr_data(&ptr, 0, sizeof(val));
|
||||
if (!data)
|
||||
return 0;
|
||||
|
||||
bpf_dynptr_clone(&ptr, &clone);
|
||||
|
||||
/* Overwrite the original - clone holds the ref */
|
||||
*(volatile __u8 *)&ptr = 0;
|
||||
|
||||
/* data was from the original dynptr, should be invalid now */
|
||||
*data = 123;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Data slices from a dynptr clone should remain valid after
|
||||
* overwriting the original dynptr
|
||||
*/
|
||||
SEC("?raw_tp")
|
||||
__success
|
||||
int dynptr_overwrite_ref_clone_slice_valid(void *ctx)
|
||||
{
|
||||
struct bpf_dynptr ptr, clone;
|
||||
int *data;
|
||||
|
||||
bpf_ringbuf_reserve_dynptr(&ringbuf, val, 0, &ptr);
|
||||
|
||||
bpf_dynptr_clone(&ptr, &clone);
|
||||
|
||||
data = bpf_dynptr_data(&clone, 0, sizeof(val));
|
||||
if (!data) {
|
||||
bpf_ringbuf_discard_dynptr(&clone, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Overwrite the original - clone holds the ref */
|
||||
*(volatile __u8 *)&ptr = 0;
|
||||
|
||||
/* data is from the clone, should still be valid */
|
||||
*data = 123;
|
||||
|
||||
bpf_ringbuf_discard_dynptr(&clone, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user