bpf: Add bpf_dynptr_from_mem for local dynptrs
This patch adds a new api bpf_dynptr_from_mem: long bpf_dynptr_from_mem(void *data, u32 size, u64 flags, struct bpf_dynptr *ptr); which initializes a dynptr to point to a bpf program's local memory. For now only local memory that is of reg type PTR_TO_MAP_VALUE is supported. Signed-off-by: Joanne Koong <joannelkoong@gmail.com> Signed-off-by: Andrii Nakryiko <andrii@kernel.org> Link: https://lore.kernel.org/bpf/20220523210712.3641569-3-joannelkoong@gmail.com
This commit is contained in:
parent
97e03f5210
commit
263ae152e9
|
@ -5178,6 +5178,17 @@ union bpf_attr {
|
|||
* Dynamically cast a *sk* pointer to a *mptcp_sock* pointer.
|
||||
* Return
|
||||
* *sk* if casting is valid, or **NULL** otherwise.
|
||||
*
|
||||
* long bpf_dynptr_from_mem(void *data, u32 size, u64 flags, struct bpf_dynptr *ptr)
|
||||
* Description
|
||||
* Get a dynptr to local memory *data*.
|
||||
*
|
||||
* *data* must be a ptr to a map value.
|
||||
* The maximum *size* supported is DYNPTR_MAX_SIZE.
|
||||
* *flags* is currently unused.
|
||||
* Return
|
||||
* 0 on success, -E2BIG if the size exceeds DYNPTR_MAX_SIZE,
|
||||
* -EINVAL if flags is not 0.
|
||||
*/
|
||||
#define __BPF_FUNC_MAPPER(FN) \
|
||||
FN(unspec), \
|
||||
|
@ -5377,6 +5388,7 @@ union bpf_attr {
|
|||
FN(kptr_xchg), \
|
||||
FN(map_lookup_percpu_elem), \
|
||||
FN(skc_to_mptcp_sock), \
|
||||
FN(dynptr_from_mem), \
|
||||
/* */
|
||||
|
||||
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
|
||||
|
|
|
@ -1412,6 +1412,69 @@ const struct bpf_func_proto bpf_kptr_xchg_proto = {
|
|||
.arg2_btf_id = BPF_PTR_POISON,
|
||||
};
|
||||
|
||||
/* Since the upper 8 bits of dynptr->size is reserved, the
|
||||
* maximum supported size is 2^24 - 1.
|
||||
*/
|
||||
#define DYNPTR_MAX_SIZE ((1UL << 24) - 1)
|
||||
#define DYNPTR_TYPE_SHIFT 28
|
||||
|
||||
static void bpf_dynptr_set_type(struct bpf_dynptr_kern *ptr, enum bpf_dynptr_type type)
|
||||
{
|
||||
ptr->size |= type << DYNPTR_TYPE_SHIFT;
|
||||
}
|
||||
|
||||
static int bpf_dynptr_check_size(u32 size)
|
||||
{
|
||||
return size > DYNPTR_MAX_SIZE ? -E2BIG : 0;
|
||||
}
|
||||
|
||||
static void bpf_dynptr_init(struct bpf_dynptr_kern *ptr, void *data,
|
||||
enum bpf_dynptr_type type, u32 offset, u32 size)
|
||||
{
|
||||
ptr->data = data;
|
||||
ptr->offset = offset;
|
||||
ptr->size = size;
|
||||
bpf_dynptr_set_type(ptr, type);
|
||||
}
|
||||
|
||||
static void bpf_dynptr_set_null(struct bpf_dynptr_kern *ptr)
|
||||
{
|
||||
memset(ptr, 0, sizeof(*ptr));
|
||||
}
|
||||
|
||||
BPF_CALL_4(bpf_dynptr_from_mem, void *, data, u32, size, u64, flags, struct bpf_dynptr_kern *, ptr)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = bpf_dynptr_check_size(size);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* flags is currently unsupported */
|
||||
if (flags) {
|
||||
err = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
bpf_dynptr_init(ptr, data, BPF_DYNPTR_TYPE_LOCAL, 0, size);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
bpf_dynptr_set_null(ptr);
|
||||
return err;
|
||||
}
|
||||
|
||||
const struct bpf_func_proto bpf_dynptr_from_mem_proto = {
|
||||
.func = bpf_dynptr_from_mem,
|
||||
.gpl_only = false,
|
||||
.ret_type = RET_INTEGER,
|
||||
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
|
||||
.arg2_type = ARG_CONST_SIZE_OR_ZERO,
|
||||
.arg3_type = ARG_ANYTHING,
|
||||
.arg4_type = ARG_PTR_TO_DYNPTR | DYNPTR_TYPE_LOCAL | MEM_UNINIT,
|
||||
};
|
||||
|
||||
const struct bpf_func_proto bpf_get_current_task_proto __weak;
|
||||
const struct bpf_func_proto bpf_get_current_task_btf_proto __weak;
|
||||
const struct bpf_func_proto bpf_probe_read_user_proto __weak;
|
||||
|
@ -1466,6 +1529,8 @@ bpf_base_func_proto(enum bpf_func_id func_id)
|
|||
return &bpf_loop_proto;
|
||||
case BPF_FUNC_strncmp:
|
||||
return &bpf_strncmp_proto;
|
||||
case BPF_FUNC_dynptr_from_mem:
|
||||
return &bpf_dynptr_from_mem_proto;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -7204,6 +7204,12 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
|
|||
err = __check_func_call(env, insn, insn_idx_p, meta.subprogno,
|
||||
set_loop_callback_state);
|
||||
break;
|
||||
case BPF_FUNC_dynptr_from_mem:
|
||||
if (regs[BPF_REG_1].type != PTR_TO_MAP_VALUE) {
|
||||
verbose(env, "Unsupported reg type %s for bpf_dynptr_from_mem data\n",
|
||||
reg_type_str(env, regs[BPF_REG_1].type));
|
||||
return -EACCES;
|
||||
}
|
||||
}
|
||||
|
||||
if (err)
|
||||
|
|
|
@ -5178,6 +5178,17 @@ union bpf_attr {
|
|||
* Dynamically cast a *sk* pointer to a *mptcp_sock* pointer.
|
||||
* Return
|
||||
* *sk* if casting is valid, or **NULL** otherwise.
|
||||
*
|
||||
* long bpf_dynptr_from_mem(void *data, u32 size, u64 flags, struct bpf_dynptr *ptr)
|
||||
* Description
|
||||
* Get a dynptr to local memory *data*.
|
||||
*
|
||||
* *data* must be a ptr to a map value.
|
||||
* The maximum *size* supported is DYNPTR_MAX_SIZE.
|
||||
* *flags* is currently unused.
|
||||
* Return
|
||||
* 0 on success, -E2BIG if the size exceeds DYNPTR_MAX_SIZE,
|
||||
* -EINVAL if flags is not 0.
|
||||
*/
|
||||
#define __BPF_FUNC_MAPPER(FN) \
|
||||
FN(unspec), \
|
||||
|
@ -5377,6 +5388,7 @@ union bpf_attr {
|
|||
FN(kptr_xchg), \
|
||||
FN(map_lookup_percpu_elem), \
|
||||
FN(skc_to_mptcp_sock), \
|
||||
FN(dynptr_from_mem), \
|
||||
/* */
|
||||
|
||||
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
|
||||
|
|
Loading…
Reference in New Issue