Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider: - [Critical] bpf: NULL deref and format string bug in bpf_xdp_link_attach() via bpf_log() - [Critical] bpf: prog refcount underflow in link_create() if bpf_log_attr_finalize() fails - [Low] bpf: link failure in bpf_xdp_link_attach() when !CONFIG_BPF_SYSCALL --
commit fa16fd00d8499a718392032e57d440248e048afc Author: Leon Hwang <[email protected]> bpf: Add syscall common attrs support for xdp link_create This commit passes the verifier log to bpf_xdp_link_attach() so that XDP link creation can report target_ifindex and driver errors through BPF syscall common attributes. > diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c > index 252b479937a4d..e96d5f09486f5 100644 > --- a/kernel/bpf/syscall.c > +++ b/kernel/bpf/syscall.c > @@ -5921,7 +5921,7 @@ static int link_create(union bpf_attr *attr, bpfptr_t > uattr, struct bpf_common_a > break; > #ifdef CONFIG_NET > case BPF_PROG_TYPE_XDP: > - ret = bpf_xdp_link_attach(attr, prog); > + ret = bpf_xdp_link_attach(attr, prog, log); > break; > case BPF_PROG_TYPE_SCHED_CLS: > if (attr->link_create.attach_type == BPF_TCX_INGRESS || [Severity: Critical] Can the cleanup path in link_create() underflow prog's reference count if bpf_log_attr_finalize() fails after bpf_xdp_link_attach() succeeds? When bpf_xdp_link_attach() succeeds, bpf_link_init() transfers ownership of prog's reference to link->link.prog and bpf_link_settle() installs the link file descriptor, returning a non-negative fd in ret. At the out label in link_create(), bpf_log_attr_finalize() writes log_true_size to uattr_common even when nothing was logged: kernel/bpf/log.c:bpf_log_attr_finalize() { ... if (attr->offsetof_true_size && copy_to_bpfptr_offset(attr->uattr, attr->offsetof_true_size, &log_true_size, sizeof(log_true_size))) return -EFAULT; } If uattr_common points to read-only memory, copy_from_bpfptr() in __sys_bpf() succeeds, bpf_xdp_link_attach() attaches and settles the link, and then bpf_log_attr_finalize() returns -EFAULT: kernel/bpf/syscall.c:link_create() { out: /* preserve original error even if log finalization is successful */ err = bpf_log_attr_finalize(&attr_log, log); if (err) ret = err; kfree(log); if (ret < 0 && prog) bpf_prog_put(prog); return ret; } Overwriting ret with err causes link_create() to call bpf_prog_put(prog) on a reference that is now owned by the settled bpf_link, leading to a use-after-free of struct bpf_prog while the link remains attached in the process fd table. > diff --git a/net/core/dev.c b/net/core/dev.c > index 18dc88990510a..fdc1de21f3589 100644 > --- a/net/core/dev.c > +++ b/net/core/dev.c [ ... ] > @@ -10771,7 +10772,8 @@ static const struct bpf_link_ops bpf_xdp_link_lops = { > .update_prog = bpf_xdp_link_update, > }; > > -int bpf_xdp_link_attach(const union bpf_attr *attr, struct bpf_prog *prog) > +int bpf_xdp_link_attach(const union bpf_attr *attr, struct bpf_prog *prog, > + struct bpf_verifier_log *log) > { > struct net *net = current->nsproxy->net_ns; > struct bpf_link_primer link_primer; > @@ -10784,6 +10786,7 @@ int bpf_xdp_link_attach(const union bpf_attr *attr, > struct bpf_prog *prog) > dev = dev_get_by_index(net, attr->link_create.target_ifindex); > if (!dev) { > rtnl_unlock(); > + bpf_log(log, "Invalid target_ifindex.\n"); > return -EINVAL; > } [Severity: Low] Will calling bpf_log() in bpf_xdp_link_attach() break linking when CONFIG_NET=y and CONFIG_BPF_SYSCALL=n? net/core/dev.c is compiled whenever CONFIG_NET=y, and bpf_xdp_link_attach() is not guarded by CONFIG_BPF_SYSCALL. While helpers like bpf_link_init() and bpf_link_prime() have static inline stubs in include/linux/bpf.h when !CONFIG_BPF_SYSCALL, bpf_log() is only compiled via kernel/bpf/log.o when CONFIG_BPF_SYSCALL=y and has no stub in include/linux/bpf_verifier.h. > > @@ -10812,6 +10815,7 @@ int bpf_xdp_link_attach(const union bpf_attr *attr, > struct bpf_prog *prog) > if (err) { > link->dev = NULL; > bpf_link_cleanup(&link_primer); > + bpf_log(log, extack._msg); > trace_bpf_xdp_link_attach_failed(extack._msg); > goto out_put_dev; > } [Severity: Critical] What happens here when dev_xdp_attach_link() fails without setting an extack message, or when extack._msg contains format specifiers? In bpf_xdp_link_attach(), extack is zero-initialized on the stack, so extack._msg starts as NULL. Many failure paths under dev_xdp_attach_link() -> dev_xdp_attach() -> dev_xdp_install() return an error without calling NL_SET_ERR_MSG(), such as netif_xdp_propagate() when a device has no ndo_bpf callback, or driver ndo_bpf callbacks like netkit_xdp() and veth_xdp() returning -EOPNOTSUPP or -EINVAL. When a user provides a log buffer via syscall common attributes and extack._msg is NULL, bpf_log() passes fmt = NULL into bpf_verifier_vlog() -> vscnprintf() -> vsnprintf(), which dereferences fmt unconditionally and crashes the kernel. Additionally, because bpf_log() is a printf-style function, passing extack._msg directly as the format string rather than using a "%s\n" format specifier allows any '%' characters in a formatted extack message (for example from NL_SET_ERR_MSG_FMT() containing an interface name) to be interpreted as format specifiers by vscnprintf(), and omits the trailing newline. Should this be guarded with if (extack._msg) and logged with bpf_log(log, "%s\n", extack._msg)? -- Sashiko AI review ยท https://sashiko.dev/#/patchset/[email protected]?part=2
