mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-28 21:33:52 +00:00
Merge branch 'bpf: __bpf_dynptr_data* and __str annotation'
Song Liu says: ==================== This set contains the first 3 patches of set [1]. Currently, [1] is waiting for [3] to be merged to bpf-next tree. So send these 3 patches first to unblock other works, such as [2]. This set is verified with new version of [1] in CI run [4]. Changes since v12 of [1]: 1. Reuse bpf_dynptr_slice() in __bpf_dynptr_data(). (Andrii) 2. Add Acked-by from Vadim Fedorenko. [1] https://lore.kernel.org/bpf/20231104001313.3538201-1-song@kernel.org/ [2] https://lore.kernel.org/bpf/20231031134900.1432945-1-vadfed@meta.com/ [3] https://lore.kernel.org/bpf/20231031215625.2343848-1-davemarchevsky@fb.com/ [4] https://github.com/kernel-patches/bpf/actions/runs/6779945063/job/18427926114 ==================== Signed-off-by: Andrii Nakryiko <andrii@kernel.org> Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
commit
b0d1c72946
5 changed files with 121 additions and 40 deletions
|
@ -135,6 +135,30 @@ Either way, the returned buffer is either NULL, or of size buffer_szk. Without t
|
|||
annotation, the verifier will reject the program if a null pointer is passed in with
|
||||
a nonzero size.
|
||||
|
||||
2.2.5 __str Annotation
|
||||
----------------------------
|
||||
This annotation is used to indicate that the argument is a constant string.
|
||||
|
||||
An example is given below::
|
||||
|
||||
__bpf_kfunc bpf_get_file_xattr(..., const char *name__str, ...)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
In this case, ``bpf_get_file_xattr()`` can be called as::
|
||||
|
||||
bpf_get_file_xattr(..., "xattr_name", ...);
|
||||
|
||||
Or::
|
||||
|
||||
const char name[] = "xattr_name"; /* This need to be global */
|
||||
int BPF_PROG(...)
|
||||
{
|
||||
...
|
||||
bpf_get_file_xattr(..., name, ...);
|
||||
...
|
||||
}
|
||||
|
||||
.. _BPF_kfunc_nodef:
|
||||
|
||||
|
|
|
@ -1222,6 +1222,8 @@ enum bpf_dynptr_type {
|
|||
|
||||
int bpf_dynptr_check_size(u32 size);
|
||||
u32 __bpf_dynptr_size(const struct bpf_dynptr_kern *ptr);
|
||||
const void *__bpf_dynptr_data(const struct bpf_dynptr_kern *ptr, u32 len);
|
||||
void *__bpf_dynptr_data_rw(const struct bpf_dynptr_kern *ptr, u32 len);
|
||||
|
||||
#ifdef CONFIG_BPF_JIT
|
||||
int bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_trampoline *tr);
|
||||
|
|
|
@ -2618,3 +2618,22 @@ static int __init kfunc_init(void)
|
|||
}
|
||||
|
||||
late_initcall(kfunc_init);
|
||||
|
||||
/* Get a pointer to dynptr data up to len bytes for read only access. If
|
||||
* the dynptr doesn't have continuous data up to len bytes, return NULL.
|
||||
*/
|
||||
const void *__bpf_dynptr_data(const struct bpf_dynptr_kern *ptr, u32 len)
|
||||
{
|
||||
return bpf_dynptr_slice(ptr, 0, NULL, len);
|
||||
}
|
||||
|
||||
/* Get a pointer to dynptr data up to len bytes for read write access. If
|
||||
* the dynptr doesn't have continuous data up to len bytes, or the dynptr
|
||||
* is read only, return NULL.
|
||||
*/
|
||||
void *__bpf_dynptr_data_rw(const struct bpf_dynptr_kern *ptr, u32 len)
|
||||
{
|
||||
if (__bpf_dynptr_is_rdonly(ptr))
|
||||
return NULL;
|
||||
return (void *)__bpf_dynptr_data(ptr, len);
|
||||
}
|
||||
|
|
|
@ -8725,6 +8725,54 @@ static enum bpf_dynptr_type dynptr_get_type(struct bpf_verifier_env *env,
|
|||
return state->stack[spi].spilled_ptr.dynptr.type;
|
||||
}
|
||||
|
||||
static int check_reg_const_str(struct bpf_verifier_env *env,
|
||||
struct bpf_reg_state *reg, u32 regno)
|
||||
{
|
||||
struct bpf_map *map = reg->map_ptr;
|
||||
int err;
|
||||
int map_off;
|
||||
u64 map_addr;
|
||||
char *str_ptr;
|
||||
|
||||
if (reg->type != PTR_TO_MAP_VALUE)
|
||||
return -EINVAL;
|
||||
|
||||
if (!bpf_map_is_rdonly(map)) {
|
||||
verbose(env, "R%d does not point to a readonly map'\n", regno);
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
if (!tnum_is_const(reg->var_off)) {
|
||||
verbose(env, "R%d is not a constant address'\n", regno);
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
if (!map->ops->map_direct_value_addr) {
|
||||
verbose(env, "no direct value access support for this map type\n");
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
err = check_map_access(env, regno, reg->off,
|
||||
map->value_size - reg->off, false,
|
||||
ACCESS_HELPER);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
map_off = reg->off + reg->var_off.value;
|
||||
err = map->ops->map_direct_value_addr(map, &map_addr, map_off);
|
||||
if (err) {
|
||||
verbose(env, "direct value access on string failed\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
str_ptr = (char *)(long)(map_addr);
|
||||
if (!strnchr(str_ptr + map_off, map->value_size - map_off, 0)) {
|
||||
verbose(env, "string is not zero-terminated\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
|
||||
struct bpf_call_arg_meta *meta,
|
||||
const struct bpf_func_proto *fn,
|
||||
|
@ -8969,44 +9017,9 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
|
|||
}
|
||||
case ARG_PTR_TO_CONST_STR:
|
||||
{
|
||||
struct bpf_map *map = reg->map_ptr;
|
||||
int map_off;
|
||||
u64 map_addr;
|
||||
char *str_ptr;
|
||||
|
||||
if (!bpf_map_is_rdonly(map)) {
|
||||
verbose(env, "R%d does not point to a readonly map'\n", regno);
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
if (!tnum_is_const(reg->var_off)) {
|
||||
verbose(env, "R%d is not a constant address'\n", regno);
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
if (!map->ops->map_direct_value_addr) {
|
||||
verbose(env, "no direct value access support for this map type\n");
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
err = check_map_access(env, regno, reg->off,
|
||||
map->value_size - reg->off, false,
|
||||
ACCESS_HELPER);
|
||||
err = check_reg_const_str(env, reg, regno);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
map_off = reg->off + reg->var_off.value;
|
||||
err = map->ops->map_direct_value_addr(map, &map_addr, map_off);
|
||||
if (err) {
|
||||
verbose(env, "direct value access on string failed\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
str_ptr = (char *)(long)(map_addr);
|
||||
if (!strnchr(str_ptr + map_off, map->value_size - map_off, 0)) {
|
||||
verbose(env, "string is not zero-terminated\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ARG_PTR_TO_KPTR:
|
||||
|
@ -10797,6 +10810,11 @@ static bool is_kfunc_arg_nullable(const struct btf *btf, const struct btf_param
|
|||
return __kfunc_param_match_suffix(btf, arg, "__nullable");
|
||||
}
|
||||
|
||||
static bool is_kfunc_arg_const_str(const struct btf *btf, const struct btf_param *arg)
|
||||
{
|
||||
return __kfunc_param_match_suffix(btf, arg, "__str");
|
||||
}
|
||||
|
||||
static bool is_kfunc_arg_scalar_with_name(const struct btf *btf,
|
||||
const struct btf_param *arg,
|
||||
const char *name)
|
||||
|
@ -10940,6 +10958,7 @@ enum kfunc_ptr_arg_type {
|
|||
KF_ARG_PTR_TO_RB_ROOT,
|
||||
KF_ARG_PTR_TO_RB_NODE,
|
||||
KF_ARG_PTR_TO_NULL,
|
||||
KF_ARG_PTR_TO_CONST_STR,
|
||||
};
|
||||
|
||||
enum special_kfunc_type {
|
||||
|
@ -11090,6 +11109,9 @@ get_kfunc_ptr_arg_type(struct bpf_verifier_env *env,
|
|||
if (is_kfunc_arg_rbtree_node(meta->btf, &args[argno]))
|
||||
return KF_ARG_PTR_TO_RB_NODE;
|
||||
|
||||
if (is_kfunc_arg_const_str(meta->btf, &args[argno]))
|
||||
return KF_ARG_PTR_TO_CONST_STR;
|
||||
|
||||
if ((base_type(reg->type) == PTR_TO_BTF_ID || reg2btf_ids[base_type(reg->type)])) {
|
||||
if (!btf_type_is_struct(ref_t)) {
|
||||
verbose(env, "kernel function %s args#%d pointer type %s %s is not supported\n",
|
||||
|
@ -11721,6 +11743,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
|
|||
case KF_ARG_PTR_TO_MEM_SIZE:
|
||||
case KF_ARG_PTR_TO_CALLBACK:
|
||||
case KF_ARG_PTR_TO_REFCOUNTED_KPTR:
|
||||
case KF_ARG_PTR_TO_CONST_STR:
|
||||
/* Trusted by default */
|
||||
break;
|
||||
default:
|
||||
|
@ -11992,6 +12015,15 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
|
|||
meta->arg_btf = reg->btf;
|
||||
meta->arg_btf_id = reg->btf_id;
|
||||
break;
|
||||
case KF_ARG_PTR_TO_CONST_STR:
|
||||
if (reg->type != PTR_TO_MAP_VALUE) {
|
||||
verbose(env, "arg#%d doesn't point to a const string\n", i);
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = check_reg_const_str(env, reg, regno);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1376,6 +1376,8 @@ __bpf_kfunc int bpf_verify_pkcs7_signature(struct bpf_dynptr_kern *data_ptr,
|
|||
struct bpf_dynptr_kern *sig_ptr,
|
||||
struct bpf_key *trusted_keyring)
|
||||
{
|
||||
const void *data, *sig;
|
||||
u32 data_len, sig_len;
|
||||
int ret;
|
||||
|
||||
if (trusted_keyring->has_ref) {
|
||||
|
@ -1392,10 +1394,12 @@ __bpf_kfunc int bpf_verify_pkcs7_signature(struct bpf_dynptr_kern *data_ptr,
|
|||
return ret;
|
||||
}
|
||||
|
||||
return verify_pkcs7_signature(data_ptr->data,
|
||||
__bpf_dynptr_size(data_ptr),
|
||||
sig_ptr->data,
|
||||
__bpf_dynptr_size(sig_ptr),
|
||||
data_len = __bpf_dynptr_size(data_ptr);
|
||||
data = __bpf_dynptr_data(data_ptr, data_len);
|
||||
sig_len = __bpf_dynptr_size(sig_ptr);
|
||||
sig = __bpf_dynptr_data(sig_ptr, sig_len);
|
||||
|
||||
return verify_pkcs7_signature(data, data_len, sig, sig_len,
|
||||
trusted_keyring->key,
|
||||
VERIFYING_UNSPECIFIED_SIGNATURE, NULL,
|
||||
NULL);
|
||||
|
|
Loading…
Reference in a new issue