Skip to contentSkip to conversation

bpf: Add log support for tracing link_create

PATCH New Leon Hwang 3 participants 16 messages Nov 9

Subsystems (11)
lore
Share & tools
lore

Leon Hwang

author
#0 lore raw

cover bpf: Add log support for tracing link_create

I'm adding syscall common attrs support for LINK_CREATE cmd.

This is the second series for the plan, that adds syscall common attrs support for LINK_CREATE of tracing prog and cgroup prog.

BTW, address Andrii's review [1] in patch 5:
1. Change if (!OPTS_ZEROED(opts, iter_info_len)) to if (!OPTS_ZEROED(opts, log_opts)).
2. Add comment to iter_info and iter_info_len that they are deprecated, and use iter.info and iter.len instead.

Links:
[1] https://lore.kernel.org/bpf/CAEf4BzbW4zwL-9j8e2B6KNXVNwsfWyMSAo_RsGCqv-nu_yGhTg@mail.gmail.com/

Leon Hwang (9):
bpf: Add log support for tracing link_create
bpf: Add log support for tracing_multi link_create
bpf: Add log support for iter link_create
bpf: Add log support for cgroup prog link_create
libbpf: Add log_opts support for BPF_TRACE_ITER selftests/bpf: Verify tracing link_create logs selftests/bpf: Verify tracing_multi link_create logs selftests/bpf: Verify iter link_create logs selftests/bpf: Verify cgroup prog link_create logs

 include/linux/bpf-cgroup.h                    |   7 +-
 include/linux/bpf.h                           |   3 +-
 include/linux/trace_events.h                  |   7 +-
 kernel/bpf/bpf_iter.c                         |  27 +-
 kernel/bpf/cgroup.c                           |  76 +++-
 kernel/bpf/syscall.c                          |  28 +-
 kernel/trace/bpf_trace.c                      |  26 +-
 tools/lib/bpf/bpf.c                           |  17 +-
 tools/lib/bpf/bpf.h                           |   8 +
 .../selftests/bpf/prog_tests/cgroup_link.c    | 324 +++++++++++++++
 .../selftests/bpf/prog_tests/iter_link.c      | 132 ++++++
 .../selftests/bpf/prog_tests/tracing_link.c   | 377 ++++++++++++++++++
 12 files changed, 979 insertions(+), 53 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/iter_link.c
 create mode 100644 tools/testing/selftests/bpf/prog_tests/tracing_link.c
Signature · 1 line
2.55.0
author
#1 lore raw

PATCH 1/9 bpf: Add log support for tracing link_create

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.

---
 kernel/bpf/syscall.c | 20 +++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)
kernel/bpf/syscall.c +15 -5
index 9004f2673ccf..6147d386b777 100644
@@ -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;
 	}
@@ -4374,7 +4381,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 +5914,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 attach_type.\n");
 			ret = -EINVAL;
 			goto out;
 		}
@@ -5929,7 +5938,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:
Signature · 1 line
2.55.0
#2 lore raw
TL;DR: Logging is incomplete: the freplace max_ctx_offset check in bpf_tracing_prog_attach() still returns -EINVAL with an empty log, and the saved_dst type mismatch may too. No functional regression.
Quoted 1 line

bpf: Add log support for tracing link_create

TL;DR: Logging is incomplete: the freplace max_ctx_offset check in bpf_tracing_prog_attach() still returns -EINVAL with an empty log, and the saved_dst type mismatch may too. No functional regression.

Quoted 3 lines
kernel/bpf/syscall.c +0 -0

[ ... ]

kernel/bpf/syscall.c static int bpf_tracing_prog_attach(struct bpf_prog *prog, · line 3855

Quoted 8 lines
kernel/bpf/syscall.c +1 -0
@@ -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;
 	}

