mirror of
https://github.com/tbsdtv/linux_media.git
synced 2025-07-23 12:43:29 +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
|
size parameter, and the value of the constant matters for program safety, __k
|
||||||
suffix should be used.
|
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
|
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
|
annotation, the verifier will reject the program if the dynptr passed in is
|
||||||
not initialized.
|
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:
|
.. _BPF_kfunc_nodef:
|
||||||
|
|
||||||
2.3 Using an existing kernel function
|
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))
|
if (likely(hlen - offset >= len))
|
||||||
return (void *)data + offset;
|
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 NULL;
|
||||||
|
|
||||||
return buffer;
|
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.
|
* bpf_dynptr_slice() - Obtain a read-only pointer to the dynptr data.
|
||||||
* @ptr: The dynptr whose data slice to retrieve
|
* @ptr: The dynptr whose data slice to retrieve
|
||||||
* @offset: Offset into the dynptr
|
* @offset: Offset into the dynptr
|
||||||
* @buffer: User-provided buffer to copy contents into
|
* @buffer__opt: User-provided buffer to copy contents into. May be NULL
|
||||||
* @buffer__szk: Size (in bytes) of the buffer. This is the length of the
|
* @buffer__szk: Size (in bytes) of the buffer if present. This is the
|
||||||
* requested slice. This must be a constant.
|
* length of the requested slice. This must be a constant.
|
||||||
*
|
*
|
||||||
* For non-skb and non-xdp type dynptrs, there is no difference between
|
* For non-skb and non-xdp type dynptrs, there is no difference between
|
||||||
* bpf_dynptr_slice and bpf_dynptr_data.
|
* 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
|
* If the intention is to write to the data slice, please use
|
||||||
* bpf_dynptr_slice_rdwr.
|
* bpf_dynptr_slice_rdwr.
|
||||||
*
|
*
|
||||||
@@ -2213,7 +2215,7 @@ __bpf_kfunc struct task_struct *bpf_task_from_pid(s32 pid)
|
|||||||
* direct pointer)
|
* direct pointer)
|
||||||
*/
|
*/
|
||||||
__bpf_kfunc void *bpf_dynptr_slice(const struct bpf_dynptr_kern *ptr, u32 offset,
|
__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;
|
enum bpf_dynptr_type type;
|
||||||
u32 len = buffer__szk;
|
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:
|
case BPF_DYNPTR_TYPE_RINGBUF:
|
||||||
return ptr->data + ptr->offset + offset;
|
return ptr->data + ptr->offset + offset;
|
||||||
case BPF_DYNPTR_TYPE_SKB:
|
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:
|
case BPF_DYNPTR_TYPE_XDP:
|
||||||
{
|
{
|
||||||
void *xdp_ptr = bpf_xdp_pointer(ptr->data, ptr->offset + offset, len);
|
void *xdp_ptr = bpf_xdp_pointer(ptr->data, ptr->offset + offset, len);
|
||||||
if (xdp_ptr)
|
if (xdp_ptr)
|
||||||
return xdp_ptr;
|
return xdp_ptr;
|
||||||
|
|
||||||
bpf_xdp_copy_buf(ptr->data, ptr->offset + offset, buffer, len, false);
|
if (!buffer__opt)
|
||||||
return buffer;
|
return NULL;
|
||||||
|
bpf_xdp_copy_buf(ptr->data, ptr->offset + offset, buffer__opt, len, false);
|
||||||
|
return buffer__opt;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
WARN_ONCE(true, "unknown dynptr type %d\n", type);
|
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.
|
* bpf_dynptr_slice_rdwr() - Obtain a writable pointer to the dynptr data.
|
||||||
* @ptr: The dynptr whose data slice to retrieve
|
* @ptr: The dynptr whose data slice to retrieve
|
||||||
* @offset: Offset into the dynptr
|
* @offset: Offset into the dynptr
|
||||||
* @buffer: User-provided buffer to copy contents into
|
* @buffer__opt: User-provided buffer to copy contents into. May be NULL
|
||||||
* @buffer__szk: Size (in bytes) of the buffer. This is the length of the
|
* @buffer__szk: Size (in bytes) of the buffer if present. This is the
|
||||||
* requested slice. This must be a constant.
|
* length of the requested slice. This must be a constant.
|
||||||
*
|
*
|
||||||
* For non-skb and non-xdp type dynptrs, there is no difference between
|
* For non-skb and non-xdp type dynptrs, there is no difference between
|
||||||
* bpf_dynptr_slice and bpf_dynptr_data.
|
* 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
|
* 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 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
|
* 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)
|
* direct pointer)
|
||||||
*/
|
*/
|
||||||
__bpf_kfunc void *bpf_dynptr_slice_rdwr(const struct bpf_dynptr_kern *ptr, u32 offset,
|
__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))
|
if (!ptr->data || __bpf_dynptr_is_rdonly(ptr))
|
||||||
return NULL;
|
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
|
* will be copied out into the buffer and the user will need to call
|
||||||
* bpf_dynptr_write() to commit changes.
|
* 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)
|
__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,
|
* 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
|
* 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.
|
* Therefore we fold these flags depending on the arg_type before comparison.
|
||||||
*/
|
*/
|
||||||
if (arg_type & MEM_RDONLY)
|
if (arg_type & MEM_RDONLY)
|
||||||
type &= ~MEM_RDONLY;
|
type &= ~MEM_RDONLY;
|
||||||
if (arg_type & PTR_MAYBE_NULL)
|
if (arg_type & PTR_MAYBE_NULL)
|
||||||
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)
|
if (meta->func_id == BPF_FUNC_kptr_xchg && type & MEM_ALLOC)
|
||||||
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");
|
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)
|
static bool is_kfunc_arg_constant(const struct btf *btf, const struct btf_param *arg)
|
||||||
{
|
{
|
||||||
return __kfunc_param_match_suffix(btf, arg, "__k");
|
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;
|
break;
|
||||||
case KF_ARG_PTR_TO_MEM_SIZE:
|
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];
|
struct bpf_reg_state *size_reg = ®s[regno + 1];
|
||||||
const struct btf_param *size_arg = &args[i + 1];
|
const struct btf_param *size_arg = &args[i + 1];
|
||||||
|
|
||||||
ret = check_kfunc_mem_size_reg(env, size_reg, regno + 1);
|
if (!register_is_null(buff_reg) || !is_kfunc_arg_optional(meta->btf, buff_arg)) {
|
||||||
if (ret < 0) {
|
ret = check_kfunc_mem_size_reg(env, size_reg, regno + 1);
|
||||||
verbose(env, "arg#%d arg#%d memory, len pair leads to invalid memory access\n", i, i + 1);
|
if (ret < 0) {
|
||||||
return ret;
|
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)) {
|
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_null", SETUP_SYSCALL_SLEEP},
|
||||||
{"test_dynptr_is_rdonly", SETUP_SKB_PROG},
|
{"test_dynptr_is_rdonly", SETUP_SKB_PROG},
|
||||||
{"test_dynptr_clone", 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)
|
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;
|
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;
|
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