Date: Wed, 01 Jul 2026 08:44:08 +0000 From: Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org Cc: YAO, Xin <mr.yaoxin@outlook.com> Subject: git: 2fc95fe26e72 - main - linuxulator: Add linux_extattr_get_vp() for atomic getxattr Message-ID: <6a44d358.36b75.5aa46ec4@gitrepo.freebsd.org>
index | next in thread | raw e-mail
The branch main has been updated by pouria: URL: https://cgit.FreeBSD.org/src/commit/?id=2fc95fe26e725439209217f53dd76437a52be76b commit 2fc95fe26e725439209217f53dd76437a52be76b Author: YAO, Xin <mr.yaoxin@outlook.com> AuthorDate: 2026-06-29 01:55:25 +0000 Commit: Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org> CommitDate: 2026-07-01 08:38:32 +0000 linuxulator: Add linux_extattr_get_vp() for atomic getxattr Move the atomic size-probe-and-read logic into a new linux_extattr_get_vp() function in linux_xattr.c instead of modifying the generic extattr_get_vp() in vfs_extattr.c. This keeps Linux-specific getxattr semantics (ERANGE on too-small buffer, EOPNOTSUPP to ENOATTR mapping) self-contained within the linuxulator. The function probes the attribute size and reads the data under a single vnode lock, preventing a TOCTOU race between the size probe and data read. Signed-off-by: YAO, Xin <mr.yaoxin@outlook.com> Reviewed by: kib Pull Request: https://github.com/freebsd/freebsd-src/pull/2263 --- sys/compat/linux/linux_xattr.c | 105 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 94 insertions(+), 11 deletions(-) diff --git a/sys/compat/linux/linux_xattr.c b/sys/compat/linux/linux_xattr.c index 70e4c3a1d3db..296b70ff409a 100644 --- a/sys/compat/linux/linux_xattr.c +++ b/sys/compat/linux/linux_xattr.c @@ -33,6 +33,9 @@ #include <sys/namei.h> #include <sys/proc.h> #include <sys/syscallsubr.h> +#include <sys/vnode.h> + +#include <security/mac/mac_framework.h> #ifdef COMPAT_LINUX32 #include <machine/../linux32/linux.h> @@ -334,11 +337,83 @@ linux_fremovexattr(struct thread *td, struct linux_fremovexattr_args *args) return (removexattr(td, &eargs)); } +/*- + * Linux-specific atomic extended attribute get on a vnode. + * + * Probes the attribute size and reads the data under a single vnode lock, + * preventing a TOCTOU race and returning ERANGE when the buffer is too + * small (matching Linux getxattr(2) semantics). + */ +static int +linux_extattr_get_vp(struct vnode *vp, int attrnamespace, const char *attrname, + void *data, size_t nbytes, struct thread *td) +{ + struct uio auio; + struct iovec aiov; + size_t size; + int error; + + if (nbytes > IOSIZE_MAX) + return (EINVAL); + + vn_lock(vp, LK_SHARED | LK_RETRY); + +#ifdef MAC + error = mac_vnode_check_getextattr(td->td_ucred, vp, attrnamespace, + attrname); + if (error != 0) + goto done; +#endif + + /* + * Probe the attribute size first under the vnode lock; + */ + error = VOP_GETEXTATTR(vp, attrnamespace, attrname, NULL, + &size, td->td_ucred, td); + if (error != 0) + goto done; + + /* + * The caller only wants the size, so we are done after this. + */ + if (data == NULL || nbytes == 0) { + td->td_retval[0] = size; + goto done; + } + /* + * If the buffer is too small, return ERANGE + * so the caller can retry (Linux getxattr semantics). + */ + if (size > nbytes) { + error = ERANGE; + goto done; + } + /* Buffer is large enough; read the value. */ + aiov.iov_base = data; + aiov.iov_len = nbytes; + auio.uio_iov = &aiov; + auio.uio_iovcnt = 1; + auio.uio_offset = 0; + auio.uio_resid = nbytes; + auio.uio_rw = UIO_READ; + auio.uio_segflg = UIO_USERSPACE; + auio.uio_td = td; + error = VOP_GETEXTATTR(vp, attrnamespace, attrname, &auio, NULL, + td->td_ucred, td); + if (error == 0) + td->td_retval[0] = nbytes - auio.uio_resid; +done: + VOP_UNLOCK(vp); + return (error); +} + static int getxattr(struct thread *td, struct getxattr_args *args) { char attrname[LINUX_XATTR_NAME_MAX + 1]; struct file *fp = NULL; + struct nameidata nd; + struct vnode *vp; cap_rights_t rights; int attrnamespace, error; @@ -347,22 +422,30 @@ getxattr(struct thread *td, struct getxattr_args *args) cap_rights_init_one(&rights, CAP_EXTATTR_GET), &fp); if (error != 0) return (error); + vp = fp->f_vnode; + } else { + NDINIT_ATRIGHTS(&nd, LOOKUP, args->follow, UIO_USERSPACE, + args->path, AT_FDCWD, + cap_rights_init_one(&rights, CAP_EXTATTR_GET)); + error = namei(&nd); + if (error != 0) + return (error); + NDFREE_PNBUF(&nd); + vp = nd.ni_vp; } error = xattr_to_extattr(args->name, &attrnamespace, attrname); - if (error != 0) - goto out_err; - if (args->path != NULL) - error = kern_extattr_get_path(td, args->path, attrnamespace, - attrname, args->value, args->size, args->follow, UIO_USERSPACE); - else - error = kern_extattr_get_fp(td, fp, attrnamespace, - attrname, args->value, args->size); + if (error == 0) { + error = linux_extattr_get_vp(vp, attrnamespace, attrname, + args->value, args->size, td); + } -out_err: - if (fp != NULL) + if (fp != NULL) { fdrop(fp, td); - return (error == EPERM ? ENOATTR : error); + } else { + vrele(nd.ni_vp); + } + return (error == EPERM || error == EOPNOTSUPP ? ENOATTR : error); } inthome | help
Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a44d358.36b75.5aa46ec4>