The check right after this one still returns -EINVAL without writing anything to the log:

	if (prog->type == BPF_PROG_TYPE_EXT &&
	    (tgt_prog->type == BPF_PROG_TYPE_RAW_TRACEPOINT ||
	     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) {
		err = -EINVAL;
		goto out_unlock;
	}

An freplace program whose max_ctx_offset is larger than that of a RAW_TRACEPOINT, TRACEPOINT or SYSCALL target will get -EINVAL from link_create with an empty log and log_true_size of 0. That is the same kind of invalid freplace target as the kprobe_write_ctx mismatch above, which now logs. This isn't a functional regression, but was this check missed?

Would something like this cover it?

	bpf_log(log, "Extension accesses more ctx than its target.\n");

Also, bpf_check_attach_target() still returns a bare -EINVAL for the saved_dst_prog_type / saved_dst_attach_type mismatch, which is the "freplace attachment to a new target" case the commit message mentions. That code is in verifier.c, so it is outside this patch, but is it intended to be left without a message?

---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/42633880300

author
#3 lore raw

PATCH 2/9 bpf: Add log support for tracing_multi link_create

In reply to Leon Hwang

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.

---
 include/linux/trace_events.h |  7 +++++--
 kernel/bpf/syscall.c         |  2 +-
 kernel/trace/bpf_trace.c     | 26 +++++++++++++++++++++-----
 3 files changed, 27 insertions(+), 8 deletions(-)
include/linux/trace_events.h +5 -2
index 321f2f3a6c8e..bb825ea5d09c 100644
@@ -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;
 }
kernel/bpf/syscall.c +1 -1
index 6147d386b777..aed1c6a2f203 100644
@@ -5932,7 +5932,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,
kernel/trace/bpf_trace.c +21 -5
index 2aec56d7f3d3..352a1cd0c863 100644
@@ -3861,7 +3861,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;
@@ -3875,12 +3876,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)
@@ -3951,7 +3966,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;
 }
Signature · 1 line
2.55.0
author
#4 lore raw

PATCH 3/9 bpf: Add log support for iter link_create

In reply to Leon Hwang

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.

---
 include/linux/bpf.h   |  3 ++-
 kernel/bpf/bpf_iter.c | 27 +++++++++++++++++++++------
 kernel/bpf/syscall.c  |  2 +-
 3 files changed, 24 insertions(+), 8 deletions(-)
include/linux/bpf.h +2 -1
index 520fd72e21fd..7ff256a5d98b 100644
@@ -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);
kernel/bpf/bpf_iter.c +21 -6
index f0b29a170935..dfefa5579e03 100644
@@ -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.\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_len.\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)
kernel/bpf/syscall.c +1 -1
index aed1c6a2f203..db135859a2d1 100644
@@ -5928,7 +5928,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))
Signature · 1 line
2.55.0
#5 lore raw

Re: [PATCH bpf-next 3/9] bpf: Add log support for iter link_create

Thank you for your contribution! Sashiko AI review found 1 potential issue to consider: - [Low] bpf: misattributed iter_info error log messages in bpf_iter_link_attach() --

Thank you for your contribution! Sashiko AI review found 1 potential issue to consider:
- [Low] bpf: misattributed iter_info error log messages in bpf_iter_link_attach()
--

commit d8f64c129a2738b81f1bf280ca7bbad040d70081
Author: Leon Hwang <[email protected]>

bpf: Add log support for iter link_create

This commit passes the verifier log to bpf_iter_link_attach() and logs diagnostic messages when BPF_LINK_CREATE fails for iterator programs.

Quoted 4 lines
kernel/bpf/bpf_iter.c +0 -0
index f0b29a1709357..dfefa5579e033 100644

[ ... ]

