mirror of
https://github.com/tbsdtv/linux_media.git
synced 2025-07-22 20:30:58 +02:00
Merge branch 'Dynptr Verifier Adjustments'
Daniel Rosenberg says: ==================== These patches relax a few verifier requirements around dynptrs. Patches 1-3 are unchanged from v2, apart from rebasing Patch 4 is the same as in v1, see https://lore.kernel.org/bpf/CA+PiJmST4WUH061KaxJ4kRL=fqy3X6+Wgb2E2rrLT5OYjUzxfQ@mail.gmail.com/ Patch 5 adds a test for the change in Patch 4 ==================== Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
@@ -100,7 +100,7 @@ Hence, whenever a constant scalar argument is accepted by a kfunc which is not a
|
||||
size parameter, and the value of the constant matters for program safety, __k
|
||||
suffix should be used.
|
||||
|
||||
2.2.2 __uninit Annotation
|
||||
2.2.3 __uninit Annotation
|
||||
-------------------------
|
||||
|
||||
This annotation is used to indicate that the argument will be treated as
|
||||
@@ -117,6 +117,27 @@ Here, the dynptr will be treated as an uninitialized dynptr. Without this
|
||||
annotation, the verifier will reject the program if the dynptr passed in is
|
||||
not initialized.
|
||||
|
||||
2.2.4 __opt Annotation
|
||||
-------------------------
|
||||
|
||||
This annotation is used to indicate that the buffer associated with an __sz or __szk
|
||||
argument may be null. If the function is passed a nullptr in place of the buffer,
|
||||
the verifier will not check that length is appropriate for the buffer. The kfunc is
|
||||
responsible for checking if this buffer is null before using it.
|
||||
|
||||
An example is given below::
|
||||
|
||||
__bpf_kfunc void *bpf_dynptr_slice(..., void *buffer__opt, u32 buffer__szk)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
Here, the buffer may be null. If buffer is not null, it at least of size buffer_szk.
|
||||
Either way, the returned buffer is either NULL, or of size buffer_szk. Without this
|
||||
annotation, the verifier will reject the program if a null pointer is passed in with
|
||||
a nonzero size.
|
||||
|
||||
|
||||
.. _BPF_kfunc_nodef:
|
||||
|
||||
2.3 Using an existing kernel function
|
||||
|
@@ -4033,7 +4033,7 @@ __skb_header_pointer(const struct sk_buff *skb, int offset, int len,
|
||||
if (likely(hlen - offset >= len))
|
||||
return (void *)data + offset;
|
||||
|
||||
if (!skb || unlikely(skb_copy_bits(skb, offset, buffer, len) < 0))
|
||||
if (!skb || !buffer || unlikely(skb_copy_bits(skb, offset, buffer, len) < 0))
|
||||
return NULL;
|
||||
|
||||
return buffer;
|
||||
|
@@ -2190,13 +2190,15 @@ __bpf_kfunc struct task_struct *bpf_task_from_pid(s32 pid)
|
||||
* bpf_dynptr_slice() - Obtain a read-only pointer to the dynptr data.
|
||||
* @ptr: The dynptr whose data slice to retrieve
|
||||
* @offset: Offset into the dynptr
|
||||
* @buffer: User-provided buffer to copy contents into
|
||||
* @buffer__szk: Size (in bytes) of the buffer. This is the length of the
|
||||
* requested slice. This must be a constant.
|
||||
* @buffer__opt: User-provided buffer to copy contents into. May be NULL
|
||||
* @buffer__szk: Size (in bytes) of the buffer if present. This is the
|
||||
* length of the requested slice. This must be a constant.
|
||||
*
|
||||
* For non-skb and non-xdp type dynptrs, there is no difference between
|
||||
* bpf_dynptr_slice and bpf_dynptr_data.
|
||||
*
|
||||
* If buffer__opt is NULL, the call will fail if buffer_opt was needed.
|
||||
*
|
||||
* If the intention is to write to the data slice, please use
|
||||
* bpf_dynptr_slice_rdwr.
|
||||
*
|
||||
@@ -2213,7 +2215,7 @@ __bpf_kfunc struct task_struct *bpf_task_from_pid(s32 pid)
|
||||
* direct pointer)
|
||||
*/
|
||||
__bpf_kfunc void *bpf_dynptr_slice(const struct bpf_dynptr_kern *ptr, u32 offset,
|
||||
void *buffer, u32 buffer__szk)
|
||||
void *buffer__opt, u32 buffer__szk)
|
||||
{
|
||||
enum bpf_dynptr_type type;
|
||||
u32 len = buffer__szk;
|
||||
@@ -2233,15 +2235,17 @@ __bpf_kfunc void *bpf_dynptr_slice(const struct bpf_dynptr_kern *ptr, u32 offset
|
||||
case BPF_DYNPTR_TYPE_RINGBUF:
|
||||
return ptr->data + ptr->offset + offset;
|
||||
case BPF_DYNPTR_TYPE_SKB:
|
||||
return skb_header_pointer(ptr->data, ptr->offset + offset, len, buffer);
|
||||
return skb_header_pointer(ptr->data, ptr->offset + offset, len, buffer__opt);
|
||||
case BPF_DYNPTR_TYPE_XDP:
|
||||
{
|
||||
void *xdp_ptr = bpf_xdp_pointer(ptr->data, ptr->offset + offset, len);
|
||||
if (xdp_ptr)
|
||||
return xdp_ptr;
|
||||
|
||||
bpf_xdp_copy_buf(ptr->data, ptr->offset + offset, buffer, len, false);
|
||||
return buffer;
|
||||
if (!buffer__opt)
|
||||
return NULL;
|
||||
bpf_xdp_copy_buf(ptr->data, ptr->offset + offset, buffer__opt, len, false);
|
||||
return buffer__opt;
|
||||
}
|
||||
default:
|
||||
WARN_ONCE(true, "unknown dynptr type %d\n", type);
|
||||
@@ -2253,13 +2257,15 @@ __bpf_kfunc void *bpf_dynptr_slice(const struct bpf_dynptr_kern *ptr, u32 offset
|
||||
* bpf_dynptr_slice_rdwr() - Obtain a writable pointer to the dynptr data.
|
||||
* @ptr: The dynptr whose data slice to retrieve
|
||||
* @offset: Offset into the dynptr
|
||||
* @buffer: User-provided buffer to copy contents into
|
||||
* @buffer__szk: Size (in bytes) of the buffer. This is the length of the
|
||||
* requested slice. This must be a constant.
|
||||
* @buffer__opt: User-provided buffer to copy contents into. May be NULL
|
||||
* @buffer__szk: Size (in bytes) of the buffer if present. This is the
|
||||
* length of the requested slice. This must be a constant.
|
||||
*
|
||||
* For non-skb and non-xdp type dynptrs, there is no difference between
|
||||
* bpf_dynptr_slice and bpf_dynptr_data.
|
||||
*
|
||||
* If buffer__opt is NULL, the call will fail if buffer_opt was needed.
|
||||
*
|
||||
* The returned pointer is writable and may point to either directly the dynptr
|
||||
* data at the requested offset or to the buffer if unable to obtain a direct
|
||||
* data pointer to (example: the requested slice is to the paged area of an skb
|
||||
@@ -2290,7 +2296,7 @@ __bpf_kfunc void *bpf_dynptr_slice(const struct bpf_dynptr_kern *ptr, u32 offset
|
||||
* direct pointer)
|
||||
*/
|
||||
__bpf_kfunc void *bpf_dynptr_slice_rdwr(const struct bpf_dynptr_kern *ptr, u32 offset,
|
||||
void *buffer, u32 buffer__szk)
|
||||
void *buffer__opt, u32 buffer__szk)
|
||||
{
|
||||
if (!ptr->data || __bpf_dynptr_is_rdonly(ptr))
|
||||
return NULL;
|
||||
@@ -2317,7 +2323,7 @@ __bpf_kfunc void *bpf_dynptr_slice_rdwr(const struct bpf_dynptr_kern *ptr, u32 o
|
||||
* will be copied out into the buffer and the user will need to call
|
||||
* bpf_dynptr_write() to commit changes.
|
||||
*/
|
||||
return bpf_dynptr_slice(ptr, offset, buffer, buffer__szk);
|
||||
return bpf_dynptr_slice(ptr, offset, buffer__opt, buffer__szk);
|
||||
}
|
||||
|
||||
__bpf_kfunc int bpf_dynptr_adjust(struct bpf_dynptr_kern *ptr, u32 start, u32 end)
|
||||
|
@@ -7495,12 +7495,16 @@ static int check_reg_type(struct bpf_verifier_env *env, u32 regno,
|
||||
* ARG_PTR_TO_MEM + MAYBE_NULL is compatible with PTR_TO_MEM and PTR_TO_MEM + MAYBE_NULL,
|
||||
* but ARG_PTR_TO_MEM is compatible only with PTR_TO_MEM but NOT with PTR_TO_MEM + MAYBE_NULL
|
||||
*
|
||||
* ARG_PTR_TO_MEM is compatible with PTR_TO_MEM that is tagged with a dynptr type.
|
||||
*
|
||||
* Therefore we fold these flags depending on the arg_type before comparison.
|
||||
*/
|
||||
if (arg_type & MEM_RDONLY)
|
||||
type &= ~MEM_RDONLY;
|
||||
if (arg_type & PTR_MAYBE_NULL)
|
||||
type &= ~PTR_MAYBE_NULL;
|
||||
if (base_type(arg_type) == ARG_PTR_TO_MEM)
|
||||
type &= ~DYNPTR_TYPE_FLAG_MASK;
|
||||
|
||||
if (meta->func_id == BPF_FUNC_kptr_xchg && type & MEM_ALLOC)
|
||||
type &= ~MEM_ALLOC;
|
||||
@@ -9743,6 +9747,11 @@ static bool is_kfunc_arg_const_mem_size(const struct btf *btf,
|
||||
return __kfunc_param_match_suffix(btf, arg, "__szk");
|
||||
}
|
||||
|
||||
static bool is_kfunc_arg_optional(const struct btf *btf, const struct btf_param *arg)
|
||||
{
|
||||
return __kfunc_param_match_suffix(btf, arg, "__opt");
|
||||
}
|
||||
|
||||
static bool is_kfunc_arg_constant(const struct btf *btf, const struct btf_param *arg)
|
||||
{
|
||||
return __kfunc_param_match_suffix(btf, arg, "__k");
|
||||
@@ -10830,13 +10839,17 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
|
||||
break;
|
||||
case KF_ARG_PTR_TO_MEM_SIZE:
|
||||
{
|
||||
struct bpf_reg_state *buff_reg = ®s[regno];
|
||||
const struct btf_param *buff_arg = &args[i];
|
||||
struct bpf_reg_state *size_reg = ®s[regno + 1];
|
||||
const struct btf_param *size_arg = &args[i + 1];
|
||||
|
||||
ret = check_kfunc_mem_size_reg(env, size_reg, regno + 1);
|
||||
if (ret < 0) {
|
||||
verbose(env, "arg#%d arg#%d memory, len pair leads to invalid memory access\n", i, i + 1);
|
||||
return ret;
|
||||
if (!register_is_null(buff_reg) || !is_kfunc_arg_optional(meta->btf, buff_arg)) {
|
||||
ret = check_kfunc_mem_size_reg(env, size_reg, regno + 1);
|
||||
if (ret < 0) {
|
||||
verbose(env, "arg#%d arg#%d memory, len pair leads to invalid memory access\n", i, i + 1);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_kfunc_arg_const_mem_size(meta->btf, size_arg, size_reg)) {
|
||||
|
@@ -26,6 +26,8 @@ static struct {
|
||||
{"test_dynptr_is_null", SETUP_SYSCALL_SLEEP},
|
||||
{"test_dynptr_is_rdonly", SETUP_SKB_PROG},
|
||||
{"test_dynptr_clone", SETUP_SKB_PROG},
|
||||
{"test_dynptr_skb_no_buff", SETUP_SKB_PROG},
|
||||
{"test_dynptr_skb_strcmp", SETUP_SKB_PROG},
|
||||
};
|
||||
|
||||
static void verify_success(const char *prog_name, enum test_setup_type setup_type)
|
||||
|
@@ -1665,3 +1665,23 @@ int clone_xdp_packet_data(struct xdp_md *xdp)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Buffers that are provided must be sufficiently long */
|
||||
SEC("?cgroup_skb/egress")
|
||||
__failure __msg("memory, len pair leads to invalid memory access")
|
||||
int test_dynptr_skb_small_buff(struct __sk_buff *skb)
|
||||
{
|
||||
struct bpf_dynptr ptr;
|
||||
char buffer[8] = {};
|
||||
__u64 *data;
|
||||
|
||||
if (bpf_dynptr_from_skb(skb, 0, &ptr)) {
|
||||
err = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* This may return NULL. SKB may require a buffer */
|
||||
data = bpf_dynptr_slice(&ptr, 0, buffer, 9);
|
||||
|
||||
return !!data;
|
||||
}
|
||||
|
@@ -505,3 +505,41 @@ int test_dynptr_clone(struct __sk_buff *skb)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?cgroup_skb/egress")
|
||||
int test_dynptr_skb_no_buff(struct __sk_buff *skb)
|
||||
{
|
||||
struct bpf_dynptr ptr;
|
||||
__u64 *data;
|
||||
|
||||
if (bpf_dynptr_from_skb(skb, 0, &ptr)) {
|
||||
err = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* This may return NULL. SKB may require a buffer */
|
||||
data = bpf_dynptr_slice(&ptr, 0, NULL, 1);
|
||||
|
||||
return !!data;
|
||||
}
|
||||
|
||||
SEC("?cgroup_skb/egress")
|
||||
int test_dynptr_skb_strcmp(struct __sk_buff *skb)
|
||||
{
|
||||
struct bpf_dynptr ptr;
|
||||
char *data;
|
||||
|
||||
if (bpf_dynptr_from_skb(skb, 0, &ptr)) {
|
||||
err = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* This may return NULL. SKB may require a buffer */
|
||||
data = bpf_dynptr_slice(&ptr, 0, NULL, 10);
|
||||
if (data) {
|
||||
bpf_strncmp(data, 10, "foo");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
Reference in New Issue
Block a user