The branch main has been updated by pouria:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=2fc95fe26e725439209217f53dd76437a52be76b

commit 2fc95fe26e725439209217f53dd76437a52be76b
Author:     YAO, Xin <[email protected]>
AuthorDate: 2026-06-29 01:55:25 +0000
Commit:     Pouria Mousavizadeh Tehrani <[email protected]>
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 <[email protected]>
    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);
 }
 
 int

Reply via email to