Quoted 1 line
kernel/bpf/bpf_iter.c +0 -0
@@ -538,24 +539,36 @@ int bpf_iter_link_attach(const union bpf_attr *attr, bpfptr_t uattr,

[ ... ]

Quoted 5 lines
kernel/bpf/bpf_iter.c +1 -1
 	if (!bpfptr_is_null(ulinfo)) {
 		err = bpf_check_uarg_tail_zero(ulinfo, sizeof(linfo),
 					       linfo_len);
-		if (err)
+		if (err) {

kernel/bpf/bpf_iter.c int bpf_iter_link_attach(const union bpf_attr *attr, bpfptr_t uattr, · line 572

Quoted 10 lines
kernel/bpf/bpf_iter.c +5 -1
+			bpf_log(log, "Invalid iter_info.\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_len.\n");
 			return -EFAULT;
+		}
 	}

[Severity: Low]
Are the log messages for iter_info and iter_info_len swapped or misattributed here?

By the time copy_from_bpfptr() runs, linfo_len has already been clamped to sizeof(linfo), so copy_from_bpfptr() only fails when reading from the ulinfo pointer (attr->link_create.iter_info) faults, yet it logs "Invalid iter_info_len.". Conversely, bpf_check_uarg_tail_zero() rejects linfo_len values larger than PAGE_SIZE with -E2BIG without touching ulinfo, which logs "Invalid iter_info.".

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3
author
#6 lore raw

PATCH 4/9 bpf: Add log support for cgroup prog link_create

In reply to Leon Hwang

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.

---
 include/linux/bpf-cgroup.h |  7 +++-
 kernel/bpf/cgroup.c        | 76 +++++++++++++++++++++++++++-----------
 kernel/bpf/syscall.c       |  4 +-
 3 files changed, 61 insertions(+), 26 deletions(-)
include/linux/bpf-cgroup.h +5 -2
index 02ef7f899b3d..ddcdd136fd49 100644
@@ -4,6 +4,7 @@
 
 #include <linux/bpf.h>
 #include <linux/bpf-cgroup-defs.h>
+#include <linux/bpf_verifier.h>
 #include <linux/errno.h>
 #include <linux/jump_label.h>
 #include <linux/percpu.h>
@@ -425,7 +426,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 +454,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;
 }
kernel/bpf/cgroup.c +54 -22

201 lines not on this page · raw message

kernel/bpf/syscall.c +2 -2
index db135859a2d1..43417824d851 100644
@@ -5907,7 +5907,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,
@@ -5930,7 +5930,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
Signature · 1 line
2.55.0
#7 lore raw

Re: [PATCH bpf-next 4/9] bpf: Add log support for cgroup prog link_create

TL;DR: Nit only: the new <linux/bpf_verifier.h> include in bpf-cgroup.h may be unnecessary, since a forward declaration would likely suffice and avoid pulling it into tcp.h users.
Quoted 1 line

bpf: Add log support for cgroup prog link_create

TL;DR: Nit only: the new <linux/bpf_verifier.h> include in bpf-cgroup.h may be unnecessary, since a forward declaration would likely suffice and avoid pulling it into tcp.h users.

include/linux/bpf-cgroup.h line 10

Quoted 11 lines
include/linux/bpf-cgroup.h +1 -0
@@ -4,6 +4,7 @@
 
 #include <linux/bpf.h>
 #include <linux/bpf-cgroup-defs.h>
+#include <linux/bpf_verifier.h>
 #include <linux/errno.h>
 #include <linux/jump_label.h>
 #include <linux/percpu.h>

this isn't a bug, but is the <linux/bpf_verifier.h> include needed here?

bpf.h already forward-declares struct bpf_verifier_log, which is all the new cgroup_bpf_link_attach() prototype and its stub need, and cgroup.c already includes bpf_verifier.h for bpf_log().

Since bpf-cgroup.h is included from include/net/tcp.h, would a plain forward declaration (as done for trace_events.h earlier in this series) keep the verifier header out of those translation units?

---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/42633880300

author
#8 lore raw

PATCH 5/9 libbpf: Add log_opts support for BPF_TRACE_ITER

In reply to Leon Hwang

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.

---
 tools/lib/bpf/bpf.c | 17 +++++++++++------
 tools/lib/bpf/bpf.h |  8 ++++++++
 2 files changed, 19 insertions(+), 6 deletions(-)
tools/lib/bpf/bpf.c +11 -6
index 38dcb31e8c0e..174a8bfaf663 100644
@@ -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,16 @@ int bpf_link_create(int prog_fd, int target_fd,
 			return libbpf_err(-EINVAL);
 	}
 
+	if (iter_info_len) {
+		if (OPTS_GET(opts, iter.len, 0))
+			return libbpf_err(-EINVAL);
+		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);
+	}
+
 	memset(&attr, 0, attr_sz);
 	attr.link_create.prog_fd = prog_fd;
 	attr.link_create.target_fd = target_fd;
@@ -970,7 +975,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;
 	}
tools/lib/bpf/bpf.h +8 -0
index fe3b7fd8512b..2ac143fd1102 100644
@@ -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;
 };
Signature · 1 line
2.55.0
#9 lore raw

Re: [PATCH bpf-next 5/9] libbpf: Add log_opts support for BPF_TRACE_ITER

Thank you for your contribution! Sashiko AI review found 3 potential issues to consider: - [Low] libbpf: missing OPTS_ZEROED() check for BPF_TRACE_ITER in bpf_link_create() - [Low] libbpf…

Thank you for your contribution! Sashiko AI review found 3 potential issues to consider:
- [Low] libbpf: missing OPTS_ZEROED() check for BPF_TRACE_ITER in bpf_link_create()
- [Low] libbpf: unreachable iter.len check in bpf_link_create()
- [Low] libbpf: deprecated iter_info fields used in bpf_program__attach_iter()
--

commit 254d03820df80f0d29b270cec3be5cafe9178fe6
Author: Leon Hwang <[email protected]>

libbpf: Add log_opts support for BPF_TRACE_ITER

This commit adds iter.info and iter.len to struct bpf_link_create_opts so that BPF_TRACE_ITER link creation can pass iterator parameters alongside log_opts, while keeping the legacy iter_info and iter_info_len fields for callers that do not use log_opts.

Quoted 17 lines
tools/lib/bpf/bpf.c +0 -5
index 38dcb31e8c0ed..174a8bfaf6638 100644
@@ -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)

tools/lib/bpf/bpf.c int bpf_link_create(int prog_fd, int target_fd, · line 821

Quoted 6 lines
tools/lib/bpf/bpf.c +3 -0
@@ -821,6 +816,16 @@ int bpf_link_create(int prog_fd, int target_fd,
 			return libbpf_err(-EINVAL);
 	}
 
+	if (iter_info_len) {
+		if (OPTS_GET(opts, iter.len, 0))
+			return libbpf_err(-EINVAL);

[Severity: Low]
Can OPTS_GET(opts, iter.len, 0) ever be non-zero here? When iter_info_len is set, the preceding if (iter_info_len || target_btf_id) block has already verified OPTS_ZEROED(opts, target_btf_id), which rejects any non-zero bytes in the union following target_btf_id, including opts->iter.len.

tools/lib/bpf/bpf.c int bpf_link_create(int prog_fd, int target_fd, · line 827

Quoted 6 lines
tools/lib/bpf/bpf.c +6 -0
+		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);
+	}

[Severity: Low]
Should bpf_program__attach_iter() also be updated to populate link_create_opts.iter.info and link_create_opts.iter.len instead of the deprecated iter_info and iter_info_len fields?

tools/lib/bpf/libbpf.c:bpf_program__attach_iter() {
    ...
	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);
    ...
}

Because iter_info shares a union with log_opts, calling bpf_program__attach_iter() with link_info != NULL and link_info_len == 0 causes bpf_link_create() to take the else branch above and dereference the union bpf_iter_link_info pointer as struct bpf_log_opts *log_opts in __sys_bpf_fd().

[ ... ]

Quoted 4 lines
tools/lib/bpf/bpf.h +0 -0
index fe3b7fd8512bd..2ac143fd1102e 100644

[ ... ]

tools/lib/bpf/bpf.h struct bpf_link_create_opts { · line 491

Quoted 11 lines
tools/lib/bpf/bpf.h +4 -0
@@ -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;
 };

[Severity: Low]
Does case BPF_TRACE_ITER in bpf_link_create() miss an OPTS_ZEROED(opts, iter) check?

