mirror of
https://github.com/torvalds/linux.git
synced 2026-09-13 15:40:03 +02:00
vfs-7.3-rc1.kfunc
Please consider pulling these changes from the signed vfs-7.3-rc1.kfunc tag. Thanks! Christian -----BEGIN PGP SIGNATURE----- iHUEABYKAB0WIQRAhzRXHqcMeLMyaSiRxhvAZXjcogUCan7RJQAKCRCRxhvAZXjc oqBnAQDzCTHIa0wColluZLaFVuL4pvipQzC7tjUUQCwPmy85EwEA2cyeguEkNYJZ SK3m4z8kfuZjO2tEOig+XCkmtVrSMQQ= =6gwq -----END PGP SIGNATURE----- Merge tag 'vfs-7.3-rc1.kfunc' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs Pull vfs bpf access updates from Christian Brauner: "This adds a bpf_sock_read_xattr() kfunc so a BPF LSM program can read a user.* extended attribute from a socket's sockfs inode locklessly. userspace already uses user.* xattrs on sockets to implement socket rate limiting and to tag sockets for other purposes such as a varlink registry. There has been no efficient way for a BPF program to read those labels back. With this a listening socket marked from userspace with fsetxattr() can be read back during bind or connect and acted upon on the connecting socket. That lets userspace mark sockets and later rediscover them or implement policy on them" * tag 'vfs-7.3-rc1.kfunc' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: selftests/bpf: Add test for bpf_sock_read_xattr() kfunc fs: Add bpf_sock_read_xattr() kfunc to read socket xattrs
This commit is contained in:
commit
de03b17ec0
|
|
@ -12,6 +12,7 @@
|
|||
#include <linux/file.h>
|
||||
#include <linux/kernfs.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/xattr.h>
|
||||
|
||||
__bpf_kfunc_start_defs();
|
||||
|
|
@ -360,6 +361,39 @@ __bpf_kfunc int bpf_cgroup_read_xattr(struct cgroup *cgroup, const char *name__s
|
|||
}
|
||||
#endif /* CONFIG_CGROUPS */
|
||||
|
||||
#ifdef CONFIG_NET
|
||||
/**
|
||||
* bpf_sock_read_xattr - read xattr of a socket's inode in sockfs
|
||||
* @sock: socket to get xattr from
|
||||
* @name__str: name of the xattr
|
||||
* @value_p: output buffer of the xattr value
|
||||
*
|
||||
* Get xattr *name__str* of *sock* and store the output in *value_p*.
|
||||
*
|
||||
* For security reasons, only *name__str* with prefix "user." is allowed.
|
||||
*
|
||||
* Return: length of the xattr value on success, a negative value on error.
|
||||
*/
|
||||
__bpf_kfunc int bpf_sock_read_xattr(struct socket *sock, const char *name__str,
|
||||
struct bpf_dynptr *value_p)
|
||||
{
|
||||
struct bpf_dynptr_kern *value_ptr = (struct bpf_dynptr_kern *)value_p;
|
||||
u32 value_len;
|
||||
void *value;
|
||||
|
||||
/* Only allow reading "user.*" xattrs */
|
||||
if (strncmp(name__str, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN))
|
||||
return -EPERM;
|
||||
|
||||
value_len = __bpf_dynptr_size(value_ptr);
|
||||
value = __bpf_dynptr_data_rw(value_ptr, value_len);
|
||||
if (!value)
|
||||
return -EINVAL;
|
||||
|
||||
return sock_read_xattr(sock, name__str, value, value_len);
|
||||
}
|
||||
#endif /* CONFIG_NET */
|
||||
|
||||
/**
|
||||
* bpf_real_data_inode - get the real inode hosting a file's data
|
||||
* @file: file to resolve
|
||||
|
|
@ -391,6 +425,9 @@ BTF_ID_FLAGS(func, bpf_get_file_xattr, KF_SLEEPABLE)
|
|||
BTF_ID_FLAGS(func, bpf_set_dentry_xattr, KF_SLEEPABLE)
|
||||
BTF_ID_FLAGS(func, bpf_remove_dentry_xattr, KF_SLEEPABLE)
|
||||
BTF_ID_FLAGS(func, bpf_real_data_inode, KF_SLEEPABLE | KF_RET_NULL)
|
||||
#ifdef CONFIG_NET
|
||||
BTF_ID_FLAGS(func, bpf_sock_read_xattr, KF_RCU)
|
||||
#endif
|
||||
BTF_KFUNCS_END(bpf_fs_kfunc_set_ids)
|
||||
|
||||
/* Side-effecting kfuncs that stay exclusive to LSM programs. */
|
||||
|
|
|
|||
|
|
@ -285,6 +285,7 @@ int sock_recvmsg(struct socket *sock, struct msghdr *msg, int flags);
|
|||
struct file *sock_alloc_file(struct socket *sock, int flags, const char *dname);
|
||||
struct socket *sockfd_lookup(int fd, int *err);
|
||||
struct socket *sock_from_file(struct file *file);
|
||||
int sock_read_xattr(struct socket *sock, const char *name, void *value, size_t size);
|
||||
#define sockfd_put(sock) fput(sock->file)
|
||||
int net_ratelimit(void);
|
||||
|
||||
|
|
|
|||
25
net/socket.c
25
net/socket.c
|
|
@ -465,6 +465,31 @@ static const struct xattr_handler sockfs_user_xattr_handler = {
|
|||
.set = sockfs_user_xattr_set,
|
||||
};
|
||||
|
||||
/**
|
||||
* sock_read_xattr - read a user.* xattr from a socket's sockfs inode
|
||||
* @sock: socket whose inode holds the xattr
|
||||
* @name: full xattr name, e.g. "user.bpf_test"
|
||||
* @value: output buffer
|
||||
* @size: size of @value in bytes
|
||||
*
|
||||
* SOCK_INODE() is valid only for sockfs sockets; sock_from_file() rejects
|
||||
* anything else (e.g. tun, tap).
|
||||
* Lockless: simple_xattr_get() looks up the value under RCU, no inode lock.
|
||||
*
|
||||
* Return: length of the value on success, a negative errno on error.
|
||||
*/
|
||||
int sock_read_xattr(struct socket *sock, const char *name, void *value, size_t size)
|
||||
{
|
||||
struct file *file = sock->file;
|
||||
struct sockfs_inode *si;
|
||||
|
||||
if (!file || sock_from_file(file) != sock)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
si = SOCKFS_I(SOCK_INODE(sock));
|
||||
return simple_xattr_get(&sockfs_xa_cache, &si->xattrs, name, value, size);
|
||||
}
|
||||
|
||||
static const struct xattr_handler * const sockfs_xattr_handlers[] = {
|
||||
&sockfs_xattr_handler,
|
||||
&sockfs_security_xattr_handler,
|
||||
|
|
|
|||
|
|
@ -364,6 +364,9 @@ extern void bpf_iter_dmabuf_destroy(struct bpf_iter_dmabuf *it) __weak __ksym;
|
|||
extern int bpf_cgroup_read_xattr(struct cgroup *cgroup, const char *name__str,
|
||||
struct bpf_dynptr *value_p) __weak __ksym;
|
||||
|
||||
extern int bpf_sock_read_xattr(struct socket *sock, const char *name__str,
|
||||
struct bpf_dynptr *value_p) __weak __ksym;
|
||||
|
||||
#define PREEMPT_BITS 8
|
||||
#define SOFTIRQ_BITS 8
|
||||
#define HARDIRQ_BITS 4
|
||||
|
|
|
|||
67
tools/testing/selftests/bpf/prog_tests/sock_xattr.c
Normal file
67
tools/testing/selftests/bpf/prog_tests/sock_xattr.c
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (c) 2026 Christian Brauner */
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/xattr.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <test_progs.h>
|
||||
|
||||
#include "sock_read_xattr.skel.h"
|
||||
|
||||
static const char xattr_value[] = "bpf_sock_value";
|
||||
static const char xattr_name[] = "user.bpf_test";
|
||||
|
||||
static void test_read_sock_xattr(void)
|
||||
{
|
||||
struct sockaddr_in addr = {};
|
||||
struct sock_read_xattr *skel = NULL;
|
||||
struct bpf_link *link = NULL;
|
||||
int sock_fd = -1, err;
|
||||
|
||||
sock_fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (!ASSERT_OK_FD(sock_fd, "socket"))
|
||||
return;
|
||||
|
||||
err = fsetxattr(sock_fd, xattr_name, xattr_value, sizeof(xattr_value), 0);
|
||||
if (!ASSERT_OK(err, "fsetxattr"))
|
||||
goto out;
|
||||
|
||||
skel = sock_read_xattr__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "sock_read_xattr__open_and_load"))
|
||||
goto out;
|
||||
|
||||
skel->bss->monitored_pid = sys_gettid();
|
||||
|
||||
/* Only attach the functional program; the verifier-only programs
|
||||
* above are not pid-gated and would clobber the shared globals.
|
||||
*/
|
||||
link = bpf_program__attach(skel->progs.read_sock_xattr);
|
||||
if (!ASSERT_OK_PTR(link, "attach read_sock_xattr"))
|
||||
goto out;
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(1234);
|
||||
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
/* Only the lsm/socket_connect hook matters; the connect may fail. */
|
||||
connect(sock_fd, (struct sockaddr *)&addr, sizeof(addr));
|
||||
|
||||
ASSERT_EQ(skel->data->read_ret, sizeof(xattr_value), "read_ret");
|
||||
ASSERT_STREQ(skel->bss->value, xattr_value, "value");
|
||||
|
||||
out:
|
||||
bpf_link__destroy(link);
|
||||
if (sock_fd >= 0)
|
||||
close(sock_fd);
|
||||
sock_read_xattr__destroy(skel);
|
||||
}
|
||||
|
||||
void test_sock_xattr(void)
|
||||
{
|
||||
RUN_TESTS(sock_read_xattr);
|
||||
|
||||
if (test__start_subtest("read_sock_xattr"))
|
||||
test_read_sock_xattr();
|
||||
}
|
||||
54
tools/testing/selftests/bpf/progs/sock_read_xattr.c
Normal file
54
tools/testing/selftests/bpf/progs/sock_read_xattr.c
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2026 Christian Brauner */
|
||||
|
||||
#include <vmlinux.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_core_read.h>
|
||||
#include "bpf_experimental.h"
|
||||
#include "bpf_misc.h"
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
char value[16];
|
||||
int read_ret = -1;
|
||||
__u32 monitored_pid = 0;
|
||||
|
||||
static __always_inline void read_xattr(struct socket *sock)
|
||||
{
|
||||
struct bpf_dynptr value_ptr;
|
||||
|
||||
bpf_dynptr_from_mem(value, sizeof(value), 0, &value_ptr);
|
||||
bpf_sock_read_xattr(sock, "user.bpf_test", &value_ptr);
|
||||
}
|
||||
|
||||
SEC("lsm.s/socket_connect")
|
||||
__success
|
||||
int BPF_PROG(trusted_sock_ptr_sleepable, struct socket *sock)
|
||||
{
|
||||
read_xattr(sock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("lsm/socket_connect")
|
||||
__success
|
||||
int BPF_PROG(trusted_sock_ptr_non_sleepable, struct socket *sock)
|
||||
{
|
||||
read_xattr(sock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("lsm.s/socket_connect")
|
||||
__success
|
||||
int BPF_PROG(read_sock_xattr, struct socket *sock)
|
||||
{
|
||||
struct bpf_dynptr value_ptr;
|
||||
__u32 pid = bpf_get_current_pid_tgid() >> 32;
|
||||
|
||||
if (pid != monitored_pid)
|
||||
return 0;
|
||||
|
||||
bpf_dynptr_from_mem(value, sizeof(value), 0, &value_ptr);
|
||||
read_ret = bpf_sock_read_xattr(sock, "user.bpf_test", &value_ptr);
|
||||
return 0;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user