]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
Merge tag 'vfs-7.3-rc1.kfunc' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 17 Aug 2026 18:02:10 +0000 (11:02 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 17 Aug 2026 18:02:10 +0000 (11:02 -0700)
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

1  2 
fs/bpf_fs_kfuncs.c

index 5b7d03e4fc6d2a8ca39fda570db4cdb039e151cb,9a4ea5c9b0c9456725a6cc72bc325ff7148c4681..6cb8772679782d5b2f9e26942229d38fd81a1a90
@@@ -360,24 -360,52 +361,57 @@@ __bpf_kfunc int bpf_cgroup_read_xattr(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_inode - get the real inode backing a dentry
 - * @dentry: dentry to resolve
 + * bpf_real_data_inode - get the real inode hosting a file's data
 + * @file: file to resolve
   *
 - * If the dentry is on a union/overlay filesystem, return the underlying, real
 - * inode that hosts the data.  Otherwise return the inode attached to the
 - * dentry itself.
 + * Resolve @file to the inode that hosts its data. For a regular file on a
 + * union/overlay filesystem this is the underlying (upper or lower) inode that
 + * stores the data, not the overlay inode.
   *
 - * Return: The real inode backing the dentry, or NULL for a negative dentry.
 + * Data resolution only applies to regular files. For a non-regular file (e.g.
 + * a device node, fifo or socket) on a union/overlay filesystem the overlay
 + * inode itself is returned; for any file on a non-union filesystem the inode
 + * attached to @file is returned.
 + *
 + * Return: The inode hosting @file's data, or NULL.
   */
 -__bpf_kfunc struct inode *bpf_real_inode(struct dentry *dentry)
 +__bpf_kfunc struct inode *bpf_real_data_inode(struct file *file)
  {
 -      return d_real_inode(dentry);
 +      return d_real_inode(file_dentry(file));
  }
  
  __bpf_kfunc_end_defs();
@@@ -390,28 -418,16 +424,31 @@@ BTF_ID_FLAGS(func, bpf_get_dentry_xattr
  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_inode, KF_SLEEPABLE | KF_RET_NULL)
 +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. */
 +BTF_SET_START(bpf_fs_kfunc_lsm_only_ids)
 +BTF_ID(func, bpf_set_dentry_xattr)
 +BTF_ID(func, bpf_remove_dentry_xattr)
 +BTF_SET_END(bpf_fs_kfunc_lsm_only_ids)
 +
  static int bpf_fs_kfuncs_filter(const struct bpf_prog *prog, u32 kfunc_id)
  {
 -      if (!btf_id_set8_contains(&bpf_fs_kfunc_set_ids, kfunc_id) ||
 -          prog->type == BPF_PROG_TYPE_LSM)
 +      if (!btf_id_set8_contains(&bpf_fs_kfunc_set_ids, kfunc_id))
 +              return 0;
 +      if (prog->type == BPF_PROG_TYPE_LSM)
 +              return 0;
 +      if (prog->type != BPF_PROG_TYPE_STRUCT_OPS)
 +              return -EACCES;
 +      /* ->st_ops is unset during the cfg pass; enforced once it is set. */
 +      if (!prog->aux->st_ops)
 +              return 0;
 +      if (bpf_prog_is_binfmt_misc_ops(prog) &&
 +          !btf_id_set_contains(&bpf_fs_kfunc_lsm_only_ids, kfunc_id))
                return 0;
        return -EACCES;
  }