* [PATCH bpf-next v2 1/9] bpf: Add log support for tracing link_create
2026-11-14 15:06 [PATCH bpf-next v2 0/9] bpf: Add log support for tracing link_create Leon Hwang
@ 2026-11-14 15:06 ` Leon Hwang
2026-11-14 15:06 ` [PATCH bpf-next v2 2/9] bpf: Add log support for tracing_multi link_create Leon Hwang
` (7 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Leon Hwang @ 2026-11-14 15:06 UTC (permalink / raw)
To: bpf
Cc: Alexei Starovoitov, Daniel Borkmann, John Fastabend,
Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
Emil Tsalapatis, Ihor Solodrai, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, KP Singh, Matt Bobrowski, Shuah Khan,
Leon Hwang, linux-kernel, linux-trace-kernel, linux-kselftest,
netdev
Tracing BPF_LINK_CREATE returns an error without explaining why attachment
failed.
Pass the BPF_LINK_CREATE log to the tracing attach path and report invalid
attach parameters. Also pass it to bpf_check_attach_target() so freplace
attachment to a new target can report target-check failures.
Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
---
kernel/bpf/syscall.c | 21 ++++++++++++++++-----
1 file changed, 16 insertions(+), 5 deletions(-)
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 04a3b3c80c74..cf6bf3765b3d 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -3674,7 +3674,8 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog,
int tgt_prog_fd,
u32 btf_id,
u64 bpf_cookie,
- enum bpf_attach_type attach_type)
+ enum bpf_attach_type attach_type,
+ struct bpf_verifier_log *log)
{
struct bpf_link_primer link_primer;
struct bpf_prog *tgt_prog = NULL;
@@ -3711,6 +3712,7 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog,
}
if (!!tgt_prog_fd != !!btf_id) {
+ bpf_log(log, "tgt_prog_fd and btf_id must be both set or both unset.\n");
err = -EINVAL;
goto out_put_prog;
}
@@ -3723,12 +3725,14 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog,
* attach_tracing_prog flag is set.
*/
if (prog->type != BPF_PROG_TYPE_EXT) {
+ bpf_log(log, "tgt_prog_fd is only allowed for freplace prog.\n");
err = -EINVAL;
goto out_put_prog;
}
tgt_prog = bpf_prog_get(tgt_prog_fd);
if (IS_ERR(tgt_prog)) {
+ bpf_log(log, "Invalid tgt_prog_fd.\n");
err = PTR_ERR(tgt_prog);
tgt_prog = NULL;
goto out_put_prog;
@@ -3783,11 +3787,13 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog,
*/
if (prog->type != BPF_PROG_TYPE_TRACING &&
prog->type != BPF_PROG_TYPE_LSM) {
+ bpf_log(log, "Only allow re-attach for tracing and lsm progs.\n");
err = -EINVAL;
goto out_unlock;
}
/* We can allow re-attach only if we have valid attach_btf. */
if (!prog->aux->attach_btf) {
+ bpf_log(log, "Invalid attach_btf.\n");
err = -EINVAL;
goto out_unlock;
}
@@ -3803,7 +3809,7 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog,
*/
struct bpf_attach_target_info tgt_info = {};
- err = bpf_check_attach_target(NULL, prog, tgt_prog, btf_id,
+ err = bpf_check_attach_target(log, prog, tgt_prog, btf_id,
&tgt_info);
if (err)
goto out_unlock;
@@ -3843,6 +3849,7 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog,
*/
if (prog->type == BPF_PROG_TYPE_EXT &&
prog->aux->kprobe_write_ctx != tgt_prog->aux->kprobe_write_ctx) {
+ bpf_log(log, "Invalid kprobe_write_ctx.\n");
err = -EINVAL;
goto out_unlock;
}
@@ -3856,6 +3863,7 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog,
tgt_prog->type == BPF_PROG_TYPE_TRACEPOINT ||
tgt_prog->type == BPF_PROG_TYPE_SYSCALL) &&
prog->aux->max_ctx_offset > tgt_prog->aux->max_ctx_offset) {
+ bpf_log(log, "Freplace prog accesses more ctx than its target.\n");
err = -EINVAL;
goto out_unlock;
}
@@ -4374,7 +4382,7 @@ static int bpf_raw_tp_link_attach(struct bpf_prog *prog,
tp_name = prog->aux->attach_func_name;
break;
}
- return bpf_tracing_prog_attach(prog, 0, 0, 0, attach_type);
+ return bpf_tracing_prog_attach(prog, 0, 0, 0, attach_type, NULL);
case BPF_PROG_TYPE_RAW_TRACEPOINT:
case BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE:
if (strncpy_from_user(buf, user_tp_name, sizeof(buf) - 1) < 0)
@@ -5907,11 +5915,13 @@ static int link_create(union bpf_attr *attr, bpfptr_t uattr, struct bpf_common_a
attr->link_create.target_fd,
attr->link_create.target_btf_id,
attr->link_create.tracing.cookie,
- attr->link_create.attach_type);
+ attr->link_create.attach_type,
+ log);
break;
case BPF_PROG_TYPE_LSM:
case BPF_PROG_TYPE_TRACING:
if (attr->link_create.attach_type != prog->expected_attach_type) {
+ bpf_log(log, "Invalid tracing attach_type.\n");
ret = -EINVAL;
goto out;
}
@@ -5929,7 +5939,8 @@ static int link_create(union bpf_attr *attr, bpfptr_t uattr, struct bpf_common_a
attr->link_create.target_fd,
attr->link_create.target_btf_id,
attr->link_create.tracing.cookie,
- attr->link_create.attach_type);
+ attr->link_create.attach_type,
+ log);
break;
case BPF_PROG_TYPE_FLOW_DISSECTOR:
case BPF_PROG_TYPE_SK_LOOKUP:
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH bpf-next v2 2/9] bpf: Add log support for tracing_multi link_create
2026-11-14 15:06 [PATCH bpf-next v2 0/9] bpf: Add log support for tracing link_create Leon Hwang
2026-11-14 15:06 ` [PATCH bpf-next v2 1/9] " Leon Hwang
@ 2026-11-14 15:06 ` Leon Hwang
2026-11-14 15:06 ` [PATCH bpf-next v2 3/9] bpf: Add log support for iter link_create Leon Hwang
` (6 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Leon Hwang @ 2026-11-14 15:06 UTC (permalink / raw)
To: bpf
Cc: Alexei Starovoitov, Daniel Borkmann, John Fastabend,
Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
Emil Tsalapatis, Ihor Solodrai, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, KP Singh, Matt Bobrowski, Shuah Khan,
Leon Hwang, linux-kernel, linux-trace-kernel, linux-kselftest,
netdev
tracing_multi BPF_LINK_CREATE returns an error for invalid counts, missing
IDs, unsupported flags, or a nonzero target FD without explaining which
check failed.
Report these failures through the BPF_LINK_CREATE log.
Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
---
include/linux/trace_events.h | 7 +++++--
kernel/bpf/syscall.c | 2 +-
kernel/trace/bpf_trace.c | 26 +++++++++++++++++++++-----
3 files changed, 27 insertions(+), 8 deletions(-)
diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h
index 321f2f3a6c8e..bb825ea5d09c 100644
--- a/include/linux/trace_events.h
+++ b/include/linux/trace_events.h
@@ -16,6 +16,7 @@ struct tracer;
struct dentry;
struct bpf_prog;
union bpf_attr;
+struct bpf_verifier_log;
/* Used for event string fields when they are NULL */
#define EVENT_NULL_STR "(null)"
@@ -796,7 +797,8 @@ int bpf_get_perf_event_info(const struct perf_event *event, u32 *prog_id,
unsigned long *missed);
int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog);
int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog);
-int bpf_tracing_multi_attach(struct bpf_prog *prog, const union bpf_attr *attr);
+int bpf_tracing_multi_attach(struct bpf_prog *prog, const union bpf_attr *attr,
+ struct bpf_verifier_log *log);
#else
static inline unsigned int trace_call_bpf(struct trace_event_call *call, void *ctx)
{
@@ -855,7 +857,8 @@ bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
return -EOPNOTSUPP;
}
static inline int
-bpf_tracing_multi_attach(struct bpf_prog *prog, const union bpf_attr *attr)
+bpf_tracing_multi_attach(struct bpf_prog *prog, const union bpf_attr *attr,
+ struct bpf_verifier_log *log)
{
return -EOPNOTSUPP;
}
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index cf6bf3765b3d..3c7bedced354 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -5933,7 +5933,7 @@ static int link_create(union bpf_attr *attr, bpfptr_t uattr, struct bpf_common_a
else if (prog->expected_attach_type == BPF_LSM_CGROUP)
ret = cgroup_bpf_link_attach(attr, prog);
else if (is_tracing_multi(prog->expected_attach_type))
- ret = bpf_tracing_multi_attach(prog, attr);
+ ret = bpf_tracing_multi_attach(prog, attr, log);
else
ret = bpf_tracing_prog_attach(prog,
attr->link_create.target_fd,
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index b4ff9d4e86b3..a3964d7c9497 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -3911,7 +3911,8 @@ static int check_dup_ids(u32 *ids, u64 *cookies, u32 cnt)
return err;
}
-int bpf_tracing_multi_attach(struct bpf_prog *prog, const union bpf_attr *attr)
+int bpf_tracing_multi_attach(struct bpf_prog *prog, const union bpf_attr *attr,
+ struct bpf_verifier_log *log)
{
struct bpf_tracing_multi_link *link = NULL;
struct bpf_tramp_node *fexits = NULL;
@@ -3925,12 +3926,26 @@ int bpf_tracing_multi_attach(struct bpf_prog *prog, const union bpf_attr *attr)
uids = u64_to_user_ptr(attr->link_create.tracing_multi.ids);
cnt = attr->link_create.tracing_multi.cnt;
- if (!cnt || !uids)
+ if (!cnt) {
+ bpf_log(log, "tracing_multi.cnt cannot be zero.\n");
return -EINVAL;
- if (cnt > MAX_TRACING_MULTI_CNT)
+ }
+ if (!uids) {
+ bpf_log(log, "tracing_multi.ids must be set.\n");
+ return -EINVAL;
+ }
+ if (cnt > MAX_TRACING_MULTI_CNT) {
+ bpf_log(log, "tracing_multi.cnt is too large.\n");
return -E2BIG;
- if (attr->link_create.flags || attr->link_create.target_fd)
+ }
+ if (attr->link_create.flags) {
+ bpf_log(log, "Invalid flags.\n");
return -EINVAL;
+ }
+ if (attr->link_create.target_fd) {
+ bpf_log(log, "Invalid target_fd.\n");
+ return -EINVAL;
+ }
ids = kvmalloc_objs(*ids, cnt);
if (!ids)
@@ -4001,7 +4016,8 @@ int bpf_tracing_multi_attach(struct bpf_prog *prog, const union bpf_attr *attr)
#else
-int bpf_tracing_multi_attach(struct bpf_prog *prog, const union bpf_attr *attr)
+int bpf_tracing_multi_attach(struct bpf_prog *prog, const union bpf_attr *attr,
+ struct bpf_verifier_log *log)
{
return -EOPNOTSUPP;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH bpf-next v2 3/9] bpf: Add log support for iter link_create
2026-11-14 15:06 [PATCH bpf-next v2 0/9] bpf: Add log support for tracing link_create Leon Hwang
2026-11-14 15:06 ` [PATCH bpf-next v2 1/9] " Leon Hwang
2026-11-14 15:06 ` [PATCH bpf-next v2 2/9] bpf: Add log support for tracing_multi link_create Leon Hwang
@ 2026-11-14 15:06 ` Leon Hwang
2026-11-14 15:06 ` [PATCH bpf-next v2 4/9] bpf: Add log support for cgroup prog link_create Leon Hwang
` (5 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Leon Hwang @ 2026-11-14 15:06 UTC (permalink / raw)
To: bpf
Cc: Alexei Starovoitov, Daniel Borkmann, John Fastabend,
Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
Emil Tsalapatis, Ihor Solodrai, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, KP Singh, Matt Bobrowski, Shuah Khan,
Leon Hwang, linux-kernel, linux-trace-kernel, linux-kselftest,
netdev
iter BPF_LINK_CREATE returns an error without explaining invalid target
FDs, flags, or iterator information, or an unsupported program.
Pass the BPF_LINK_CREATE log to the iter attach path and report these
failures.
Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
---
include/linux/bpf.h | 3 ++-
kernel/bpf/bpf_iter.c | 27 +++++++++++++++++++++------
kernel/bpf/syscall.c | 2 +-
3 files changed, 24 insertions(+), 8 deletions(-)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 36587b63ce13..c3f6364bc1d8 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -3276,7 +3276,8 @@ void bpf_iter_unreg_target(const struct bpf_iter_reg *reg_info);
int bpf_iter_prog_supported(struct bpf_prog *prog);
const struct bpf_func_proto *
bpf_iter_get_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog);
-int bpf_iter_link_attach(const union bpf_attr *attr, bpfptr_t uattr, struct bpf_prog *prog);
+int bpf_iter_link_attach(const union bpf_attr *attr, bpfptr_t uattr, struct bpf_prog *prog,
+ struct bpf_verifier_log *log);
int bpf_iter_new_fd(struct bpf_link *link);
bool bpf_link_is_iter(struct bpf_link *link);
struct bpf_prog *bpf_iter_get_info(struct bpf_iter_meta *meta, bool in_stop);
diff --git a/kernel/bpf/bpf_iter.c b/kernel/bpf/bpf_iter.c
index f0b29a170935..4a9e08fb8e75 100644
--- a/kernel/bpf/bpf_iter.c
+++ b/kernel/bpf/bpf_iter.c
@@ -5,6 +5,7 @@
#include <linux/anon_inodes.h>
#include <linux/filter.h>
#include <linux/bpf.h>
+#include <linux/bpf_verifier.h>
#include <linux/rcupdate_trace.h>
struct bpf_iter_target_info {
@@ -528,7 +529,7 @@ bool bpf_link_is_iter(struct bpf_link *link)
}
int bpf_iter_link_attach(const union bpf_attr *attr, bpfptr_t uattr,
- struct bpf_prog *prog)
+ struct bpf_prog *prog, struct bpf_verifier_log *log)
{
struct bpf_iter_target_info *tinfo = NULL, *iter;
struct bpf_link_primer link_primer;
@@ -538,24 +539,36 @@ int bpf_iter_link_attach(const union bpf_attr *attr, bpfptr_t uattr,
bpfptr_t ulinfo;
int err;
- if (attr->link_create.target_fd || attr->link_create.flags)
+ if (attr->link_create.target_fd) {
+ bpf_log(log, "Invalid target_fd.\n");
+ return -EINVAL;
+ }
+ if (attr->link_create.flags) {
+ bpf_log(log, "Invalid flags.\n");
return -EINVAL;
+ }
memset(&linfo, 0, sizeof(union bpf_iter_link_info));
ulinfo = make_bpfptr(attr->link_create.iter_info, uattr.is_kernel);
linfo_len = attr->link_create.iter_info_len;
- if (bpfptr_is_null(ulinfo) ^ !linfo_len)
+ if (bpfptr_is_null(ulinfo) ^ !linfo_len) {
+ bpf_log(log, "iter_info and iter_info_len must be both set or both unset.\n");
return -EINVAL;
+ }
if (!bpfptr_is_null(ulinfo)) {
err = bpf_check_uarg_tail_zero(ulinfo, sizeof(linfo),
linfo_len);
- if (err)
+ if (err) {
+ bpf_log(log, "Invalid iter_info_len.\n");
return err;
+ }
linfo_len = min_t(u32, linfo_len, sizeof(linfo));
- if (copy_from_bpfptr(&linfo, ulinfo, linfo_len))
+ if (copy_from_bpfptr(&linfo, ulinfo, linfo_len)) {
+ bpf_log(log, "Invalid iter_info.\n");
return -EFAULT;
+ }
}
prog_btf_id = prog->aux->attach_btf_id;
@@ -570,8 +583,10 @@ int bpf_iter_link_attach(const union bpf_attr *attr, bpfptr_t uattr,
if (!tinfo)
return -ENOENT;
- if (!bpf_iter_target_support_prog(tinfo, prog))
+ if (!bpf_iter_target_support_prog(tinfo, prog)) {
+ bpf_log(log, "The iter target does not support the bpf prog.\n");
return -EINVAL;
+ }
link = kzalloc_obj(*link, GFP_USER | __GFP_NOWARN);
if (!link)
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 3c7bedced354..9627dfc982ff 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -5929,7 +5929,7 @@ static int link_create(union bpf_attr *attr, bpfptr_t uattr, struct bpf_common_a
ret = bpf_raw_tp_link_attach(prog, NULL, attr->link_create.tracing.cookie,
attr->link_create.attach_type);
else if (prog->expected_attach_type == BPF_TRACE_ITER)
- ret = bpf_iter_link_attach(attr, uattr, prog);
+ ret = bpf_iter_link_attach(attr, uattr, prog, log);
else if (prog->expected_attach_type == BPF_LSM_CGROUP)
ret = cgroup_bpf_link_attach(attr, prog);
else if (is_tracing_multi(prog->expected_attach_type))
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH bpf-next v2 4/9] bpf: Add log support for cgroup prog link_create
2026-11-14 15:06 [PATCH bpf-next v2 0/9] bpf: Add log support for tracing link_create Leon Hwang
` (2 preceding siblings ...)
2026-11-14 15:06 ` [PATCH bpf-next v2 3/9] bpf: Add log support for iter link_create Leon Hwang
@ 2026-11-14 15:06 ` Leon Hwang
2026-11-14 15:06 ` [PATCH bpf-next v2 5/9] libbpf: Add log_opts support for BPF_TRACE_ITER Leon Hwang
` (4 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Leon Hwang @ 2026-11-14 15:06 UTC (permalink / raw)
To: bpf
Cc: Alexei Starovoitov, Daniel Borkmann, John Fastabend,
Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
Emil Tsalapatis, Ihor Solodrai, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, KP Singh, Matt Bobrowski, Shuah Khan,
Leon Hwang, linux-kernel, linux-trace-kernel, linux-kselftest,
netdev
Cgroup prog BPF_LINK_CREATE returns errors without explaining invalid
flags, target FDs, or relative attachment parameters. Revision mismatches,
cgroup hierarchy restrictions, conflicting attachment flags, and the
per-cgroup program limit can also cause unexplained failures.
Pass the BPF_LINK_CREATE log through the cgroup attachment helpers and
report these failures at the checks that reject the attachment. Diagnose
relative link attachments that omit BPF_F_LINK.
Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
---
include/linux/bpf-cgroup.h | 6 ++-
kernel/bpf/cgroup.c | 76 +++++++++++++++++++++++++++-----------
kernel/bpf/syscall.c | 4 +-
3 files changed, 60 insertions(+), 26 deletions(-)
diff --git a/include/linux/bpf-cgroup.h b/include/linux/bpf-cgroup.h
index 02ef7f899b3d..684bd2cfda6f 100644
--- a/include/linux/bpf-cgroup.h
+++ b/include/linux/bpf-cgroup.h
@@ -425,7 +425,8 @@ int cgroup_bpf_prog_attach(const union bpf_attr *attr,
enum bpf_prog_type ptype, struct bpf_prog *prog);
int cgroup_bpf_prog_detach(const union bpf_attr *attr,
enum bpf_prog_type ptype);
-int cgroup_bpf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog);
+int cgroup_bpf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog,
+ struct bpf_verifier_log *log);
int cgroup_bpf_prog_query(const union bpf_attr *attr,
union bpf_attr __user *uattr, u32 uattr_size);
@@ -452,7 +453,8 @@ static inline int cgroup_bpf_prog_detach(const union bpf_attr *attr,
}
static inline int cgroup_bpf_link_attach(const union bpf_attr *attr,
- struct bpf_prog *prog)
+ struct bpf_prog *prog,
+ struct bpf_verifier_log *log)
{
return -EINVAL;
}
diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c
index 57a785241d42..a4d49c4df712 100644
--- a/kernel/bpf/cgroup.c
+++ b/kernel/bpf/cgroup.c
@@ -854,7 +854,8 @@ static struct bpf_prog *bpf_get_anchor_prog(u32 flags, u32 id_or_fd)
}
static struct bpf_prog_list *get_prog_list(struct hlist_head *progs, struct bpf_prog *prog,
- struct bpf_cgroup_link *link, u32 flags, u32 id_or_fd)
+ struct bpf_cgroup_link *link, u32 flags, u32 id_or_fd,
+ struct bpf_verifier_log *log)
{
bool is_link = flags & BPF_F_LINK, is_id = flags & BPF_F_ID;
struct bpf_prog_list *pltmp, *pl = ERR_PTR(-EINVAL);
@@ -867,20 +868,30 @@ static struct bpf_prog_list *get_prog_list(struct hlist_head *progs, struct bpf_
is_after = flags & BPF_F_AFTER;
if (is_link || is_id || id_or_fd) {
/* flags must have either BPF_F_BEFORE or BPF_F_AFTER */
- if (is_before == is_after)
+ if (is_before == is_after) {
+ bpf_log(log, "Specify exactly one of BPF_F_BEFORE or BPF_F_AFTER.\n");
return ERR_PTR(-EINVAL);
- if ((is_link && !link) || (!is_link && !prog))
+ }
+ if (is_link && !link)
+ return ERR_PTR(-EINVAL);
+ if (!is_link && !prog) {
+ bpf_log(log, "BPF_F_LINK is required for relative link attachment.\n");
return ERR_PTR(-EINVAL);
+ }
} else if (!hlist_empty(progs)) {
/* flags cannot have both BPF_F_BEFORE and BPF_F_AFTER */
- if (is_before && is_after)
+ if (is_before && is_after) {
+ bpf_log(log, "BPF_F_BEFORE and BPF_F_AFTER are mutually exclusive.\n");
return ERR_PTR(-EINVAL);
+ }
}
if (is_link) {
anchor_link = bpf_get_anchor_link(flags, id_or_fd);
- if (IS_ERR(anchor_link))
+ if (IS_ERR(anchor_link)) {
+ bpf_log(log, "Invalid relative link %s.\n", is_id ? "ID" : "FD");
return ERR_CAST(anchor_link);
+ }
} else if (is_id || id_or_fd) {
anchor_prog = bpf_get_anchor_prog(flags, id_or_fd);
if (IS_ERR(anchor_prog))
@@ -905,13 +916,16 @@ static struct bpf_prog_list *get_prog_list(struct hlist_head *progs, struct bpf_
hlist_for_each_entry(pltmp, progs, node) {
if ((anchor_prog && anchor_prog == pltmp->prog) ||
(anchor_link && anchor_link == &pltmp->link->link)) {
- if (!!(pltmp->flags & BPF_F_PREORDER) != preorder)
+ if (!!(pltmp->flags & BPF_F_PREORDER) != preorder) {
+ bpf_log(log, "Relative attachment BPF_F_PREORDER mismatch.\n");
goto out;
+ }
pl = pltmp;
goto out;
}
}
+ bpf_log(log, "Relative attachment not found in target cgroup.\n");
pl = ERR_PTR(-ENOENT);
out:
if (anchor_link)
@@ -923,11 +937,11 @@ static struct bpf_prog_list *get_prog_list(struct hlist_head *progs, struct bpf_
static int insert_pl_to_hlist(struct bpf_prog_list *pl, struct hlist_head *progs,
struct bpf_prog *prog, struct bpf_cgroup_link *link,
- u32 flags, u32 id_or_fd)
+ u32 flags, u32 id_or_fd, struct bpf_verifier_log *log)
{
struct bpf_prog_list *pltmp;
- pltmp = get_prog_list(progs, prog, link, flags, id_or_fd);
+ pltmp = get_prog_list(progs, prog, link, flags, id_or_fd, log);
if (IS_ERR(pltmp))
return PTR_ERR(pltmp);
@@ -952,6 +966,7 @@ static int insert_pl_to_hlist(struct bpf_prog_list *pl, struct hlist_head *progs
* @flags: Option flags
* @id_or_fd: Relative prog id or fd
* @revision: bpf_prog_list revision
+ * @log: Optional log for attachment errors
*
* Exactly one of @prog or @link can be non-null.
* Must be called with cgroup_mutex held.
@@ -960,7 +975,7 @@ static int __cgroup_bpf_attach(struct cgroup *cgrp,
struct bpf_prog *prog, struct bpf_prog *replace_prog,
struct bpf_cgroup_link *link,
enum bpf_attach_type type, u32 flags, u32 id_or_fd,
- u64 revision)
+ u64 revision, struct bpf_verifier_log *log)
{
u32 saved_flags = (flags & (BPF_F_ALLOW_OVERRIDE | BPF_F_ALLOW_MULTI));
struct bpf_prog *old_prog = NULL;
@@ -991,23 +1006,33 @@ static int __cgroup_bpf_attach(struct cgroup *cgrp,
atype = bpf_cgroup_atype_find(type, new_prog->aux->attach_btf_id);
if (atype < 0)
return -EINVAL;
- if (revision && revision != cgrp->bpf.revisions[atype])
+ if (revision && revision != cgrp->bpf.revisions[atype]) {
+ bpf_log(log, "Cgroup revision mismatch: expected %llu, actual %llu.\n",
+ revision, cgrp->bpf.revisions[atype]);
return -ESTALE;
+ }
progs = &cgrp->bpf.progs[atype];
- if (!hierarchy_allows_attach(cgrp, atype))
+ if (!hierarchy_allows_attach(cgrp, atype)) {
+ bpf_log(log, "Cgroup hierarchy does not allow attachment.\n");
return -EPERM;
+ }
- if (!hlist_empty(progs) && cgrp->bpf.flags[atype] != saved_flags)
+ if (!hlist_empty(progs) && cgrp->bpf.flags[atype] != saved_flags) {
/* Disallow attaching non-overridable on top
* of existing overridable in this cgroup.
* Disallow attaching multi-prog if overridable or none
*/
+ bpf_log(log, "Attachment flags conflict with existing flags.\n");
return -EPERM;
+ }
- if (prog_list_length(progs, NULL) >= BPF_CGROUP_MAX_PROGS)
+ if (prog_list_length(progs, NULL) >= BPF_CGROUP_MAX_PROGS) {
+ bpf_log(log, "Too many programs attached to cgroup (limit %u).\n",
+ BPF_CGROUP_MAX_PROGS);
return -E2BIG;
+ }
pl = find_attach_entry(progs, prog, link, replace_prog,
flags & BPF_F_ALLOW_MULTI);
@@ -1029,7 +1054,7 @@ static int __cgroup_bpf_attach(struct cgroup *cgrp,
return -ENOMEM;
}
- err = insert_pl_to_hlist(pl, progs, prog, link, flags, id_or_fd);
+ err = insert_pl_to_hlist(pl, progs, prog, link, flags, id_or_fd, log);
if (err) {
kfree(pl);
bpf_cgroup_storages_free(new_storage);
@@ -1089,13 +1114,14 @@ static int cgroup_bpf_attach(struct cgroup *cgrp,
struct bpf_prog *prog, struct bpf_prog *replace_prog,
struct bpf_cgroup_link *link,
enum bpf_attach_type type,
- u32 flags, u32 id_or_fd, u64 revision)
+ u32 flags, u32 id_or_fd, u64 revision,
+ struct bpf_verifier_log *log)
{
int ret;
cgroup_lock();
ret = __cgroup_bpf_attach(cgrp, prog, replace_prog, link, type, flags,
- id_or_fd, revision);
+ id_or_fd, revision, log);
cgroup_unlock();
return ret;
}
@@ -1572,7 +1598,7 @@ int cgroup_bpf_prog_attach(const union bpf_attr *attr,
ret = cgroup_bpf_attach(cgrp, prog, replace_prog, NULL,
attr->attach_type, attr->attach_flags,
- attr->relative_fd, attr->expected_revision);
+ attr->relative_fd, attr->expected_revision, NULL);
if (replace_prog)
bpf_prog_put(replace_prog);
@@ -1702,19 +1728,24 @@ static const struct bpf_link_ops bpf_cgroup_link_lops = {
BPF_F_PREORDER | \
BPF_F_LINK)
-int cgroup_bpf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
+int cgroup_bpf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog,
+ struct bpf_verifier_log *log)
{
struct bpf_link_primer link_primer;
struct bpf_cgroup_link *link;
struct cgroup *cgrp;
int err;
- if (attr->link_create.flags & (~BPF_F_LINK_ATTACH_MASK))
+ if (attr->link_create.flags & (~BPF_F_LINK_ATTACH_MASK)) {
+ bpf_log(log, "Invalid flags.\n");
return -EINVAL;
+ }
cgrp = cgroup_get_from_fd(attr->link_create.target_fd);
- if (IS_ERR(cgrp))
+ if (IS_ERR(cgrp)) {
+ bpf_log(log, "Invalid target_fd.\n");
return PTR_ERR(cgrp);
+ }
link = kzalloc_obj(*link, GFP_USER);
if (!link) {
@@ -1734,7 +1765,7 @@ int cgroup_bpf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
err = cgroup_bpf_attach(cgrp, NULL, NULL, link,
link->link.attach_type, BPF_F_ALLOW_MULTI | attr->link_create.flags,
attr->link_create.cgroup.relative_fd,
- attr->link_create.cgroup.expected_revision);
+ attr->link_create.cgroup.expected_revision, log);
if (err) {
bpf_link_cleanup(&link_primer);
goto out_put_cgroup;
@@ -3184,7 +3215,8 @@ int cgroup_bpf_struct_ops_attach(struct bpf_map *map, const union bpf_attr *attr
cgrp->bpf.flags[atype] = BPF_F_ALLOW_MULTI;
err = insert_pl_to_hlist(pl, progs, NULL, link,
- flags | BPF_F_ALLOW_MULTI, attr->link_create.cgroup.relative_fd);
+ flags | BPF_F_ALLOW_MULTI,
+ attr->link_create.cgroup.relative_fd, NULL);
if (err)
goto unlock;
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 9627dfc982ff..35f8a71b5786 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -5908,7 +5908,7 @@ static int link_create(union bpf_attr *attr, bpfptr_t uattr, struct bpf_common_a
case BPF_PROG_TYPE_CGROUP_DEVICE:
case BPF_PROG_TYPE_CGROUP_SYSCTL:
case BPF_PROG_TYPE_CGROUP_SOCKOPT:
- ret = cgroup_bpf_link_attach(attr, prog);
+ ret = cgroup_bpf_link_attach(attr, prog, log);
break;
case BPF_PROG_TYPE_EXT:
ret = bpf_tracing_prog_attach(prog,
@@ -5931,7 +5931,7 @@ static int link_create(union bpf_attr *attr, bpfptr_t uattr, struct bpf_common_a
else if (prog->expected_attach_type == BPF_TRACE_ITER)
ret = bpf_iter_link_attach(attr, uattr, prog, log);
else if (prog->expected_attach_type == BPF_LSM_CGROUP)
- ret = cgroup_bpf_link_attach(attr, prog);
+ ret = cgroup_bpf_link_attach(attr, prog, log);
else if (is_tracing_multi(prog->expected_attach_type))
ret = bpf_tracing_multi_attach(prog, attr, log);
else
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH bpf-next v2 5/9] libbpf: Add log_opts support for BPF_TRACE_ITER
2026-11-14 15:06 [PATCH bpf-next v2 0/9] bpf: Add log support for tracing link_create Leon Hwang
` (3 preceding siblings ...)
2026-11-14 15:06 ` [PATCH bpf-next v2 4/9] bpf: Add log support for cgroup prog link_create Leon Hwang
@ 2026-11-14 15:06 ` Leon Hwang
2026-11-14 15:06 ` [PATCH bpf-next v2 6/9] selftests/bpf: Verify tracing link_create logs Leon Hwang
` (3 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Leon Hwang @ 2026-11-14 15:06 UTC (permalink / raw)
To: bpf
Cc: Alexei Starovoitov, Daniel Borkmann, John Fastabend,
Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
Emil Tsalapatis, Ihor Solodrai, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, KP Singh, Matt Bobrowski, Shuah Khan,
Leon Hwang, linux-kernel, linux-trace-kernel, linux-kselftest,
netdev
Add iter.info and iter.len to pass iterator information separately from
log_opts. Keep the existing iter_info and iter_info_len fields working
for callers that do not request logs.
Thereafter, log_opts can work for iter link_create.
Meanwhile, add log_opts support for bpf_program__attach_iter() API.
Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
---
tools/lib/bpf/bpf.c | 23 +++++++++++++++++------
tools/lib/bpf/bpf.h | 8 ++++++++
tools/lib/bpf/libbpf.c | 5 +++--
tools/lib/bpf/libbpf.h | 3 ++-
4 files changed, 30 insertions(+), 9 deletions(-)
diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c
index 38dcb31e8c0e..ad6b356cc48c 100644
--- a/tools/lib/bpf/bpf.c
+++ b/tools/lib/bpf/bpf.c
@@ -808,11 +808,6 @@ int bpf_link_create(int prog_fd, int target_fd,
if (iter_info_len && attach_type != BPF_TRACE_ITER)
return libbpf_err(-EINVAL);
- if (iter_info_len)
- iter_info = OPTS_GET(opts, iter_info, (void *)0);
- else
- log_opts = OPTS_GET(opts, log_opts, (void *)0);
-
/* validate we don't have unexpected combinations of non-zero fields */
if (iter_info_len || target_btf_id) {
if (iter_info_len && target_btf_id)
@@ -821,6 +816,22 @@ int bpf_link_create(int prog_fd, int target_fd,
return libbpf_err(-EINVAL);
}
+ if (attach_type == BPF_TRACE_ITER) {
+ if (iter_info_len) {
+ iter_info = OPTS_GET(opts, iter_info, (void *)0);
+ } else {
+ log_opts = OPTS_GET(opts, log_opts, (void *)0);
+ iter_info = OPTS_GET(opts, iter.info, (void *)0);
+ iter_info_len = OPTS_GET(opts, iter.len, 0);
+ if (iter_info_len && target_btf_id)
+ return libbpf_err(-EINVAL);
+ if (!OPTS_ZEROED(opts, iter))
+ return libbpf_err(-EINVAL);
+ }
+ } else {
+ log_opts = OPTS_GET(opts, log_opts, (void *)0);
+ }
+
memset(&attr, 0, attr_sz);
attr.link_create.prog_fd = prog_fd;
attr.link_create.target_fd = target_fd;
@@ -970,7 +981,7 @@ int bpf_link_create(int prog_fd, int target_fd,
return libbpf_err(-EINVAL);
break;
default:
- if (!OPTS_ZEROED(opts, iter_info_len))
+ if (!OPTS_ZEROED(opts, log_opts))
return libbpf_err(-EINVAL);
break;
}
diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h
index fe3b7fd8512b..2ac143fd1102 100644
--- a/tools/lib/bpf/bpf.h
+++ b/tools/lib/bpf/bpf.h
@@ -422,6 +422,10 @@ union bpf_iter_link_info; /* defined in up-to-date linux/bpf.h */
struct bpf_link_create_opts {
size_t sz; /* size of this struct for forward/backward compatibility */
__u32 flags;
+ /*
+ * iter_info and iter_info_len are deprecated, use iter.info and
+ * iter.len instead.
+ */
union {
union bpf_iter_link_info *iter_info;
struct bpf_log_opts *log_opts;
@@ -478,6 +482,10 @@ struct bpf_link_create_opts {
const __u64 *cookies;
__u32 cnt;
} tracing_multi;
+ struct {
+ union bpf_iter_link_info *info;
+ __u32 len;
+ } iter;
};
size_t :0;
};
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 44134421bd7d..28830d198e9d 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -14811,8 +14811,9 @@ bpf_program__attach_iter(const struct bpf_program *prog,
if (!OPTS_VALID(opts, bpf_iter_attach_opts))
return libbpf_err_ptr(-EINVAL);
- link_create_opts.iter_info = OPTS_GET(opts, link_info, (void *)0);
- link_create_opts.iter_info_len = OPTS_GET(opts, link_info_len, 0);
+ link_create_opts.log_opts = OPTS_GET(opts, log_opts, (void *)0);
+ link_create_opts.iter.info = OPTS_GET(opts, link_info, (void *)0);
+ link_create_opts.iter.len = OPTS_GET(opts, link_info_len, 0);
prog_fd = bpf_program__fd(prog);
if (prog_fd < 0) {
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 49f1120f4021..56300be81016 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -1000,8 +1000,9 @@ struct bpf_iter_attach_opts {
size_t sz; /* size of this struct for forward/backward compatibility */
union bpf_iter_link_info *link_info;
__u32 link_info_len;
+ struct bpf_log_opts *log_opts;
};
-#define bpf_iter_attach_opts__last_field link_info_len
+#define bpf_iter_attach_opts__last_field log_opts
LIBBPF_API struct bpf_link *
bpf_program__attach_iter(const struct bpf_program *prog,
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH bpf-next v2 6/9] selftests/bpf: Verify tracing link_create logs
2026-11-14 15:06 [PATCH bpf-next v2 0/9] bpf: Add log support for tracing link_create Leon Hwang
` (4 preceding siblings ...)
2026-11-14 15:06 ` [PATCH bpf-next v2 5/9] libbpf: Add log_opts support for BPF_TRACE_ITER Leon Hwang
@ 2026-11-14 15:06 ` Leon Hwang
2026-11-14 15:06 ` [PATCH bpf-next v2 7/9] selftests/bpf: Verify tracing_multi " Leon Hwang
` (2 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Leon Hwang @ 2026-11-14 15:06 UTC (permalink / raw)
To: bpf
Cc: Alexei Starovoitov, Daniel Borkmann, John Fastabend,
Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
Emil Tsalapatis, Ihor Solodrai, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, KP Singh, Matt Bobrowski, Shuah Khan,
Leon Hwang, linux-kernel, linux-trace-kernel, linux-kselftest,
netdev
Check the error code, log message, and reported log size for invalid
tracing and freplace link creation parameters. Cover reattachment and
attach-target checks, including incompatible kprobe_write_ctx and
max_ctx_offset settings.
Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
---
.../selftests/bpf/prog_tests/tracing_link.c | 327 ++++++++++++++++++
.../selftests/bpf/progs/tracing_link_ctx.c | 17 +
2 files changed, 344 insertions(+)
create mode 100644 tools/testing/selftests/bpf/prog_tests/tracing_link.c
create mode 100644 tools/testing/selftests/bpf/progs/tracing_link_ctx.c
diff --git a/tools/testing/selftests/bpf/prog_tests/tracing_link.c b/tools/testing/selftests/bpf/prog_tests/tracing_link.c
new file mode 100644
index 000000000000..28154fdd2f0c
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/tracing_link.c
@@ -0,0 +1,327 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <test_progs.h>
+#include <bpf/btf.h>
+#include "fentry_recursive.skel.h"
+#include "priv_freplace_prog.skel.h"
+#include "xdp_dummy.skel.h"
+#include "kprobe_write_ctx.skel.h"
+#include "tracing_link_ctx.skel.h"
+
+static void test_fentry_link_create_log(int target_fd, __u32 target_btf_id, int cookie,
+ enum bpf_attach_type attach_type,
+ bool reattach, const char *msg)
+{
+ struct fentry_recursive *skel = NULL;
+ struct xdp_dummy *target;
+ char log_buf[128] = {};
+ int err, fd, prog_fd;
+ LIBBPF_OPTS(bpf_log_opts, log_opts,
+ .buf = log_buf,
+ .size = sizeof(log_buf),
+ .level = 1,
+ );
+ LIBBPF_OPTS(bpf_link_create_opts, opts,
+ .log_opts = &log_opts,
+ .target_btf_id = target_btf_id,
+ .tracing.cookie = cookie,
+ );
+
+ target = xdp_dummy__open_and_load();
+ if (!ASSERT_OK_PTR(target, "xdp_dummy__open_and_load"))
+ return;
+
+ skel = fentry_recursive__open();
+ if (!ASSERT_OK_PTR(skel, "fentry_recursive__open"))
+ goto out;
+
+ err = bpf_program__set_attach_target(skel->progs.recursive_attach,
+ bpf_program__fd(target->progs.xdp_dummy_prog),
+ "xdp_dummy_prog");
+ if (!ASSERT_OK(err, "bpf_program__set_attach_target"))
+ goto out;
+
+ err = fentry_recursive__load(skel);
+ if (!ASSERT_OK(err, "fentry_recursive__load"))
+ goto out;
+
+ prog_fd = bpf_program__fd(skel->progs.recursive_attach);
+ if (reattach) {
+ /* Consume the initial target saved when the program was loaded. */
+ fd = bpf_link_create(prog_fd, 0, BPF_TRACE_FENTRY, &opts);
+ if (!ASSERT_GE(fd, 0, "bpf_link_create first"))
+ goto out;
+
+ close(fd);
+ memset(log_buf, 0, sizeof(log_buf));
+ log_opts.true_size = 0;
+ }
+
+ fd = bpf_link_create(prog_fd, target_fd, attach_type, &opts);
+ if (!ASSERT_LT(fd, 0, "bpf_link_create")) {
+ close(fd);
+ goto out;
+ }
+
+ ASSERT_EQ(fd, -EINVAL, "err");
+ ASSERT_STREQ(log_buf, msg, "log_buf");
+ ASSERT_EQ(log_opts.true_size, strlen(msg) + 1, "log_size");
+
+out:
+ fentry_recursive__destroy(skel);
+ xdp_dummy__destroy(target);
+}
+
+static void test_log_invalid_attach_type(void)
+{
+ const char *msg = "Invalid tracing attach_type.\n";
+
+ test_fentry_link_create_log(0, 0, 0, BPF_TRACE_FEXIT, false, msg);
+}
+
+static void test_log_tgt_prog_fd_only(void)
+{
+ const char *msg = "tgt_prog_fd and btf_id must be both set or both unset.\n";
+
+ test_fentry_link_create_log(-1, 0, 1, BPF_TRACE_FENTRY, false, msg);
+}
+
+static void test_log_tgt_prog_fd_disallowed(void)
+{
+ const char *msg = "tgt_prog_fd is only allowed for freplace prog.\n";
+
+ test_fentry_link_create_log(-1, 1, 0, BPF_TRACE_FENTRY, false, msg);
+}
+
+static void test_log_invalid_attach_btf(void)
+{
+ const char *msg = "Invalid attach_btf.\n";
+
+ test_fentry_link_create_log(0, 0, 1, BPF_TRACE_FENTRY, true, msg);
+}
+
+static void test_freplace_link_create_log(int target_fd, __u32 target_btf_id, int err_exp,
+ bool reattach, const char *msg)
+{
+ struct priv_freplace_prog *skel = NULL;
+ char log_buf[256] = {};
+ struct xdp_dummy *xdp;
+ int err, fd, prog_fd;
+ LIBBPF_OPTS(bpf_log_opts, log_opts,
+ .buf = log_buf,
+ .size = sizeof(log_buf),
+ .level = 1,
+ );
+ LIBBPF_OPTS(bpf_link_create_opts, opts,
+ .log_opts = &log_opts,
+ .target_btf_id = target_btf_id,
+ );
+
+ xdp = xdp_dummy__open_and_load();
+ if (!ASSERT_OK_PTR(xdp, "xdp_dummy__open_and_load"))
+ return;
+
+ skel = priv_freplace_prog__open();
+ if (!ASSERT_OK_PTR(skel, "priv_freplace_prog__open"))
+ goto out;
+
+ err = bpf_program__set_attach_target(skel->progs.new_xdp_prog2,
+ bpf_program__fd(xdp->progs.xdp_dummy_prog),
+ "xdp_dummy_prog");
+ if (!ASSERT_OK(err, "bpf_program__set_attach_target"))
+ goto out;
+
+ err = priv_freplace_prog__load(skel);
+ if (!ASSERT_OK(err, "priv_freplace_prog__load"))
+ goto out;
+
+ prog_fd = bpf_program__fd(skel->progs.new_xdp_prog2);
+ if (reattach) {
+ /* Consume the initial target saved when the program was loaded. */
+ fd = bpf_link_create(prog_fd, 0, 0, &opts);
+ if (!ASSERT_GE(fd, 0, "bpf_link_create first"))
+ goto out;
+
+ close(fd);
+ memset(log_buf, 0, sizeof(log_buf));
+ log_opts.true_size = 0;
+ }
+
+ if (!target_fd && target_btf_id)
+ target_fd = bpf_program__fd(xdp->progs.xdp_dummy_prog);
+
+ fd = bpf_link_create(prog_fd, target_fd, 0, &opts);
+ if (!ASSERT_LT(fd, 0, "bpf_link_create")) {
+ close(fd);
+ goto out;
+ }
+
+ ASSERT_EQ(fd, err_exp, "err");
+ ASSERT_STREQ(log_buf, msg, "log_buf");
+ ASSERT_EQ(log_opts.true_size, strlen(msg) + 1, "log_size");
+
+out:
+ priv_freplace_prog__destroy(skel);
+ xdp_dummy__destroy(xdp);
+}
+
+static void test_log_invalid_target_fd(void)
+{
+ const char *msg = "Invalid tgt_prog_fd.\n";
+
+ test_freplace_link_create_log(-1, 1, -EBADF, false, msg);
+}
+
+static void test_log_reattach_without_target(void)
+{
+ const char *msg = "Only allow re-attach for tracing and lsm progs.\n";
+
+ test_freplace_link_create_log(0, 0, -EINVAL, true, msg);
+}
+
+static void test_log_invalid_target_btf_id(void)
+{
+ const char *msg = "attach_btf_id 4294967295 is invalid\n";
+
+ test_freplace_link_create_log(0, ~0U, -EINVAL, false, msg);
+}
+
+#ifdef __x86_64__
+static void test_log_invalid_kprobe_write_ctx(void)
+{
+ struct kprobe_write_ctx *skel_kprobe, *skel_freplace = NULL;
+ struct bpf_program *prog_kprobe, *prog_freplace;
+ const char *msg = "Invalid kprobe_write_ctx.\n";
+ int err, prog_fd, btf_id, fd;
+ char log_buf[128] = {};
+ LIBBPF_OPTS(bpf_log_opts, log_opts,
+ .buf = log_buf,
+ .size = sizeof(log_buf),
+ .level = 1,
+ );
+ LIBBPF_OPTS(bpf_link_create_opts, link_opts,
+ .log_opts = &log_opts,
+ );
+
+ skel_kprobe = kprobe_write_ctx__open();
+ if (!ASSERT_OK_PTR(skel_kprobe, "kprobe_write_ctx__open kprobe"))
+ return;
+
+ prog_kprobe = skel_kprobe->progs.kprobe_dummy;
+ bpf_program__set_autoload(prog_kprobe, true);
+
+ err = kprobe_write_ctx__load(skel_kprobe);
+ if (!ASSERT_OK(err, "kprobe_write_ctx__load kprobe"))
+ goto out;
+
+ skel_freplace = kprobe_write_ctx__open();
+ if (!ASSERT_OK_PTR(skel_freplace, "kprobe_write_ctx__open freplace"))
+ goto out;
+
+ prog_freplace = skel_freplace->progs.freplace_kprobe;
+ bpf_program__set_autoload(prog_freplace, true);
+
+ prog_fd = bpf_program__fd(skel_kprobe->progs.kprobe_write_ctx);
+ bpf_program__set_attach_target(prog_freplace, prog_fd, "kprobe_write_ctx");
+
+ err = kprobe_write_ctx__load(skel_freplace);
+ if (!ASSERT_OK(err, "kprobe_write_ctx__load freplace"))
+ goto out;
+
+ prog_fd = bpf_program__fd(prog_kprobe);
+ btf_id = btf__find_by_name_kind(bpf_object__btf(skel_kprobe->obj), "kprobe_dummy",
+ BTF_KIND_FUNC);
+ if (!ASSERT_GT(btf_id, 0, "btf__find_by_name_kind"))
+ goto out;
+
+ link_opts.target_btf_id = btf_id;
+ fd = bpf_link_create(bpf_program__fd(prog_freplace), prog_fd, 0, &link_opts);
+ if (!ASSERT_LT(fd, 0, "bpf_link_create")) {
+ close(fd);
+ goto out;
+ }
+
+ ASSERT_EQ(fd, -EINVAL, "err");
+ ASSERT_STREQ(log_buf, msg, "log_buf");
+ ASSERT_EQ(log_opts.true_size, strlen(msg) + 1, "log_size");
+
+out:
+ kprobe_write_ctx__destroy(skel_freplace);
+ kprobe_write_ctx__destroy(skel_kprobe);
+}
+#else
+static void test_log_invalid_kprobe_write_ctx(void)
+{
+ test__skip();
+}
+#endif
+
+static void test_log_freplace_ctx_size(void)
+{
+ const char *msg = "Freplace prog accesses more ctx than its target.\n";
+ struct tracing_link_ctx *target, *skel = NULL;
+ char log_buf[128] = {};
+ int err, fd;
+ LIBBPF_OPTS(bpf_log_opts, log_opts,
+ .buf = log_buf,
+ .size = sizeof(log_buf),
+ .level = 1,
+ );
+ LIBBPF_OPTS(bpf_link_create_opts, opts,
+ .log_opts = &log_opts,
+ );
+
+ target = tracing_link_ctx__open_and_load();
+ if (!ASSERT_OK_PTR(target, "tracing_link_ctx__open_and_load"))
+ return;
+
+ skel = tracing_link_ctx__open();
+ if (!ASSERT_OK_PTR(skel, "tracing_link_ctx__open"))
+ goto out;
+
+ bpf_program__set_autoload(skel->progs.target, false);
+ bpf_program__set_autoload(skel->progs.replacement, true);
+ err = bpf_program__set_attach_target(skel->progs.replacement,
+ bpf_program__fd(target->progs.target), "target");
+ if (!ASSERT_OK(err, "bpf_program__set_attach_target"))
+ goto out;
+
+ err = tracing_link_ctx__load(skel);
+ if (!ASSERT_OK(err, "tracing_link_ctx__load"))
+ goto out;
+
+ fd = bpf_link_create(bpf_program__fd(skel->progs.replacement), 0, 0, &opts);
+ if (!ASSERT_LT(fd, 0, "bpf_link_create")) {
+ close(fd);
+ goto out;
+ }
+
+ ASSERT_EQ(fd, -EINVAL, "err");
+ ASSERT_STREQ(log_buf, msg, "log_buf");
+ ASSERT_EQ(log_opts.true_size, strlen(msg) + 1, "log_size");
+
+out:
+ tracing_link_ctx__destroy(skel);
+ tracing_link_ctx__destroy(target);
+}
+
+void test_tracing_link_log(void)
+{
+ if (test__start_subtest("invalid_attach_type"))
+ test_log_invalid_attach_type();
+ if (test__start_subtest("tgt_prog_fd_only"))
+ test_log_tgt_prog_fd_only();
+ if (test__start_subtest("tgt_prog_fd_disallowed"))
+ test_log_tgt_prog_fd_disallowed();
+ if (test__start_subtest("invalid_attach_btf"))
+ test_log_invalid_attach_btf();
+ if (test__start_subtest("invalid_target_fd"))
+ test_log_invalid_target_fd();
+ if (test__start_subtest("reattach_without_target"))
+ test_log_reattach_without_target();
+ if (test__start_subtest("invalid_target_btf_id"))
+ test_log_invalid_target_btf_id();
+ if (test__start_subtest("invalid_kprobe_write_ctx"))
+ test_log_invalid_kprobe_write_ctx();
+ if (test__start_subtest("freplace_ctx_size"))
+ test_log_freplace_ctx_size();
+}
diff --git a/tools/testing/selftests/bpf/progs/tracing_link_ctx.c b/tools/testing/selftests/bpf/progs/tracing_link_ctx.c
new file mode 100644
index 000000000000..940a7d74b7e3
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/tracing_link_ctx.c
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+
+SEC("tracepoint")
+int target(struct trace_event_raw_sys_enter *ctx __arg_ctx)
+{
+ return 0;
+}
+
+SEC("?freplace")
+int replacement(struct trace_event_raw_sys_enter *ctx __arg_ctx)
+{
+ return ctx->id;
+}
+
+char LICENSE[] SEC("license") = "GPL";
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH bpf-next v2 7/9] selftests/bpf: Verify tracing_multi link_create logs
2026-11-14 15:06 [PATCH bpf-next v2 0/9] bpf: Add log support for tracing link_create Leon Hwang
` (5 preceding siblings ...)
2026-11-14 15:06 ` [PATCH bpf-next v2 6/9] selftests/bpf: Verify tracing link_create logs Leon Hwang
@ 2026-11-14 15:06 ` Leon Hwang
2026-11-14 15:06 ` [PATCH bpf-next v2 8/9] selftests/bpf: Verify iter " Leon Hwang
2026-11-14 15:06 ` [PATCH bpf-next v2 9/9] selftests/bpf: Verify cgroup prog " Leon Hwang
8 siblings, 0 replies; 10+ messages in thread
From: Leon Hwang @ 2026-11-14 15:06 UTC (permalink / raw)
To: bpf
Cc: Alexei Starovoitov, Daniel Borkmann, John Fastabend,
Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
Emil Tsalapatis, Ihor Solodrai, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, KP Singh, Matt Bobrowski, Shuah Khan,
Leon Hwang, linux-kernel, linux-trace-kernel, linux-kselftest,
netdev
Check the error code, log message, and reported log size for a zero or
excessive count, missing IDs, invalid flags, and a nonzero target FD.
Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
---
.../selftests/bpf/prog_tests/tracing_link.c | 102 ++++++++++++++++++
1 file changed, 102 insertions(+)
diff --git a/tools/testing/selftests/bpf/prog_tests/tracing_link.c b/tools/testing/selftests/bpf/prog_tests/tracing_link.c
index 28154fdd2f0c..49fd62478438 100644
--- a/tools/testing/selftests/bpf/prog_tests/tracing_link.c
+++ b/tools/testing/selftests/bpf/prog_tests/tracing_link.c
@@ -6,6 +6,7 @@
#include "xdp_dummy.skel.h"
#include "kprobe_write_ctx.skel.h"
#include "tracing_link_ctx.skel.h"
+#include "tracing_multi_fail.skel.h"
static void test_fentry_link_create_log(int target_fd, __u32 target_btf_id, int cookie,
enum bpf_attach_type attach_type,
@@ -325,3 +326,104 @@ void test_tracing_link_log(void)
if (test__start_subtest("freplace_ctx_size"))
test_log_freplace_ctx_size();
}
+
+static void test_tracing_multi_link_create_log(int target_fd, __u32 flags, const __u32 *ids,
+ __u32 cnt, int err_exp, const char *msg)
+{
+ struct tracing_multi_fail *skel;
+ char log_buf[128] = {};
+ int err, fd, prog_fd;
+ LIBBPF_OPTS(bpf_log_opts, log_opts,
+ .buf = log_buf,
+ .size = sizeof(log_buf),
+ .level = 1,
+ );
+ LIBBPF_OPTS(bpf_link_create_opts, opts,
+ .log_opts = &log_opts,
+ .flags = flags,
+ .tracing_multi.ids = ids,
+ .tracing_multi.cnt = cnt,
+ );
+
+ skel = tracing_multi_fail__open();
+ if (!ASSERT_OK_PTR(skel, "tracing_multi_fail__open"))
+ return;
+
+ bpf_program__set_autoload(skel->progs.test_fentry_s, false);
+ err = tracing_multi_fail__load(skel);
+ if (!ASSERT_OK(err, "tracing_multi_fail__load"))
+ goto out;
+
+ prog_fd = bpf_program__fd(skel->progs.test_fentry);
+ fd = bpf_link_create(prog_fd, target_fd, BPF_TRACE_FENTRY_MULTI, &opts);
+ if (!ASSERT_LT(fd, 0, "bpf_link_create")) {
+ close(fd);
+ goto out;
+ }
+
+ ASSERT_EQ(fd, err_exp, "err");
+ ASSERT_STREQ(log_buf, msg, "log_buf");
+ ASSERT_EQ(log_opts.true_size, strlen(msg) + 1, "log_size");
+
+out:
+ tracing_multi_fail__destroy(skel);
+}
+
+static void test_multi_log_zero_count(void)
+{
+ const char *msg = "tracing_multi.cnt cannot be zero.\n";
+ __u32 id = 0;
+
+ test_tracing_multi_link_create_log(0, 0, &id, 0, -EINVAL, msg);
+}
+
+static void test_multi_log_missing_ids(void)
+{
+ const char *msg = "tracing_multi.ids must be set.\n";
+
+ test_tracing_multi_link_create_log(0, 0, NULL, 1, -EINVAL, msg);
+}
+
+static void test_multi_log_excessive_count(void)
+{
+ const char *msg = "tracing_multi.cnt is too large.\n";
+ __u32 id = 0;
+
+ test_tracing_multi_link_create_log(0, 0, &id, INT_MAX, -E2BIG, msg);
+}
+
+static void test_multi_log_invalid_flags(void)
+{
+ const char *msg = "Invalid flags.\n";
+ __u32 id = 0;
+
+ test_tracing_multi_link_create_log(0, 1, &id, 1, -EINVAL, msg);
+}
+
+static void test_multi_log_invalid_target_fd(void)
+{
+ const char *msg = "Invalid target_fd.\n";
+ __u32 id = 0;
+
+ test_tracing_multi_link_create_log(-1, 0, &id, 1, -EINVAL, msg);
+}
+
+void test_tracing_multi_link_log(void)
+{
+#ifndef __x86_64__
+ test__skip();
+ return;
+#endif
+
+ if (test__start_subtest("zero_count"))
+ test_multi_log_zero_count();
+ if (test__start_subtest("missing_ids"))
+ test_multi_log_missing_ids();
+ if (test__start_subtest("excessive_count"))
+ test_multi_log_excessive_count();
+ if (test__start_subtest("invalid_flags"))
+ test_multi_log_invalid_flags();
+ if (test__start_subtest("invalid_target_fd"))
+ test_multi_log_invalid_target_fd();
+}
+
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH bpf-next v2 8/9] selftests/bpf: Verify iter link_create logs
2026-11-14 15:06 [PATCH bpf-next v2 0/9] bpf: Add log support for tracing link_create Leon Hwang
` (6 preceding siblings ...)
2026-11-14 15:06 ` [PATCH bpf-next v2 7/9] selftests/bpf: Verify tracing_multi " Leon Hwang
@ 2026-11-14 15:06 ` Leon Hwang
2026-11-14 15:06 ` [PATCH bpf-next v2 9/9] selftests/bpf: Verify cgroup prog " Leon Hwang
8 siblings, 0 replies; 10+ messages in thread
From: Leon Hwang @ 2026-11-14 15:06 UTC (permalink / raw)
To: bpf
Cc: Alexei Starovoitov, Daniel Borkmann, John Fastabend,
Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
Emil Tsalapatis, Ihor Solodrai, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, KP Singh, Matt Bobrowski, Shuah Khan,
Leon Hwang, linux-kernel, linux-trace-kernel, linux-kselftest,
netdev
Check the error code, log message, and reported log size for invalid target
FDs, flags, and iterator information. Also cover attaching a sleepable
program to an iterator that does not support it.
Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
---
.../selftests/bpf/prog_tests/iter_link.c | 156 ++++++++++++++++++
1 file changed, 156 insertions(+)
create mode 100644 tools/testing/selftests/bpf/prog_tests/iter_link.c
diff --git a/tools/testing/selftests/bpf/prog_tests/iter_link.c b/tools/testing/selftests/bpf/prog_tests/iter_link.c
new file mode 100644
index 000000000000..623a84ba612d
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/iter_link.c
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <test_progs.h>
+#include "bpf_iter_bpf_map.skel.h"
+
+static void test_iter_link_create_log(int target_fd, struct bpf_link_create_opts *opts,
+ __u64 flags, int err_exp, const char *msg)
+{
+ struct bpf_iter_bpf_map *skel;
+ char log_buf[128] = {};
+ int err, fd, prog_fd;
+ LIBBPF_OPTS(bpf_log_opts, log_opts,
+ .buf = log_buf,
+ .size = sizeof(log_buf),
+ .level = 1,
+ );
+
+ skel = bpf_iter_bpf_map__open();
+ if (!ASSERT_OK_PTR(skel, "bpf_iter_bpf_map__open"))
+ return;
+
+ err = bpf_program__set_flags(skel->progs.dump_bpf_map, flags);
+ if (!ASSERT_OK(err, "bpf_program__set_flags"))
+ goto out;
+
+ err = bpf_iter_bpf_map__load(skel);
+ if (!ASSERT_OK(err, "bpf_iter_bpf_map__load"))
+ goto out;
+
+ opts->log_opts = &log_opts;
+ prog_fd = bpf_program__fd(skel->progs.dump_bpf_map);
+ fd = bpf_link_create(prog_fd, target_fd, BPF_TRACE_ITER, opts);
+ if (!ASSERT_LT(fd, 0, "bpf_link_create")) {
+ close(fd);
+ goto out;
+ }
+
+ ASSERT_EQ(fd, err_exp, "err");
+ ASSERT_STREQ(log_buf, msg, "log_buf");
+ ASSERT_EQ(log_opts.true_size, strlen(msg) + 1, "log_size");
+
+out:
+ bpf_iter_bpf_map__destroy(skel);
+}
+
+static void test_log_invalid_target_fd(void)
+{
+ const char *msg = "Invalid target_fd.\n";
+ LIBBPF_OPTS(bpf_link_create_opts, opts);
+
+ test_iter_link_create_log(-1, &opts, 0, -EINVAL, msg);
+}
+
+static void test_log_invalid_flags(void)
+{
+ const char *msg = "Invalid flags.\n";
+ LIBBPF_OPTS(bpf_link_create_opts, opts,
+ .flags = 1,
+ );
+
+ test_iter_link_create_log(0, &opts, 0, -EINVAL, msg);
+}
+
+static void test_log_iter_info_only(void)
+{
+ const char *msg = "iter_info and iter_info_len must be both set or both unset.\n";
+ union bpf_iter_link_info info = {};
+ LIBBPF_OPTS(bpf_link_create_opts, opts,
+ .iter.info = &info,
+ );
+
+ test_iter_link_create_log(0, &opts, 0, -EINVAL, msg);
+}
+
+static void test_log_iter_info_len_only(void)
+{
+ const char *msg = "iter_info and iter_info_len must be both set or both unset.\n";
+ LIBBPF_OPTS(bpf_link_create_opts, opts,
+ .iter.len = sizeof(union bpf_iter_link_info),
+ );
+
+ test_iter_link_create_log(0, &opts, 0, -EINVAL, msg);
+}
+
+static void test_log_invalid_iter_info(void)
+{
+ unsigned char info[sizeof(union bpf_iter_link_info) + 1] = {};
+ const char *msg = "Invalid iter_info_len.\n";
+ struct bpf_iter_bpf_map *skel;
+ struct bpf_link *link;
+ char log_buf[128] = {};
+ LIBBPF_OPTS(bpf_log_opts, log_opts,
+ .buf = log_buf,
+ .size = sizeof(log_buf),
+ .level = 1,
+ );
+ LIBBPF_OPTS(bpf_iter_attach_opts, opts,
+ .link_info = (union bpf_iter_link_info *)info,
+ .link_info_len = sizeof(info),
+ .log_opts = &log_opts,
+ );
+
+ skel = bpf_iter_bpf_map__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "bpf_iter_bpf_map__open_and_load"))
+ return;
+
+ info[sizeof(info) - 1] = 1;
+ link = bpf_program__attach_iter(skel->progs.dump_bpf_map, &opts);
+ if (!ASSERT_ERR_PTR(link, "bpf_program__attach_iter")) {
+ bpf_link__destroy(link);
+ goto out;
+ }
+
+ ASSERT_EQ(libbpf_get_error(link), -E2BIG, "err");
+ ASSERT_STREQ(log_buf, msg, "log_buf");
+ ASSERT_EQ(log_opts.true_size, strlen(msg) + 1, "log_size");
+
+out:
+ bpf_iter_bpf_map__destroy(skel);
+}
+
+static void test_log_invalid_iter_info_len(void)
+{
+ const char *msg = "Invalid iter_info.\n";
+ LIBBPF_OPTS(bpf_link_create_opts, opts,
+ .iter.info = (union bpf_iter_link_info *)1,
+ .iter.len = sizeof(union bpf_iter_link_info),
+ );
+
+ test_iter_link_create_log(0, &opts, 0, -EFAULT, msg);
+}
+
+static void test_log_invalid_prog(void)
+{
+ const char *msg = "The iter target does not support the bpf prog.\n";
+ LIBBPF_OPTS(bpf_link_create_opts, opts);
+
+ test_iter_link_create_log(0, &opts, BPF_F_SLEEPABLE, -EINVAL, msg);
+}
+
+void test_iter_link_log(void)
+{
+ if (test__start_subtest("invalid_target_fd"))
+ test_log_invalid_target_fd();
+ if (test__start_subtest("invalid_flags"))
+ test_log_invalid_flags();
+ if (test__start_subtest("iter_info_only"))
+ test_log_iter_info_only();
+ if (test__start_subtest("iter_info_len_only"))
+ test_log_iter_info_len_only();
+ if (test__start_subtest("invalid_iter_info"))
+ test_log_invalid_iter_info();
+ if (test__start_subtest("invalid_iter_info_len"))
+ test_log_invalid_iter_info_len();
+ if (test__start_subtest("invalid_prog"))
+ test_log_invalid_prog();
+}
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH bpf-next v2 9/9] selftests/bpf: Verify cgroup prog link_create logs
2026-11-14 15:06 [PATCH bpf-next v2 0/9] bpf: Add log support for tracing link_create Leon Hwang
` (7 preceding siblings ...)
2026-11-14 15:06 ` [PATCH bpf-next v2 8/9] selftests/bpf: Verify iter " Leon Hwang
@ 2026-11-14 15:06 ` Leon Hwang
8 siblings, 0 replies; 10+ messages in thread
From: Leon Hwang @ 2026-11-14 15:06 UTC (permalink / raw)
To: bpf
Cc: Alexei Starovoitov, Daniel Borkmann, John Fastabend,
Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
Emil Tsalapatis, Ihor Solodrai, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, KP Singh, Matt Bobrowski, Shuah Khan,
Leon Hwang, linux-kernel, linux-trace-kernel, linux-kselftest,
netdev
Check the error code, log message, and reported log size for unsupported
flags, invalid target FDs, and relative attachment errors. Cover missing
or conflicting ordering flags, a missing BPF_F_LINK flag, invalid link FDs
and IDs, preorder mismatches, and anchors outside the target cgroup.
Also cover revision mismatches, conflicting attachment flags, cgroup
hierarchy restrictions, and the per-cgroup program limit.
Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
---
.../selftests/bpf/prog_tests/cgroup_link.c | 324 ++++++++++++++++++
1 file changed, 324 insertions(+)
diff --git a/tools/testing/selftests/bpf/prog_tests/cgroup_link.c b/tools/testing/selftests/bpf/prog_tests/cgroup_link.c
index 15093a69510e..5a27d3a6a26c 100644
--- a/tools/testing/selftests/bpf/prog_tests/cgroup_link.c
+++ b/tools/testing/selftests/bpf/prog_tests/cgroup_link.c
@@ -253,3 +253,327 @@ void serial_test_cgroup_link(void)
}
cleanup_cgroup_environment();
}
+
+static void check_cgroup_link_create_log(int prog_fd, int target_fd,
+ struct bpf_link_create_opts *opts,
+ int err_exp, const char *msg)
+{
+ char log_buf[128] = {};
+ LIBBPF_OPTS(bpf_log_opts, log_opts,
+ .buf = log_buf,
+ .size = sizeof(log_buf),
+ .level = 1,
+ );
+ int fd;
+
+ opts->log_opts = &log_opts;
+ fd = bpf_link_create(prog_fd, target_fd, BPF_CGROUP_INET_EGRESS, opts);
+ if (!ASSERT_LT(fd, 0, "bpf_link_create")) {
+ close(fd);
+ return;
+ }
+
+ ASSERT_EQ(fd, err_exp, "err");
+ ASSERT_STREQ(log_buf, msg, "log_buf");
+ ASSERT_EQ(log_opts.true_size, strlen(msg) + 1, "log_size");
+}
+
+static void test_cgroup_link_create_log(int target_fd, __u32 flags, int err_exp, const char *msg)
+{
+ LIBBPF_OPTS(bpf_link_create_opts, opts,
+ .flags = flags,
+ );
+ struct test_cgroup_link *skel;
+
+ skel = test_cgroup_link__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "test_cgroup_link__open_and_load"))
+ return;
+
+ check_cgroup_link_create_log(bpf_program__fd(skel->progs.egress), target_fd, &opts,
+ err_exp, msg);
+
+ test_cgroup_link__destroy(skel);
+}
+
+static void test_log_invalid_flags(void)
+{
+ const char *msg = "Invalid flags.\n";
+
+ test_cgroup_link_create_log(-1, BPF_F_ALLOW_OVERRIDE, -EINVAL, msg);
+}
+
+static void test_log_invalid_target_fd(void)
+{
+ const char *msg = "Invalid target_fd.\n";
+
+ test_cgroup_link_create_log(-1, 0, -EBADF, msg);
+}
+
+static void test_log_cgroup_attach(struct bpf_link_create_opts *opts,
+ int err_exp, const char *msg)
+{
+ struct test_cgroup_link *skel;
+ int cg;
+
+ cg = test__join_cgroup("/cgroup_link_log_attach");
+ if (!ASSERT_GE(cg, 0, "join_cgroup"))
+ return;
+
+ skel = test_cgroup_link__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "test_cgroup_link__open_and_load"))
+ goto out;
+
+ check_cgroup_link_create_log(bpf_program__fd(skel->progs.egress), cg, opts, err_exp, msg);
+ test_cgroup_link__destroy(skel);
+
+out:
+ close(cg);
+}
+
+static void test_log_relative_without_order(void)
+{
+ const char *msg = "Specify exactly one of BPF_F_BEFORE or BPF_F_AFTER.\n";
+ LIBBPF_OPTS(bpf_link_create_opts, opts,
+ .flags = BPF_F_LINK,
+ );
+
+ test_log_cgroup_attach(&opts, -EINVAL, msg);
+}
+
+static void test_log_relative_both_orders(void)
+{
+ const char *msg = "Specify exactly one of BPF_F_BEFORE or BPF_F_AFTER.\n";
+ LIBBPF_OPTS(bpf_link_create_opts, opts,
+ .flags = BPF_F_LINK | BPF_F_BEFORE | BPF_F_AFTER,
+ );
+
+ test_log_cgroup_attach(&opts, -EINVAL, msg);
+}
+
+static void test_log_relative_type_mismatch(void)
+{
+ const char *msg = "BPF_F_LINK is required for relative link attachment.\n";
+ LIBBPF_OPTS(bpf_link_create_opts, opts,
+ .flags = BPF_F_BEFORE,
+ .cgroup.relative_fd = -1,
+ );
+
+ test_log_cgroup_attach(&opts, -EINVAL, msg);
+}
+
+static void test_log_invalid_relative_link_fd(void)
+{
+ const char *msg = "Invalid relative link FD.\n";
+ LIBBPF_OPTS(bpf_link_create_opts, opts,
+ .flags = BPF_F_LINK | BPF_F_BEFORE,
+ .cgroup.relative_fd = -1,
+ );
+
+ test_log_cgroup_attach(&opts, -EBADF, msg);
+}
+
+static void test_log_invalid_relative_link_id(void)
+{
+ const char *msg = "Invalid relative link ID.\n";
+ /* Link ID zero cannot identify a link. */
+ LIBBPF_OPTS(bpf_link_create_opts, opts,
+ .flags = BPF_F_LINK | BPF_F_ID | BPF_F_BEFORE,
+ );
+
+ test_log_cgroup_attach(&opts, -ENOENT, msg);
+}
+
+static void test_log_relative_anchor(__u32 flags, bool other_cgroup, int err_exp, const char *msg)
+{
+ int cg, other_cg = -1, anchor_fd = -1, prog_fd;
+ LIBBPF_OPTS(bpf_link_create_opts, opts,
+ .flags = flags,
+ );
+ struct test_cgroup_link *skel;
+
+ cg = test__join_cgroup("/cgroup_link_log_relative");
+ if (!ASSERT_GE(cg, 0, "join_cgroup"))
+ return;
+
+ if (other_cgroup) {
+ other_cg = test__join_cgroup("/cgroup_link_log_other");
+ if (!ASSERT_GE(other_cg, 0, "join_cgroup other"))
+ goto cleanup;
+ }
+
+ skel = test_cgroup_link__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "test_cgroup_link__open_and_load"))
+ goto cleanup;
+
+ prog_fd = bpf_program__fd(skel->progs.egress);
+ anchor_fd = bpf_link_create(prog_fd, cg, BPF_CGROUP_INET_EGRESS, NULL);
+ if (!ASSERT_GE(anchor_fd, 0, "bpf_link_create"))
+ goto destroy;
+
+ if (flags & BPF_F_LINK)
+ opts.cgroup.relative_fd = anchor_fd;
+ check_cgroup_link_create_log(prog_fd, other_cgroup ? other_cg : cg, &opts, err_exp, msg);
+
+ close(anchor_fd);
+
+destroy:
+ test_cgroup_link__destroy(skel);
+cleanup:
+ if (other_cg >= 0)
+ close(other_cg);
+ close(cg);
+}
+
+static void test_log_both_orders(void)
+{
+ const char *msg = "BPF_F_BEFORE and BPF_F_AFTER are mutually exclusive.\n";
+
+ test_log_relative_anchor(BPF_F_BEFORE | BPF_F_AFTER, false, -EINVAL, msg);
+}
+
+static void test_log_relative_preorder_mismatch(void)
+{
+ const char *msg = "Relative attachment BPF_F_PREORDER mismatch.\n";
+
+ test_log_relative_anchor(BPF_F_LINK | BPF_F_BEFORE | BPF_F_PREORDER, false, -EINVAL, msg);
+}
+
+static void test_log_relative_not_in_cgroup(void)
+{
+ const char *msg = "Relative attachment not found in target cgroup.\n";
+
+ test_log_relative_anchor(BPF_F_LINK | BPF_F_BEFORE, true, -ENOENT, msg);
+}
+
+static void test_log_revision_mismatch(void)
+{
+ const char *msg = "Cgroup revision mismatch: expected 2, actual 1.\n";
+ LIBBPF_OPTS(bpf_link_create_opts, opts,
+ .cgroup.expected_revision = 2,
+ );
+
+ test_log_cgroup_attach(&opts, -ESTALE, msg);
+}
+
+static void test_log_exclusive_attach(bool child, const char *msg)
+{
+ LIBBPF_OPTS(bpf_link_create_opts, opts);
+ int cg, child_cg = -1, prog_fd, err;
+ struct test_cgroup_link *skel;
+
+ cg = test__join_cgroup("/cgroup_link_log_exclusive");
+ if (!ASSERT_GE(cg, 0, "join_cgroup"))
+ return;
+
+ if (child) {
+ child_cg = test__join_cgroup("/cgroup_link_log_exclusive/child");
+ if (!ASSERT_GE(child_cg, 0, "join_child_cgroup"))
+ goto cleanup;
+ }
+
+ skel = test_cgroup_link__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "test_cgroup_link__open_and_load"))
+ goto cleanup;
+
+ prog_fd = bpf_program__fd(skel->progs.egress);
+
+ err = bpf_prog_attach(prog_fd, cg, BPF_CGROUP_INET_EGRESS, 0);
+ if (!ASSERT_OK(err, "bpf_prog_attach"))
+ goto destroy;
+
+ check_cgroup_link_create_log(prog_fd, child ? child_cg : cg, &opts, -EPERM, msg);
+
+ err = bpf_prog_detach2(prog_fd, cg, BPF_CGROUP_INET_EGRESS);
+ ASSERT_OK(err, "bpf_prog_detach2");
+
+destroy:
+ test_cgroup_link__destroy(skel);
+cleanup:
+ if (child_cg >= 0)
+ close(child_cg);
+ close(cg);
+}
+
+static void test_log_attachment_flags_conflict(void)
+{
+ const char *msg = "Attachment flags conflict with existing flags.\n";
+
+ test_log_exclusive_attach(false, msg);
+}
+
+static void test_log_hierarchy_disallows_attach(void)
+{
+ const char *msg = "Cgroup hierarchy does not allow attachment.\n";
+
+ test_log_exclusive_attach(true, msg);
+}
+
+#define BPF_CGROUP_MAX_PROGS 64
+
+static void test_log_too_many_programs(void)
+{
+ const char *msg = "Too many programs attached to cgroup (limit 64).\n";
+ LIBBPF_OPTS(bpf_link_create_opts, opts);
+ int cg, prog_fd, err, i, nr_links = 0;
+ int link_fds[BPF_CGROUP_MAX_PROGS];
+ struct test_cgroup_link *skel;
+
+ cg = test__join_cgroup("/cgroup_link_log_limit");
+ if (!ASSERT_GE(cg, 0, "join_cgroup"))
+ return;
+
+ skel = test_cgroup_link__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "test_cgroup_link__open_and_load"))
+ goto cleanup;
+
+ prog_fd = bpf_program__fd(skel->progs.egress);
+
+ for (i = 0; i < ARRAY_SIZE(link_fds); i++) {
+ err = bpf_link_create(prog_fd, cg, BPF_CGROUP_INET_EGRESS, NULL);
+ if (!ASSERT_GE(err, 0, "bpf_link_create"))
+ goto destroy;
+
+ link_fds[nr_links++] = err;
+ }
+
+ check_cgroup_link_create_log(prog_fd, cg, &opts, -E2BIG, msg);
+
+destroy:
+ for (i = 0; i < nr_links; i++)
+ close(link_fds[i]);
+ test_cgroup_link__destroy(skel);
+cleanup:
+ close(cg);
+}
+
+void test_cgroup_link_log(void)
+{
+ if (test__start_subtest("invalid_flags"))
+ test_log_invalid_flags();
+ if (test__start_subtest("invalid_target_fd"))
+ test_log_invalid_target_fd();
+ if (test__start_subtest("relative_without_order"))
+ test_log_relative_without_order();
+ if (test__start_subtest("relative_both_orders"))
+ test_log_relative_both_orders();
+ if (test__start_subtest("relative_type_mismatch"))
+ test_log_relative_type_mismatch();
+ if (test__start_subtest("invalid_relative_link_fd"))
+ test_log_invalid_relative_link_fd();
+ if (test__start_subtest("invalid_relative_link_id"))
+ test_log_invalid_relative_link_id();
+ if (test__start_subtest("both_orders"))
+ test_log_both_orders();
+ if (test__start_subtest("relative_preorder_mismatch"))
+ test_log_relative_preorder_mismatch();
+ if (test__start_subtest("relative_not_in_cgroup"))
+ test_log_relative_not_in_cgroup();
+ if (test__start_subtest("revision_mismatch"))
+ test_log_revision_mismatch();
+ if (test__start_subtest("attachment_flags_conflict"))
+ test_log_attachment_flags_conflict();
+ if (test__start_subtest("hierarchy_disallows_attach"))
+ test_log_hierarchy_disallows_attach();
+ if (test__start_subtest("too_many_programs"))
+ test_log_too_many_programs();
+}
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread