Merge branch 'bpf-extend-bpf-syscall-with-common-attributes-support'

Leon Hwang says:

====================
bpf: Extend BPF syscall with common attributes support

This patch series builds upon the discussion in
"[PATCH bpf-next v4 0/4] bpf: Improve error reporting for freplace attachment failure" [1].

This patch series introduces support for *common attributes* in the BPF
syscall, providing a unified mechanism for passing shared metadata across
all BPF commands, initially used by BPF_PROG_LOAD, BPF_BTF_LOAD, and
BPF_MAP_CREATE.

The initial set of common attributes includes:

1. 'log_buf': User-provided buffer for storing log output.
2. 'log_size': Size of the provided log buffer.
3. 'log_level': Verbosity level for logging.
4. 'log_true_size': Actual log size reported by kernel.

With this extension, the BPF syscall will be able to return meaningful
error messages (e.g., map creation failures), improving debuggability
and user experience.

Links:
[1] https://lore.kernel.org/bpf/20250224153352.64689-1-leon.hwang@linux.dev/

Changes:
v13 -> v14:
* Replace __u64 with __aligned_u64 in struct bpf_common_attr in patch #1
  (per bot+bpf-ci).
* Add a single line comment for preserving original error in patch #6
  (per bot+bpf-ci and Alexei).
* Drop unused label in patch #6 (per bot+bpf-ci).
* v13: https://lore.kernel.org/bpf/20260511152817.89191-1-leon.hwang@linux.dev/

v12 -> v13:
* Rebase on bpf-next tree to resolve code conflict.
* Report log_true_size on success path in patch #6.
* Check size instead of common->log_buf in patch #6 (per bot+bpf-ci).
* v12: https://lore.kernel.org/bpf/20260420141804.27179-1-leon.hwang@linux.dev/

v11 -> v12:
* Drop "log_" prefix in struct bpf_log_attr in patch #3.
* Drop "log_" prefix in struct bpf_log_opts in patch #7.
* Copy log_true_size using copy_to_bpfptr_offset() in patch #3 (per Alexei).
* v11: https://lore.kernel.org/bpf/20260216150445.68278-1-leon.hwang@linux.dev/

v10 -> v11:
* Collect Acked-by from Andrii, thanks.
* Validate whether log_buf, log_size, and log_level are valid by reusing
  bpf_verifier_log_attr_valid() in patch #4 (per Andrii).
* v10: https://lore.kernel.org/bpf/20260211151115.78013-1-leon.hwang@linux.dev/

v9 -> v10:
* Collect Acked-by from Andrii, thanks.
* Address comments from Andrii:
  * Drop log NULL check in bpf_log_attr_finalize().
  * Return -EFAULT early in bpf_log_attr_finalize().
  * Validate whether log_buf, log_size, and log_level are set.
  * Keep log_buf, log_size, log_level, and user-pointer log_true_size in struct
    bpf_log_attr.
  * Make prog_load and btf_load work with the new struct bpf_log_attr.
  * Add comment to log_true_size of struct bpf_log_opts in libbpf.
* Address comment from Alexei:
  * Avoid using BPF_LOG_FIXED as log_level in tests.
* v9: https://lore.kernel.org/bpf/20260202144046.30651-1-leon.hwang@linux.dev/

v8 -> v9:
* Rework reporting 'log_true_size' for prog_load, btf_load, and map_create to
  simplify struct bpf_log_attr (per Alexei).
* v8: https://lore.kernel.org/bpf/20260126151409.52072-1-leon.hwang@linux.dev/

v7 -> v8:
* Return 0 when fd < 0 and errno != EFAULT in probe_sys_bpf_ext(), then simplify
  probe_bpf_syscall_common_attrs() (per Alexei and Andrii).
* v7: https://lore.kernel.org/bpf/20260123032445.125259-1-leon.hwang@linux.dev/

v6 -> v7:
* Return -errno when fd < 0 and errno != EFAULT in probe_sys_bpf_ext().
* Convert return value of probe_sys_bpf_ext() to bool in
  probe_bpf_syscall_common_attrs().
* Address comments from Andrii:
  * Drop the comment, and handle fd >= 0 case explicitly in
    probe_sys_bpf_ext().
  * Return an error when fd >= 0 in probe_sys_bpf_ext().
* v6: https://lore.kernel.org/bpf/20260120152424.40766-1-leon.hwang@linux.dev/

v5 -> v6:
* Address comments from Andrii:
  * Update some variables' name.
  * Drop unnecessary 'close(fd)' in libbpf.
  * Rename FEAT_EXTENDED_SYSCALL to FEAT_BPF_SYSCALL_COMMON_ATTRS with
    updated description in libbpf.
  * Use EINVAL instead of EUSERS, as EUSERS is not used in bpf yet.
  * Rename struct bpf_syscall_common_attr_opts to bpf_log_opts in libbpf.
  * Add 'OPTS_SET(log_opts, log_true_size, 0);' in libbpf's 'bpf_map_create()'.
* v5: https://lore.kernel.org/bpf/20260112145616.44195-1-leon.hwang@linux.dev/

v4 -> v5:
* Rework reporting 'log_true_size' for prog_load, btf_load, and map_create
  (per Alexei).
* v4: https://lore.kernel.org/bpf/20260106172018.57757-1-leon.hwang@linux.dev/

