Re: [PATCH vfs-7.5.bpf v2 1/3] bpf: Add bpf_path_getattr()

From: Christian Brauner

Date: Fri Nov 06 2026 - 05:39:17 EST


On Mon, Nov 02, 2026 at 12:46:53PM +0200, Anastasios Papagiannis wrote:
> + ret = vfs_getattr_nosec(path, &stat, request_mask, query_flags);

This blocks on the filesystem and it's callable from every sleepable LSM
hook. It's trivial to get a trusted path via bpf_get_task_exe_file().

So the issue I have is that some of those hooks run under locks where
blocking on the filesystem is a deadlock...

For example, a synchronous NFS rpc is transmitted in the caller's
context,

__rpc_execute()
-> call_transmit() /* XPRT_LOCKED, xprt->snd_task = T1 */
-> xs_tcp_send_request()
-> xprt_sock_sendmsg()
-> sock_sendmsg()
-> __sock_sendmsg()
-> security_socket_sendmsg()

so a program on lsm.s/socket_sendmsg doing

file = bpf_get_task_exe_file(bpf_get_current_task_btf());
bpf_path_getattr(&file->f_path, STATX_BASIC_STATS, 0, &st, sizeof(st));

When the attribute cache expires and the file on the same nfs mount you
get:

nfs_getattr()
-> __nfs_revalidate_inode()
-> rpc_call_sync() /* T2 */
-> xprt_reserve_xprt() /* XPRT_LOCKED && T2 != snd_task */
-> rpc_sleep_on(&xprt->sending)

with T1 never finishing the send. cifs has the same shape with
server->_srv_mutex held across smb_send() and ocfs2 with sc_send_lock.

There are others of the same kind:

- bprm_committing_creds and bprm_committed_creds run with
exec_update_lock held for write.

A FUSE_GETATTR on bprm->file deadlocks if the daemon does
readlink("/proc/<pid>/exe") or reads /proc/<pid>/stat of the
requester because both take exec_update_lock now.

I just massaged that codepath and moved close_cloexec_files() out from
under that lock for exactly this reason.

Fun fact, request_wait_answer() is uninterruptible once the daemon has
read the request.

- sb_show_options and sb_statfs via statmount() hold namespace_sem.

- inode_free_security runs from evict under s_umount or from reclaim.

- task_setscheduler via cpuset_can_attach() holds threadgroup_rwsem for write.

- file_receive from seccomp addfd holds notify_lock.

The worst part is that - as I said in the bpf session at LSFMM -
bpf_get_file_xattr() already brings in similar problems.

But it needs an NFSv4.2 server with xattr support and a cache miss.

In contrast, getattr is hit on every attribute cache expiry and every
fuse getattr.

So we need mechanisms in bpf to guard against things like that
architecturally. Ideally it would not just be a filter like in the past.

And really, request_mask and query_flags reach the filesystem
unfiltered and do_statx() does:

if (mask & STATX__RESERVED)
return -EINVAL;
if ((flags & AT_STATX_SYNC_TYPE) == AT_STATX_SYNC_TYPE)
return -EINVAL;
mask &= ~STATX_CHANGE_COOKIE;

and we should do the same here.

And STATX_CHANGE_COOKIE does changes to the inode. generic_fillattr()
calls inode_query_iversion() which sets I_VERSION_QUERIED. That forces
i_version bumps. That's kernel-only infra.

> -/* Side-effecting kfuncs that stay exclusive to LSM programs. */
> +/* Kfuncs that stay exclusive to LSM programs. */
> BTF_SET_START(bpf_fs_kfunc_lsm_only_ids)
> +BTF_ID(func, bpf_path_getattr)

The other thing is that struct kstat uses very different formats for say
device numbers than userspace.