tools/lib/bpf/bpf.c:bpf_link_create() {
    ...
	switch (attach_type) {
	case BPF_TRACE_ITER:
		attr.link_create.iter_info = ptr_to_u64(iter_info);
		attr.link_create.iter_info_len = iter_info_len;
		break;
    ...
}

When opts->iter is used (so iter_info_len and target_btf_id are 0), trailing bytes in the anonymous union after opts->iter overlapping larger members such as uprobe_multi are not checked for zero, unlike all other union members in the switch statement. Also, opts->iter.info and opts->iter.len are read in the !iter_info_len branch even when attach_type is not BPF_TRACE_ITER.

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=5
#10 lore raw

Re: [PATCH bpf-next 5/9] libbpf: Add log_opts support for BPF_TRACE_ITER

In reply to Leon Hwang
TL;DR: - libbpf BPF_TRACE_ITER case may not reject stray bytes after iter.len (missing OPTS_ZEROED check), so unknown fields could be silently ignored - Nit: iter.len test in the legacy branch looks…
Quoted 1 line

libbpf: Add log_opts support for BPF_TRACE_ITER

TL;DR:
- libbpf BPF_TRACE_ITER case may not reject stray bytes after iter.len (missing OPTS_ZEROED check), so unknown fields could be silently ignored
- Nit: iter.len test in the legacy branch looks like dead code

Quoted 3 lines
tools/lib/bpf/bpf.c +0 -0

[ ... ]

Quoted 4 lines
tools/lib/bpf/bpf.c +0 -0
@@ -821,6 +816,16 @@ int bpf_link_create(int prog_fd, int target_fd,
 			return libbpf_err(-EINVAL);
 	}
 

tools/lib/bpf/bpf.c int bpf_link_create(int prog_fd, int target_fd, · line 828

Quoted 10 lines
tools/lib/bpf/bpf.c +10 -0
+	if (iter_info_len) {
+		if (OPTS_GET(opts, iter.len, 0))
+			return libbpf_err(-EINVAL);
+		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);
+	}
+

When the legacy iter_info_len is non-zero, the check above already requires OPTS_ZEROED(opts, target_btf_id), which covers every byte from that offset up to sz, including iter.info and iter.len. Can this OPTS_GET(opts, iter.len, 0) test ever be true? It looks like dead code.

[ ... ]

tools/lib/bpf/bpf.c

Quoted 5 lines
tools/lib/bpf/bpf.c +0 -0
 	switch (attach_type) {
 	case BPF_TRACE_ITER:
 		attr.link_create.iter_info = ptr_to_u64(iter_info);
tools/lib/bpf/bpf.c +0 -0
 		attr.link_create.iter_info_len = iter_info_len;
 		break;

Every other union member's case ends with a check such as:

if (!OPTS_ZEROED(opts, tracing_multi))
return libbpf_err(-EINVAL);

Should the BPF_TRACE_ITER case do the same with OPTS_ZEROED(opts, iter)?

On the new iter.info/iter.len path, nothing checks the bytes after iter.len. A caller that sets opts.iter.info, opts.iter.len and opts.tracing_multi.cnt gets the stray field silently ignored instead of -EINVAL. Likewise, a later field added to the iter struct after len, built against newer headers, would be silently dropped by this libbpf because sz still equals sizeof() and OPTS_VALID() cannot see it.

Legacy callers would be unaffected, since their union is already zero, and the iter.len test in the legacy branch could then be dropped. The later commits in this series only touch selftests and do not address this.

---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/42633880300

author
#11 lore raw

PATCH 6/9 selftests/bpf: Verify tracing link_create logs

In reply to Leon Hwang

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 settings.

---
 .../selftests/bpf/prog_tests/tracing_link.c   | 275 ++++++++++++++++++
 1 file changed, 275 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/tracing_link.c
tools/testing/selftests/bpf/prog_tests/tracing_link.c new file +275 -0
new file mode 100644
index 000000000000..1b64cb3e9983
@@ -0,0 +1,275 @@
+// 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"
+
+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 attach_type.\n";
+
+	test_fentry_link_create_log(0, 0, 0, BPF_XDP, 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
+
+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();
+}
Signature · 1 line
2.55.0
#12 lore raw

Re: [PATCH bpf-next 6/9] selftests/bpf: Verify tracing link_create logs

TL;DR: Test issue: the invalid attach_type subtest in tracing_link.c may fail earlier in the generic check, so it may not exercise the new tracing log. Using BPF_TRACE_FEXIT on the fentry program…
Quoted 1 line

selftests/bpf: Verify tracing link_create logs

TL;DR: Test issue: the invalid attach_type subtest in tracing_link.c may fail earlier in the generic check, so it may not exercise the new tracing log. Using BPF_TRACE_FEXIT on the fentry program might fix it.

Quoted 4 lines
tools/testing/selftests/bpf/prog_tests/tracing_link.c new file +0 -0
new file mode 100644

[ ... ]

tools/testing/selftests/bpf/prog_tests/tracing_link.c line 78

Quoted 6 lines
tools/testing/selftests/bpf/prog_tests/tracing_link.c new file +6 -0
+static void test_log_invalid_attach_type(void)
+{
+	const char *msg = "Invalid attach_type.\n";
+
+	test_fentry_link_create_log(0, 0, 0, BPF_XDP, false, msg);
+}

Does this subtest reach the tracing-specific log added by "bpf: Add log support for tracing link_create"?

With BPF_XDP on the fentry program, link_create() fails earlier in the generic check:

    ret = bpf_prog_attach_check_attach_type(prog, attr->link_create.attach_type);
    if (ret) {
        bpf_log(log, "Invalid attach_type.\n");
        goto out;
    }

For BPF_PROG_TYPE_TRACING, attach_type_to_prog_type(BPF_XDP) returns BPF_PROG_TYPE_XDP, so bpf_prog_attach_check_attach_type() returns -EINVAL and the case below is never reached:

    case BPF_PROG_TYPE_TRACING:
        if (attr->link_create.attach_type != prog->expected_attach_type) {
            bpf_log(log, "Invalid attach_type.\n");

Both messages are identical, so this subtest would still pass if the tracing-branch bpf_log() were removed. The generic check is already covered by the invalid_attach_type subtest in xdp_link.c.

Would passing an attach type that maps to BPF_PROG_TYPE_TRACING but differs from the program's expected_attach_type, for example BPF_TRACE_FEXIT for this fentry program, exercise the new log instead?

---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/42633880300

author
#13 lore raw

PATCH 7/9 selftests/bpf: Verify tracing_multi link_create logs

In reply to Leon Hwang

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.

---
 .../selftests/bpf/prog_tests/tracing_link.c   | 102 ++++++++++++++++++
 1 file changed, 102 insertions(+)
tools/testing/selftests/bpf/prog_tests/tracing_link.c +102 -0
index 1b64cb3e9983..6f22a21be946 100644
@@ -5,6 +5,7 @@
 #include "priv_freplace_prog.skel.h"
 #include "xdp_dummy.skel.h"
 #include "kprobe_write_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,
@@ -273,3 +274,104 @@ void test_tracing_link_log(void)
 	if (test__start_subtest("invalid_kprobe_write_ctx"))
 		test_log_invalid_kprobe_write_ctx();
 }
+
+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();
+}
+
Signature · 1 line
2.55.0
author
#14 lore raw

PATCH 8/9 selftests/bpf: Verify iter link_create logs

In reply to Leon Hwang

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.

---
 .../selftests/bpf/prog_tests/iter_link.c      | 132 ++++++++++++++++++
 1 file changed, 132 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/iter_link.c
tools/testing/selftests/bpf/prog_tests/iter_link.c new file +132 -0
new file mode 100644
index 000000000000..c684a109091a
@@ -0,0 +1,132 @@
+// 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.\n";
+	LIBBPF_OPTS(bpf_link_create_opts, opts,
+		    .iter.info = (union bpf_iter_link_info *)info,
+		    .iter.len = sizeof(info),
+	);
+
+	info[sizeof(info) - 1] = 1;
+	test_iter_link_create_log(0, &opts, 0, -E2BIG, msg);
+}
+
+static void test_log_invalid_iter_info_len(void)
+{
+	const char *msg = "Invalid iter_info_len.\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();
+}
Signature · 1 line
2.55.0
author
#15 lore raw

PATCH 9/9 selftests/bpf: Verify cgroup prog link_create logs

In reply to Leon Hwang

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.

---
 .../selftests/bpf/prog_tests/cgroup_link.c    | 324 ++++++++++++++++++
 1 file changed, 324 insertions(+)
tools/testing/selftests/bpf/prog_tests/cgroup_link.c +324 -0
index 15093a69510e..5a27d3a6a26c 100644
@@ -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();
+}
Signature · 1 line
2.55.0