RFC v3 -> v4:
* Drop RFC.
* Address comments from Andrii:
  * Add parentheses in 'sys_bpf_ext()'.
  * Avoid creating new fd in 'probe_sys_bpf_ext()'.
  * Add a new struct to wrap log fields in libbpf.
* Address comments from Alexei:
  * Do not skip writing to user space when log_true_size is zero.
  * Do not use 'bool' arguments.
  * Drop the adding WARN_ON_ONCE()'s.
* v3: https://lore.kernel.org/bpf/20251002154841.99348-1-leon.hwang@linux.dev/

RFC v2 -> RFC v3:
* Rename probe_sys_bpf_extended to probe_sys_bpf_ext.
* Refactor reporting 'log_true_size' for prog_load.
* Refactor reporting 'btf_log_true_size' for btf_load.
* Add warnings for internal bugs in map_create.
* Check log_true_size in test cases.
* Address comment from Alexei:
  * Change kvzalloc/kvfree to kzalloc/kfree.
* Address comments from Andrii:
  * Move BPF_COMMON_ATTRS to 'enum bpf_cmd' alongside brief comment.
  * Add bpf_check_uarg_tail_zero() for extra checks.
  * Rename sys_bpf_extended to sys_bpf_ext.
  * Rename sys_bpf_fd_extended to sys_bpf_ext_fd.
  * Probe the new feature using NULL and -EFAULT.
  * Move probe_sys_bpf_ext to libbpf_internal.h and drop LIBBPF_API.
  * Return -EUSERS when log attrs are conflict between bpf_attr and
    bpf_common_attr.
  * Avoid touching bpf_vlog_init().
  * Update the reason messages in map_create.
  * Finalize the log using __cleanup().
  * Report log size to users.
  * Change type of log_buf from '__u64' to 'const char *' and cast type
    using ptr_to_u64() in bpf_map_create().
  * Do not return -EOPNOTSUPP when kernel doesn't support this feature
    in bpf_map_create().
  * Add log_level support for map creation for consistency.
* Address comment from Eduard:
  * Use common_attrs->log_level instead of BPF_LOG_FIXED.
* v2: https://lore.kernel.org/bpf/20250911163328.93490-1-leon.hwang@linux.dev/

RFC v1 -> RFC v2:
* Fix build error reported by test bot.
* Address comments from Alexei:
  * Drop new uapi for freplace.
  * Add common attributes support for prog_load and btf_load.
  * Add common attributes support for map_create.
* v1: https://lore.kernel.org/bpf/20250728142346.95681-1-leon.hwang@linux.dev/
====================

Link: https://patch.msgid.link/20260512153157.28382-1-leon.hwang@linux.dev
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
Alexei Starovoitov 2026-05-12 12:44:47 -07:00
commit 6318f11d53
15 changed files with 473 additions and 67 deletions

View File

@ -2919,7 +2919,9 @@ int bpf_check_uarg_tail_zero(bpfptr_t uaddr, size_t expected_size,
size_t actual_size);
/* verify correctness of eBPF program */
int bpf_check(struct bpf_prog **fp, union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size);
struct bpf_log_attr;
int bpf_check(struct bpf_prog **fp, union bpf_attr *attr, bpfptr_t uattr,
struct bpf_log_attr *attr_log);
#ifndef CONFIG_BPF_JIT_ALWAYS_ON
void bpf_patch_call_args(struct bpf_insn *insn, u32 stack_depth);

View File

@ -755,6 +755,22 @@ static inline bool bpf_verifier_log_needed(const struct bpf_verifier_log *log)
return log && log->level;
}
struct bpf_log_attr {
char __user *ubuf;
u32 size;
u32 level;
u32 offsetof_true_size;
bpfptr_t uattr;
};
int bpf_log_attr_init(struct bpf_log_attr *log, u64 log_buf, u32 log_size, u32 log_level,
u32 offsetof_log_true_size, bpfptr_t uattr, struct bpf_common_attr *common,
bpfptr_t uattr_common, u32 size_common);
struct bpf_verifier_log *bpf_log_attr_create_vlog(struct bpf_log_attr *attr_log,
struct bpf_common_attr *common, bpfptr_t uattr,
u32 size);
int bpf_log_attr_finalize(struct bpf_log_attr *attr, struct bpf_verifier_log *log);
#define BPF_MAX_SUBPROGS 256
struct bpf_subprog_arg_info {

View File

@ -145,7 +145,8 @@ const char *btf_get_name(const struct btf *btf);
void btf_get(struct btf *btf);
void btf_put(struct btf *btf);
const struct btf_header *btf_header(const struct btf *btf);
int btf_new_fd(const union bpf_attr *attr, bpfptr_t uattr, u32 uattr_sz);
struct bpf_log_attr;
int btf_new_fd(const union bpf_attr *attr, bpfptr_t uattr, struct bpf_log_attr *attr_log);
struct btf *btf_get_by_fd(int fd);
int btf_get_info_by_fd(const struct btf *btf,
const union bpf_attr *attr,

View File

@ -936,7 +936,8 @@ asmlinkage long sys_seccomp(unsigned int op, unsigned int flags,
asmlinkage long sys_getrandom(char __user *buf, size_t count,
unsigned int flags);
asmlinkage long sys_memfd_create(const char __user *uname_ptr, unsigned int flags);
asmlinkage long sys_bpf(int cmd, union bpf_attr __user *attr, unsigned int size);
asmlinkage long sys_bpf(int cmd, union bpf_attr __user *attr, unsigned int size,
struct bpf_common_attr __user *attr_common, unsigned int size_common);
asmlinkage long sys_execveat(int dfd, const char __user *filename,
const char __user *const __user *argv,
const char __user *const __user *envp, int flags);

View File

@ -994,6 +994,7 @@ enum bpf_cmd {
BPF_PROG_STREAM_READ_BY_FD,
BPF_PROG_ASSOC_STRUCT_OPS,
__MAX_BPF_CMD,
BPF_COMMON_ATTRS = 1 << 16, /* Indicate carrying syscall common attrs. */
};
enum bpf_map_type {
@ -1500,6 +1501,13 @@ struct bpf_stack_build_id {
};
};
struct bpf_common_attr {
__aligned_u64 log_buf;
__u32 log_size;
__u32 log_level;
__u32 log_true_size;
};
#define BPF_OBJ_NAME_LEN 16U
enum {

View File

@ -5907,25 +5907,10 @@ static int btf_check_type_tags(struct btf_verifier_env *env,
return 0;
}
static int finalize_log(struct bpf_verifier_log *log, bpfptr_t uattr, u32 uattr_size)
{
u32 log_true_size;
int err;
err = bpf_vlog_finalize(log, &log_true_size);
if (uattr_size >= offsetofend(union bpf_attr, btf_log_true_size) &&
copy_to_bpfptr_offset(uattr, offsetof(union bpf_attr, btf_log_true_size),
&log_true_size, sizeof(log_true_size)))
err = -EFAULT;
return err;
}
static struct btf *btf_parse(const union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
static struct btf *btf_parse(const union bpf_attr *attr, bpfptr_t uattr,
struct bpf_log_attr *attr_log)
{
bpfptr_t btf_data = make_bpfptr(attr->btf, uattr.is_kernel);
char __user *log_ubuf = u64_to_user_ptr(attr->btf_log_buf);
struct btf_struct_metas *struct_meta_tab;
struct btf_verifier_env *env = NULL;
struct btf *btf = NULL;
@ -5942,8 +5927,7 @@ static struct btf *btf_parse(const union bpf_attr *attr, bpfptr_t uattr, u32 uat
/* user could have requested verbose verifier output
* and supplied buffer to store the verification trace
*/
err = bpf_vlog_init(&env->log, attr->btf_log_level,
log_ubuf, attr->btf_log_size);
err = bpf_vlog_init(&env->log, attr_log->level, attr_log->ubuf, attr_log->size);
if (err)
goto errout_free;
@ -6008,7 +5992,7 @@ static struct btf *btf_parse(const union bpf_attr *attr, bpfptr_t uattr, u32 uat
}
}
err = finalize_log(&env->log, uattr, uattr_size);
err = bpf_log_attr_finalize(attr_log, &env->log);
if (err)
goto errout_free;
@ -6020,7 +6004,7 @@ static struct btf *btf_parse(const union bpf_attr *attr, bpfptr_t uattr, u32 uat
btf_free_struct_meta_tab(btf);
errout:
/* overwrite err with -ENOSPC or -EFAULT */
ret = finalize_log(&env->log, uattr, uattr_size);
ret = bpf_log_attr_finalize(attr_log, &env->log);
if (ret)
err = ret;
errout_free:
@ -8189,12 +8173,12 @@ static int __btf_new_fd(struct btf *btf)
return anon_inode_getfd("btf", &btf_fops, btf, O_RDONLY | O_CLOEXEC);
}
int btf_new_fd(const union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
int btf_new_fd(const union bpf_attr *attr, bpfptr_t uattr, struct bpf_log_attr *attr_log)
{
struct btf *btf;
int ret;
btf = btf_parse(attr, uattr, uattr_size);
btf = btf_parse(attr, uattr, attr_log);
if (IS_ERR(btf))
return PTR_ERR(btf);

View File

@ -13,17 +13,17 @@
#define verbose(env, fmt, args...) bpf_verifier_log_write(env, fmt, ##args)
static bool bpf_verifier_log_attr_valid(const struct bpf_verifier_log *log)
static bool bpf_verifier_log_attr_valid(u32 log_level, char __user *log_buf, u32 log_size)
{
/* ubuf and len_total should both be specified (or not) together */
if (!!log->ubuf != !!log->len_total)
if (!!log_buf != !!log_size)
return false;
/* log buf without log_level is meaningless */
if (log->ubuf && log->level == 0)
if (log_buf && log_level == 0)
return false;
if (log->level & ~BPF_LOG_MASK)
if (log_level & ~BPF_LOG_MASK)
return false;
if (log->len_total > UINT_MAX >> 2)
if (log_size > UINT_MAX >> 2)
return false;
return true;
}
@ -36,7 +36,7 @@ int bpf_vlog_init(struct bpf_verifier_log *log, u32 log_level,
log->len_total = log_size;
/* log attributes have to be sane */
if (!bpf_verifier_log_attr_valid(log))
if (!bpf_verifier_log_attr_valid(log_level, log_buf, log_size))
return -EINVAL;
return 0;
@ -825,3 +825,81 @@ void print_insn_state(struct bpf_verifier_env *env, const struct bpf_verifier_st
}
print_verifier_state(env, vstate, frameno, false);
}
int bpf_log_attr_init(struct bpf_log_attr *log, u64 log_buf, u32 log_size, u32 log_level,
u32 offsetof_log_true_size, bpfptr_t uattr, struct bpf_common_attr *common,
bpfptr_t uattr_common, u32 size_common)
{
char __user *ubuf_common = u64_to_user_ptr(common->log_buf);
char __user *ubuf = u64_to_user_ptr(log_buf);
if (!bpf_verifier_log_attr_valid(common->log_level, ubuf_common, common->log_size) ||
!bpf_verifier_log_attr_valid(log_level, ubuf, log_size))
return -EINVAL;
if (ubuf && ubuf_common && (ubuf != ubuf_common || log_size != common->log_size ||
log_level != common->log_level))
return -EINVAL;
memset(log, 0, sizeof(*log));
log->ubuf = ubuf;
log->size = log_size;
log->level = log_level;
log->offsetof_true_size = offsetof_log_true_size;
log->uattr = uattr;
if (!ubuf && ubuf_common) {
log->ubuf = ubuf_common;
log->size = common->log_size;
log->level = common->log_level;
log->uattr = uattr_common;
log->offsetof_true_size = 0;
if (size_common >= offsetofend(struct bpf_common_attr, log_true_size))
log->offsetof_true_size = offsetof(struct bpf_common_attr, log_true_size);
}
return 0;
}
struct bpf_verifier_log *bpf_log_attr_create_vlog(struct bpf_log_attr *attr_log,
struct bpf_common_attr *common, bpfptr_t uattr,
u32 size)
{
struct bpf_verifier_log *log;
int err;
memset(attr_log, 0, sizeof(*attr_log));
attr_log->uattr = uattr;
if (size >= offsetofend(struct bpf_common_attr, log_true_size))
attr_log->offsetof_true_size = offsetof(struct bpf_common_attr, log_true_size);
if (!size)
return NULL;
log = kzalloc_obj(*log, GFP_KERNEL);
if (!log)
return ERR_PTR(-ENOMEM);
err = bpf_vlog_init(log, common->log_level, u64_to_user_ptr(common->log_buf),
common->log_size);
if (err) {
kfree(log);
return ERR_PTR(err);
}
return log;
}
int bpf_log_attr_finalize(struct bpf_log_attr *attr, struct bpf_verifier_log *log)
{
u32 log_true_size;
int err;
err = bpf_vlog_finalize(log, &log_true_size);
if (attr->offsetof_true_size &&
copy_to_bpfptr_offset(attr->uattr, attr->offsetof_true_size, &log_true_size,
sizeof(log_true_size)))
return -EFAULT;
return err;
}

View File

@ -1359,7 +1359,7 @@ static int map_check_btf(struct bpf_map *map, struct bpf_token *token,
#define BPF_MAP_CREATE_LAST_FIELD excl_prog_hash_size
/* called via syscall */
static int map_create(union bpf_attr *attr, bpfptr_t uattr)
static int __map_create(union bpf_attr *attr, bpfptr_t uattr, struct bpf_verifier_log *log)
{
const struct bpf_map_ops *ops;
struct bpf_token *token = NULL;
@ -1371,8 +1371,10 @@ static int map_create(union bpf_attr *attr, bpfptr_t uattr)
int err;
err = CHECK_ATTR(BPF_MAP_CREATE);
if (err)
if (err) {
bpf_log(log, "Invalid attr.\n");
return -EINVAL;
}
/* check BPF_F_TOKEN_FD flag, remember if it's set, and then clear it
* to avoid per-map type checks tripping on unknown flag
@ -1381,17 +1383,25 @@ static int map_create(union bpf_attr *attr, bpfptr_t uattr)
attr->map_flags &= ~BPF_F_TOKEN_FD;
if (attr->btf_vmlinux_value_type_id) {
if (attr->map_type != BPF_MAP_TYPE_STRUCT_OPS ||
attr->btf_key_type_id || attr->btf_value_type_id)
if (attr->map_type != BPF_MAP_TYPE_STRUCT_OPS) {
bpf_log(log, "btf_vmlinux_value_type_id can only be used with struct_ops maps.\n");
return -EINVAL;
}
if (attr->btf_key_type_id || attr->btf_value_type_id) {
bpf_log(log, "btf_vmlinux_value_type_id is mutually exclusive with btf_key_type_id and btf_value_type_id.\n");
return -EINVAL;
}
} else if (attr->btf_key_type_id && !attr->btf_value_type_id) {
bpf_log(log, "Invalid btf_value_type_id.\n");
return -EINVAL;
}
if (attr->map_type != BPF_MAP_TYPE_BLOOM_FILTER &&
attr->map_type != BPF_MAP_TYPE_ARENA &&
attr->map_extra != 0)
attr->map_extra != 0) {
bpf_log(log, "Invalid map_extra.\n");
return -EINVAL;
}
f_flags = bpf_get_file_flag(attr->map_flags);
if (f_flags < 0)
@ -1399,13 +1409,17 @@ static int map_create(union bpf_attr *attr, bpfptr_t uattr)
if (numa_node != NUMA_NO_NODE &&
((unsigned int)numa_node >= nr_node_ids ||
!node_online(numa_node)))
!node_online(numa_node))) {
bpf_log(log, "Invalid numa_node.\n");
return -EINVAL;
}
/* find map type and init map: hashtable vs rbtree vs bloom vs ... */
map_type = attr->map_type;
if (map_type >= ARRAY_SIZE(bpf_map_types))
if (map_type >= ARRAY_SIZE(bpf_map_types)) {
bpf_log(log, "Invalid map_type.\n");
return -EINVAL;
}
map_type = array_index_nospec(map_type, ARRAY_SIZE(bpf_map_types));
ops = bpf_map_types[map_type];
if (!ops)
@ -1423,8 +1437,10 @@ static int map_create(union bpf_attr *attr, bpfptr_t uattr)
if (token_flag) {
token = bpf_token_get_from_fd(attr->map_token_fd);
if (IS_ERR(token))
if (IS_ERR(token)) {
bpf_log(log, "Invalid map_token_fd.\n");
return PTR_ERR(token);
}
/* if current token doesn't grant map creation permissions,
* then we can't use this token, so ignore it and rely on
@ -1507,8 +1523,10 @@ static int map_create(union bpf_attr *attr, bpfptr_t uattr)
err = bpf_obj_name_cpy(map->name, attr->map_name,
sizeof(attr->map_name));
if (err < 0)
if (err < 0) {
bpf_log(log, "Invalid map_name.\n");
goto free_map;
}
preempt_disable();
map->cookie = gen_cookie_next(&bpf_map_cookie);
@ -1531,6 +1549,7 @@ static int map_create(union bpf_attr *attr, bpfptr_t uattr)
btf = btf_get_by_fd(attr->btf_fd);
if (IS_ERR(btf)) {
bpf_log(log, "Invalid btf_fd.\n");
err = PTR_ERR(btf);
goto free_map;
}
@ -1558,6 +1577,7 @@ static int map_create(union bpf_attr *attr, bpfptr_t uattr)
bpfptr_t uprog_hash = make_bpfptr(attr->excl_prog_hash, uattr.is_kernel);
if (attr->excl_prog_hash_size != SHA256_DIGEST_SIZE) {
bpf_log(log, "Invalid excl_prog_hash_size.\n");
err = -EINVAL;
goto free_map;
}
@ -1573,6 +1593,7 @@ static int map_create(union bpf_attr *attr, bpfptr_t uattr)
goto free_map;
}
} else if (attr->excl_prog_hash_size) {
bpf_log(log, "Invalid excl_prog_hash_size.\n");
err = -EINVAL;
goto free_map;
}
@ -1611,6 +1632,31 @@ static int map_create(union bpf_attr *attr, bpfptr_t uattr)
return err;
}
static int map_create(union bpf_attr *attr, bpfptr_t uattr, struct bpf_common_attr *attr_common,
bpfptr_t uattr_common, u32 size_common)
{
struct bpf_verifier_log *log;
struct bpf_log_attr attr_log;
int err, ret;
log = bpf_log_attr_create_vlog(&attr_log, attr_common, uattr_common, size_common);
if (IS_ERR(log))
return PTR_ERR(log);
err = __map_create(attr, uattr, log);
/* preserve original error even if log finalization is successful */
ret = bpf_log_attr_finalize(&attr_log, log);
if (ret) {
if (err >= 0)
close_fd(err);
err = ret;
}
kfree(log);
return err;
}
void bpf_map_inc(struct bpf_map *map)
{
atomic64_inc(&map->refcnt);
@ -2861,7 +2907,7 @@ static int bpf_prog_mark_insn_arrays_ready(struct bpf_prog *prog)
/* last field in 'union bpf_attr' used by this command */
#define BPF_PROG_LOAD_LAST_FIELD keyring_id
static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, struct bpf_log_attr *attr_log)
{
enum bpf_prog_type type = attr->prog_type;
struct bpf_prog *prog, *dst_prog = NULL;
@ -3079,7 +3125,7 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
goto free_prog_sec;
/* run eBPF verifier */
err = bpf_check(&prog, attr, uattr, uattr_size);
err = bpf_check(&prog, attr, uattr, attr_log);
if (err < 0)
goto free_used_maps;
@ -5474,7 +5520,7 @@ static int bpf_obj_get_info_by_fd(const union bpf_attr *attr,
#define BPF_BTF_LOAD_LAST_FIELD btf_token_fd
static int bpf_btf_load(const union bpf_attr *attr, bpfptr_t uattr, __u32 uattr_size)
static int bpf_btf_load(const union bpf_attr *attr, bpfptr_t uattr, struct bpf_log_attr *attr_log)
{
struct bpf_token *token = NULL;
@ -5501,7 +5547,7 @@ static int bpf_btf_load(const union bpf_attr *attr, bpfptr_t uattr, __u32 uattr_
bpf_token_put(token);
return btf_new_fd(attr, uattr, uattr_size);
return btf_new_fd(attr, uattr, attr_log);
}
#define BPF_BTF_GET_FD_BY_ID_LAST_FIELD fd_by_id_token_fd
@ -6211,8 +6257,12 @@ static int prog_assoc_struct_ops(union bpf_attr *attr)
return ret;
}
static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size)
static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size,
bpfptr_t uattr_common, unsigned int size_common)
{
struct bpf_common_attr attr_common;
u32 offsetof_log_true_size = 0;
struct bpf_log_attr attr_log;
union bpf_attr attr;
int err;
@ -6226,13 +6276,27 @@ static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size)
if (copy_from_bpfptr(&attr, uattr, size) != 0)
return -EFAULT;
memset(&attr_common, 0, sizeof(attr_common));
if (cmd & BPF_COMMON_ATTRS) {
err = bpf_check_uarg_tail_zero(uattr_common, sizeof(attr_common), size_common);
if (err)
return err;
cmd &= ~BPF_COMMON_ATTRS;
size_common = min_t(u32, size_common, sizeof(attr_common));
if (copy_from_bpfptr(&attr_common, uattr_common, size_common) != 0)
return -EFAULT;
} else {
size_common = 0;
}
err = security_bpf(cmd, &attr, size, uattr.is_kernel);
if (err < 0)
return err;
switch (cmd) {
case BPF_MAP_CREATE:
err = map_create(&attr, uattr);
err = map_create(&attr, uattr, &attr_common, uattr_common, size_common);
break;
case BPF_MAP_LOOKUP_ELEM:
err = map_lookup_elem(&attr);
@ -6250,7 +6314,12 @@ static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size)
err = map_freeze(&attr);
break;
case BPF_PROG_LOAD:
err = bpf_prog_load(&attr, uattr, size);
if (size >= offsetofend(union bpf_attr, log_true_size))
offsetof_log_true_size = offsetof(union bpf_attr, log_true_size);
err = bpf_log_attr_init(&attr_log, attr.log_buf, attr.log_size, attr.log_level,
offsetof_log_true_size, uattr, &attr_common, uattr_common,
size_common);
err = err ?: bpf_prog_load(&attr, uattr, &attr_log);
break;
case BPF_OBJ_PIN:
err = bpf_obj_pin(&attr);
@ -6295,7 +6364,12 @@ static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size)
err = bpf_raw_tracepoint_open(&attr);
break;
case BPF_BTF_LOAD:
err = bpf_btf_load(&attr, uattr, size);
if (size >= offsetofend(union bpf_attr, btf_log_true_size))
offsetof_log_true_size = offsetof(union bpf_attr, btf_log_true_size);
err = bpf_log_attr_init(&attr_log, attr.btf_log_buf, attr.btf_log_size,
attr.btf_log_level, offsetof_log_true_size, uattr,
&attr_common, uattr_common, size_common);
err = err ?: bpf_btf_load(&attr, uattr, &attr_log);
break;
case BPF_BTF_GET_FD_BY_ID:
err = bpf_btf_get_fd_by_id(&attr);
@ -6361,9 +6435,10 @@ static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size)
return err;
}
SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size)
SYSCALL_DEFINE5(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size,
struct bpf_common_attr __user *, uattr_common, unsigned int, size_common)
{
return __sys_bpf(cmd, USER_BPFPTR(uattr), size);
return __sys_bpf(cmd, USER_BPFPTR(uattr), size, USER_BPFPTR(uattr_common), size_common);
}
static bool syscall_prog_is_valid_access(int off, int size,
@ -6393,7 +6468,7 @@ BPF_CALL_3(bpf_sys_bpf, int, cmd, union bpf_attr *, attr, u32, attr_size)
default:
return -EINVAL;
}
return __sys_bpf(cmd, KERNEL_BPFPTR(attr), attr_size);
return __sys_bpf(cmd, KERNEL_BPFPTR(attr), attr_size, KERNEL_BPFPTR(NULL), 0);
}

View File

@ -19294,12 +19294,12 @@ int bpf_fixup_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
return 0;
}
int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr, __u32 uattr_size)
int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,
struct bpf_log_attr *attr_log)
{
u64 start_time = ktime_get_ns();
struct bpf_verifier_env *env;
int i, len, ret = -EINVAL, err;
u32 log_true_size;
bool is_priv;
BTF_TYPE_EMIT(enum bpf_features);
@ -19346,9 +19346,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr, __u3
/* user could have requested verbose verifier output
* and supplied buffer to store the verification trace
*/
ret = bpf_vlog_init(&env->log, attr->log_level,
(char __user *) (unsigned long) attr->log_buf,
attr->log_size);
ret = bpf_vlog_init(&env->log, attr_log->level, attr_log->ubuf, attr_log->size);
if (ret)
goto err_unlock;
@ -19510,17 +19508,10 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr, __u3
env->prog->aux->verified_insns = env->insn_processed;
/* preserve original error even if log finalization is successful */
err = bpf_vlog_finalize(&env->log, &log_true_size);
err = bpf_log_attr_finalize(attr_log, &env->log);
if (err)
ret = err;
if (uattr_size >= offsetofend(union bpf_attr, log_true_size) &&
copy_to_bpfptr_offset(uattr, offsetof(union bpf_attr, log_true_size),
&log_true_size, sizeof(log_true_size))) {
ret = -EFAULT;
goto err_release_maps;
}
if (ret)
goto err_release_maps;

View File

@ -994,6 +994,7 @@ enum bpf_cmd {
BPF_PROG_STREAM_READ_BY_FD,
BPF_PROG_ASSOC_STRUCT_OPS,
__MAX_BPF_CMD,
BPF_COMMON_ATTRS = 1 << 16, /* Indicate carrying syscall common attrs. */
};
enum bpf_map_type {
@ -1500,6 +1501,13 @@ struct bpf_stack_build_id {
};
};
struct bpf_common_attr {
__aligned_u64 log_buf;
__u32 log_size;
__u32 log_level;
__u32 log_true_size;
};
#define BPF_OBJ_NAME_LEN 16U
enum {

View File

@ -69,6 +69,42 @@ static inline __u64 ptr_to_u64(const void *ptr)
return (__u64) (unsigned long) ptr;
}
static inline int sys_bpf_ext(enum bpf_cmd cmd, union bpf_attr *attr,
unsigned int size,
struct bpf_common_attr *attr_common,
unsigned int size_common)
{
cmd = attr_common ? (cmd | BPF_COMMON_ATTRS) : (cmd & ~BPF_COMMON_ATTRS);
return syscall(__NR_bpf, cmd, attr, size, attr_common, size_common);
}
static inline int sys_bpf_ext_fd(enum bpf_cmd cmd, union bpf_attr *attr,
unsigned int size,
struct bpf_common_attr *attr_common,
unsigned int size_common)
{
int fd;
fd = sys_bpf_ext(cmd, attr, size, attr_common, size_common);
return ensure_good_fd(fd);
}
int probe_sys_bpf_ext(void)
{
const size_t attr_sz = offsetofend(union bpf_attr, prog_token_fd);
union bpf_attr attr;
int fd;
memset(&attr, 0, attr_sz);
fd = syscall(__NR_bpf, BPF_PROG_LOAD | BPF_COMMON_ATTRS, &attr, attr_sz, NULL,
sizeof(struct bpf_common_attr));
if (fd >= 0) {
close(fd);
return -EINVAL;
}
return errno == EFAULT ? 1 : 0;
}
static inline int sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr,
unsigned int size)
{
@ -173,6 +209,9 @@ int bpf_map_create(enum bpf_map_type map_type,
const struct bpf_map_create_opts *opts)
{
const size_t attr_sz = offsetofend(union bpf_attr, excl_prog_hash_size);
const size_t attr_common_sz = sizeof(struct bpf_common_attr);
struct bpf_common_attr attr_common;
struct bpf_log_opts *log_opts;
union bpf_attr attr;
int fd;
@ -206,7 +245,18 @@ int bpf_map_create(enum bpf_map_type map_type,
attr.excl_prog_hash = ptr_to_u64(OPTS_GET(opts, excl_prog_hash, NULL));
attr.excl_prog_hash_size = OPTS_GET(opts, excl_prog_hash_size, 0);
fd = sys_bpf_fd(BPF_MAP_CREATE, &attr, attr_sz);
log_opts = OPTS_GET(opts, log_opts, NULL);
if (log_opts && feat_supported(NULL, FEAT_BPF_SYSCALL_COMMON_ATTRS)) {
memset(&attr_common, 0, attr_common_sz);
attr_common.log_buf = ptr_to_u64(OPTS_GET(log_opts, buf, NULL));
attr_common.log_size = OPTS_GET(log_opts, size, 0);
attr_common.log_level = OPTS_GET(log_opts, level, 0);
fd = sys_bpf_ext_fd(BPF_MAP_CREATE, &attr, attr_sz, &attr_common, attr_common_sz);
OPTS_SET(log_opts, true_size, attr_common.log_true_size);
} else {
fd = sys_bpf_fd(BPF_MAP_CREATE, &attr, attr_sz);
OPTS_SET(log_opts, true_size, 0);
}
return libbpf_err_errno(fd);
}

View File

@ -37,6 +37,18 @@ extern "C" {
LIBBPF_API int libbpf_set_memlock_rlim(size_t memlock_bytes);
struct bpf_log_opts {
size_t sz; /* size of this struct for forward/backward compatibility */
char *buf;
__u32 size;
__u32 level;
__u32 true_size; /* out parameter set by kernel */
size_t :0;
};
#define bpf_log_opts__last_field true_size
struct bpf_map_create_opts {
size_t sz; /* size of this struct for forward/backward compatibility */
@ -57,9 +69,12 @@ struct bpf_map_create_opts {
const void *excl_prog_hash;
__u32 excl_prog_hash_size;
struct bpf_log_opts *log_opts;
size_t :0;
};
#define bpf_map_create_opts__last_field excl_prog_hash_size
#define bpf_map_create_opts__last_field log_opts
LIBBPF_API int bpf_map_create(enum bpf_map_type map_type,
const char *map_name,

View File

@ -615,6 +615,11 @@ static int probe_kern_btf_layout(int token_fd)
(char *)layout, token_fd));
}
static int probe_bpf_syscall_common_attrs(int token_fd)
{
return probe_sys_bpf_ext();
}
typedef int (*feature_probe_fn)(int /* token_fd */);
static struct kern_feature_cache feature_cache;
@ -699,6 +704,9 @@ static struct kern_feature_desc {
[FEAT_BTF_LAYOUT] = {
"kernel supports BTF layout", probe_kern_btf_layout,
},
[FEAT_BPF_SYSCALL_COMMON_ATTRS] = {
"BPF syscall common attributes support", probe_bpf_syscall_common_attrs,
},
};
bool feat_supported(struct kern_feature_cache *cache, enum kern_feature_id feat_id)

View File

@ -398,6 +398,8 @@ enum kern_feature_id {
FEAT_UPROBE_SYSCALL,
/* Kernel supports BTF layout information */
FEAT_BTF_LAYOUT,
/* Kernel supports BPF syscall common attributes */
FEAT_BPF_SYSCALL_COMMON_ATTRS,
__FEAT_CNT,
};
@ -768,4 +770,5 @@ int probe_fd(int fd);
#define SHA256_DWORD_SIZE SHA256_DIGEST_LENGTH / sizeof(__u64)
void libbpf_sha256(const void *data, size_t len, __u8 out[SHA256_DIGEST_LENGTH]);
int probe_sys_bpf_ext(void);
#endif /* __LIBBPF_LIBBPF_INTERNAL_H */

View File

@ -212,3 +212,169 @@ void test_map_init(void)
if (test__start_subtest("pcpu_lru_map_init"))
test_pcpu_lru_map_init();
}
static void test_map_create(enum bpf_map_type map_type, const char *map_name,
struct bpf_map_create_opts *opts, const char *exp_msg)
{
const int key_size = 4, value_size = 4, max_entries = 1;
char log_buf[128];
int fd;
LIBBPF_OPTS(bpf_log_opts, log_opts);
log_buf[0] = '\0';
log_opts.buf = log_buf;
log_opts.size = sizeof(log_buf);
log_opts.level = 1;
opts->log_opts = &log_opts;
fd = bpf_map_create(map_type, map_name, key_size, value_size, max_entries, opts);
if (!ASSERT_LT(fd, 0, "bpf_map_create")) {
close(fd);
return;
}
ASSERT_STREQ(log_buf, exp_msg, "log_buf");
ASSERT_EQ(log_opts.true_size, strlen(exp_msg) + 1, "true_size");
}
static void test_map_create_array(struct bpf_map_create_opts *opts, const char *exp_msg)
{
test_map_create(BPF_MAP_TYPE_ARRAY, "test_map_create", opts, exp_msg);
}
static void test_invalid_vmlinux_value_type_id_struct_ops(void)
{
const char *msg = "btf_vmlinux_value_type_id can only be used with struct_ops maps.\n";
LIBBPF_OPTS(bpf_map_create_opts, opts,
.btf_vmlinux_value_type_id = 1,
);
test_map_create_array(&opts, msg);
}
static void test_invalid_vmlinux_value_type_id_kv_type_id(void)
{
const char *msg = "btf_vmlinux_value_type_id is mutually exclusive with btf_key_type_id and btf_value_type_id.\n";
LIBBPF_OPTS(bpf_map_create_opts, opts,
.btf_vmlinux_value_type_id = 1,
.btf_key_type_id = 1,
);
test_map_create(BPF_MAP_TYPE_STRUCT_OPS, "test_map_create", &opts, msg);
}
static void test_invalid_value_type_id(void)
{
const char *msg = "Invalid btf_value_type_id.\n";
LIBBPF_OPTS(bpf_map_create_opts, opts,
.btf_key_type_id = 1,
);
test_map_create_array(&opts, msg);
}
static void test_invalid_map_extra(void)
{
const char *msg = "Invalid map_extra.\n";
LIBBPF_OPTS(bpf_map_create_opts, opts,
.map_extra = 1,
);
test_map_create_array(&opts, msg);
}
static void test_invalid_numa_node(void)
{
const char *msg = "Invalid numa_node.\n";
LIBBPF_OPTS(bpf_map_create_opts, opts,
.map_flags = BPF_F_NUMA_NODE,
.numa_node = 0xFF,
);
test_map_create_array(&opts, msg);
}
static void test_invalid_map_type(void)
{
const char *msg = "Invalid map_type.\n";
LIBBPF_OPTS(bpf_map_create_opts, opts);
test_map_create(__MAX_BPF_MAP_TYPE, "test_map_create", &opts, msg);
}
static void test_invalid_token_fd(void)
{
const char *msg = "Invalid map_token_fd.\n";
LIBBPF_OPTS(bpf_map_create_opts, opts,
.map_flags = BPF_F_TOKEN_FD,
.token_fd = 0xFF,
);
test_map_create_array(&opts, msg);
}
static void test_invalid_map_name(void)
{
const char *msg = "Invalid map_name.\n";
LIBBPF_OPTS(bpf_map_create_opts, opts);
test_map_create(BPF_MAP_TYPE_ARRAY, "test-!@#", &opts, msg);
}
static void test_invalid_btf_fd(void)
{
const char *msg = "Invalid btf_fd.\n";
LIBBPF_OPTS(bpf_map_create_opts, opts,
.btf_fd = -1,
.btf_key_type_id = 1,
.btf_value_type_id = 1,
);
test_map_create_array(&opts, msg);
}
static void test_excl_prog_hash_size_1(void)
{
const char *msg = "Invalid excl_prog_hash_size.\n";
const char *hash = "DEADCODE";
LIBBPF_OPTS(bpf_map_create_opts, opts,
.excl_prog_hash = hash,
);
test_map_create_array(&opts, msg);
}
static void test_excl_prog_hash_size_2(void)
{
const char *msg = "Invalid excl_prog_hash_size.\n";
LIBBPF_OPTS(bpf_map_create_opts, opts,
.excl_prog_hash_size = 1,
);
test_map_create_array(&opts, msg);
}
void test_map_create_failure(void)
{
if (test__start_subtest("invalid_vmlinux_value_type_id_struct_ops"))
test_invalid_vmlinux_value_type_id_struct_ops();
if (test__start_subtest("invalid_vmlinux_value_type_id_kv_type_id"))
test_invalid_vmlinux_value_type_id_kv_type_id();
if (test__start_subtest("invalid_value_type_id"))
test_invalid_value_type_id();
if (test__start_subtest("invalid_map_extra"))
test_invalid_map_extra();
if (test__start_subtest("invalid_numa_node"))
test_invalid_numa_node();
if (test__start_subtest("invalid_map_type"))
test_invalid_map_type();
if (test__start_subtest("invalid_token_fd"))
test_invalid_token_fd();
if (test__start_subtest("invalid_map_name"))
test_invalid_map_name();
if (test__start_subtest("invalid_btf_fd"))
test_invalid_btf_fd();
if (test__start_subtest("invalid_excl_prog_hash_size_1"))
test_excl_prog_hash_size_1();
if (test__start_subtest("invalid_excl_prog_hash_size_2"))
test_excl_prog_hash_size_2();
}