On Tue Jun 23, 2026 at 12:25 PM CEST, Sudhir Dumbhare via 
lists.openembedded.org wrote:
> From: Sudhir Dumbhare <[email protected]>
>
> - This patch applies the upstream fix [5] as referenced in [7].
> - To successfully apply the fixed commit, apply the dependent commits [2] to 
> [4]
>   which are included in v2.8.6, as referenced in [7].
> - Additionally, include dependent commit [1] from v2.8.3, as referenced in [8]
>   under the [2.5.4-38.2] description, along with compilation fix commit [6]
>   from v2.7.1
> - Reference:
>   [1] 
> https://git.linux-nfs.org/?p=steved/nfs-utils.git;a=commit;h=cd90f2925790
>   [2] 
> https://git.linux-nfs.org/?p=steved/nfs-utils.git;a=commit;h=7e8b36522f58
>   [3] 
> https://git.linux-nfs.org/?p=steved/nfs-utils.git;a=commit;h=42f01e6a78fe
>   [4] 
> https://git.linux-nfs.org/?p=steved/nfs-utils.git;a=commit;h=51738ae56d92
>   [5] 
> https://git.linux-nfs.org/?p=steved/nfs-utils.git;a=commit;h=f36bd900a899
>   [6] 
> https://git.linux-nfs.org/?p=steved/nfs-utils.git;a=commit;h=a2c95e4f557a
>   [7] https://security-tracker.debian.org/tracker/CVE-2025-12801
>   [8] https://linux.oracle.com/errata/ELSA-2026-3940.html
>
> Signed-off-by: Sudhir Dumbhare <[email protected]>
> ---
> Changes v1 -> v2:
> - Fully backport commit cd90f29257904f36509ea5a04a86f42398fbe94a for 
> completeness.

I understand that I need to drop the v1.
What was the issue with the partial cd90f2925 commit?
This is a really big CVE patch. Anything to simplify its review would be
welcome.

Regards,
>
>  .../nfs-utils/CVE-2025-12801-build-fix.patch  |  44 ++
>  .../CVE-2025-12801-dependent_p1.patch         | 450 +++++++++++++++++
>  .../CVE-2025-12801-dependent_p2.patch         |  81 +++
>  .../CVE-2025-12801-dependent_p3.patch         | 181 +++++++
>  .../CVE-2025-12801-dependent_p4.patch         | 468 ++++++++++++++++++
>  .../nfs-utils/nfs-utils/CVE-2025-12801.patch  | 254 ++++++++++
>  .../nfs-utils/nfs-utils_2.6.4.bb              |   6 +
>  7 files changed, 1484 insertions(+)
>  create mode 100644 
> meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801-build-fix.patch
>  create mode 100644 
> meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801-dependent_p1.patch
>  create mode 100644 
> meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801-dependent_p2.patch
>  create mode 100644 
> meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801-dependent_p3.patch
>  create mode 100644 
> meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801-dependent_p4.patch
>  create mode 100644 
> meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801.patch
>
> diff --git 
> a/meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801-build-fix.patch
>  
> b/meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801-build-fix.patch
> new file mode 100644
> index 0000000000..d7aaca2242
> --- /dev/null
> +++ 
> b/meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801-build-fix.patch
> @@ -0,0 +1,44 @@
> +From 30e0f57fff545b0bb3071fa071c7b12c2923bac8 Mon Sep 17 00:00:00 2001
> +From: Steve Dickson <[email protected]>
> +Date: Mon, 22 Jan 2024 13:23:57 -0500
> +Subject: [PATCH] reexport.c: Some Distros need the following include to
> + avoid the following error
> +
> +reexport.c: In function ‘connect_fsid_service’:
> +reexport.c:41:28: error: implicit declaration of function ‘offsetof’ 
> [-Werror=implicit-function-declaration]
> +   41 |                 addr_len = offsetof(struct sockaddr_un, sun_path) + 
> strlen(addr.sun_path);
> +      |                            ^~~~~~~~
> +reexport.c:19:1: note: ‘offsetof’ is defined in header ‘<stddef.h>’; did you 
> forget to ‘#include <stddef.h>’?
> +   18 | #include "xlog.h"
> +  +++ |+#include <stddef.h>
> +   19 |
> +reexport.c:41:37: error: expected expression before ‘struct’
> +   41 |                 addr_len = offsetof(struct sockaddr_un, sun_path) + 
> strlen(addr.sun_path);
> +      |                                     ^~~~~~
> +cc1: some warnings being treated as errors
> +
> +CVE: CVE-2025-12801
> +Upstream-Status: Backport 
> [https://git.linux-nfs.org/?p=steved/nfs-utils.git;a=commit;h=a2c95e4f557a71b482bb62bad6d93ddde51e5dc6]
> +
> +Signed-off-by: Steve Dickson <[email protected]>
> +(cherry picked from commit a2c95e4f557a71b482bb62bad6d93ddde51e5dc6)
> +Signed-off-by: Sudhir Dumbhare <[email protected]>
> +---
> + support/reexport/reexport.c | 1 +
> + 1 file changed, 1 insertion(+)
> +
> +diff --git a/support/reexport/reexport.c b/support/reexport/reexport.c
> +index 78516586..16dde0fb 100644
> +--- a/support/reexport/reexport.c
> ++++ b/support/reexport/reexport.c
> +@@ -8,6 +8,7 @@
> + #include <sys/types.h>
> + #include <sys/vfs.h>
> + #include <errno.h>
> ++#include <stddef.h>
> + 
> + #include "nfsd_path.h"
> + #include "conffile.h"
> +-- 
> +2.44.4
> +
> diff --git 
> a/meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801-dependent_p1.patch
>  
> b/meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801-dependent_p1.patch
> new file mode 100644
> index 0000000000..c1fb7c2f12
> --- /dev/null
> +++ 
> b/meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801-dependent_p1.patch
> @@ -0,0 +1,450 @@
> +From bbec1c68cbf9a9b3b28aad213b4573d288879a6f Mon Sep 17 00:00:00 2001
> +From: Christopher Bii <[email protected]>
> +Date: Wed, 15 Jan 2025 12:10:48 -0500
> +Subject: [PATCH] NFS export symlink vulnerability fix
> +
> +Replaced dangerous use of realpath within support/nfs/export.c with
> +nfsd_realpath variant that is executed within the chrooted thread
> +rather than main thread.
> +
> +Implemented nfsd_path.h methods to work securely within chrooted
> +thread using nfsd_run_task() help
> +
> +CVE: CVE-2025-12801
> +Upstream-Status: Backport 
> [https://git.linux-nfs.org/?p=steved/nfs-utils.git;a=commit;h=cd90f29257904f36509ea5a04a86f42398fbe94a]
> +
> +Signed-off-by: Christopher Bii <[email protected]>
> +Signed-off-by: Steve Dickson <[email protected]>
> +(cherry picked from commit cd90f29257904f36509ea5a04a86f42398fbe94a)
> +Signed-off-by: Sudhir Dumbhare <[email protected]>
> +---
> + support/export/cache.c      |   2 +-
> + support/include/nfsd_path.h |   5 +-
> + support/misc/nfsd_path.c    | 257 +++++++++++-------------------------
> + support/nfs/exports.c       |   3 +-
> + 4 files changed, 83 insertions(+), 184 deletions(-)
> +
> +diff --git a/support/export/cache.c b/support/export/cache.c
> +index 6c0a44a3..a4c339f2 100644
> +--- a/support/export/cache.c
> ++++ b/support/export/cache.c
> +@@ -65,7 +65,7 @@ static ssize_t cache_read(int fd, char *buf, size_t len)
> +     return nfsd_path_read(fd, buf, len);
> + }
> + 
> +-static ssize_t cache_write(int fd, const char *buf, size_t len)
> ++static ssize_t cache_write(int fd, void *buf, size_t len)
> + {
> +     return nfsd_path_write(fd, buf, len);
> + }
> +diff --git a/support/include/nfsd_path.h b/support/include/nfsd_path.h
> +index aa1e1dd0..f600fb5a 100644
> +--- a/support/include/nfsd_path.h
> ++++ b/support/include/nfsd_path.h
> +@@ -8,6 +8,7 @@
> + 
> + struct file_handle;
> + struct statfs;
> ++struct nfsd_task_t;
> + 
> + void                nfsd_path_init(void);
> + 
> +@@ -23,8 +24,8 @@ int                nfsd_path_statfs(const char *pathname,
> + 
> + char *              nfsd_realpath(const char *path, char *resolved_path);
> + 
> +-ssize_t             nfsd_path_read(int fd, char *buf, size_t len);
> +-ssize_t             nfsd_path_write(int fd, const char *buf, size_t len);
> ++ssize_t             nfsd_path_read(int fd, void* buf, size_t len);
> ++ssize_t             nfsd_path_write(int fd, void* buf, size_t len);
> + 
> + int         nfsd_name_to_handle_at(int fd, const char *path,
> +                                    struct file_handle *fh,
> +diff --git a/support/misc/nfsd_path.c b/support/misc/nfsd_path.c
> +index c3dea4f0..caec33ca 100644
> +--- a/support/misc/nfsd_path.c
> ++++ b/support/misc/nfsd_path.c
> +@@ -19,7 +19,20 @@
> + #include "nfsd_path.h"
> + #include "workqueue.h"
> + 
> +-static struct xthread_workqueue *nfsd_wq;
> ++static struct xthread_workqueue *nfsd_wq = NULL;
> ++
> ++struct nfsd_task_t {
> ++        int             ret;
> ++        void*           data;
> ++};
> ++/* Function used to offload tasks that must be ran within the correct
> ++ * chroot environment.
> ++ */
> ++static void
> ++nfsd_run_task(void (*func)(void*), void* data){
> ++        nfsd_wq ? xthread_work_run_sync(nfsd_wq, func, data) : func(data);
> ++};
> ++
> + 
> + static int
> + nfsd_path_isslash(const char *path)
> +@@ -124,224 +137,119 @@ nfsd_path_init(void)
> + }
> + 
> + struct nfsd_stat_data {
> +-    const char *pathname;
> +-    struct stat *statbuf;
> +-    int ret;
> +-    int err;
> ++    const char      *pathname;
> ++    struct stat     *statbuf;
> ++        int             (*stat_handler)(const char*, struct stat*);
> + };
> + 
> + static void
> +-nfsd_statfunc(void *data)
> +-{
> +-    struct nfsd_stat_data *d = data;
> +-
> +-    d->ret = xstat(d->pathname, d->statbuf);
> +-    if (d->ret < 0)
> +-            d->err = errno;
> +-}
> +-
> +-static void
> +-nfsd_lstatfunc(void *data)
> ++nfsd_handle_stat(void *data)
> + {
> +-    struct nfsd_stat_data *d = data;
> +-
> +-    d->ret = xlstat(d->pathname, d->statbuf);
> +-    if (d->ret < 0)
> +-            d->err = errno;
> ++        struct nfsd_task_t*     t = data;
> ++    struct nfsd_stat_data*  d = t->data;
> ++        t->ret = d->stat_handler(d->pathname, d->statbuf);
> + }
> + 
> + static int
> +-nfsd_run_stat(struct xthread_workqueue *wq,
> +-            void (*func)(void *),
> +-            const char *pathname,
> +-            struct stat *statbuf)
> ++nfsd_run_stat(const char *pathname,
> ++            struct stat *statbuf,
> ++                int (*handler)(const char*, struct stat*))
> + {
> +-    struct nfsd_stat_data data = {
> +-            pathname,
> +-            statbuf,
> +-            0,
> +-            0
> +-    };
> +-    xthread_work_run_sync(wq, func, &data);
> +-    if (data.ret < 0)
> +-            errno = data.err;
> +-    return data.ret;
> ++        struct nfsd_task_t      t;
> ++        struct nfsd_stat_data   d = { pathname, statbuf, handler };
> ++        t.data = &d;
> ++        nfsd_run_task(nfsd_handle_stat, &t);
> ++    return t.ret;
> + }
> + 
> + int
> + nfsd_path_stat(const char *pathname, struct stat *statbuf)
> + {
> +-    if (!nfsd_wq)
> +-            return xstat(pathname, statbuf);
> +-    return nfsd_run_stat(nfsd_wq, nfsd_statfunc, pathname, statbuf);
> ++        return nfsd_run_stat(pathname, statbuf, stat);
> + }
> + 
> + int
> +-nfsd_path_lstat(const char *pathname, struct stat *statbuf)
> +-{
> +-    if (!nfsd_wq)
> +-            return xlstat(pathname, statbuf);
> +-    return nfsd_run_stat(nfsd_wq, nfsd_lstatfunc, pathname, statbuf);
> +-}
> +-
> +-struct nfsd_statfs_data {
> +-    const char *pathname;
> +-    struct statfs *statbuf;
> +-    int ret;
> +-    int err;
> ++nfsd_path_lstat(const char* pathname, struct stat* statbuf){
> ++        return nfsd_run_stat(pathname, statbuf, lstat);
> + };
> + 
> +-static void
> +-nfsd_statfsfunc(void *data)
> +-{
> +-    struct nfsd_statfs_data *d = data;
> +-
> +-    d->ret = statfs(d->pathname, d->statbuf);
> +-    if (d->ret < 0)
> +-            d->err = errno;
> +-}
> +-
> +-static int
> +-nfsd_run_statfs(struct xthread_workqueue *wq,
> +-              const char *pathname,
> +-              struct statfs *statbuf)
> +-{
> +-    struct nfsd_statfs_data data = {
> +-            pathname,
> +-            statbuf,
> +-            0,
> +-            0
> +-    };
> +-    xthread_work_run_sync(wq, nfsd_statfsfunc, &data);
> +-    if (data.ret < 0)
> +-            errno = data.err;
> +-    return data.ret;
> +-}
> +-
> + int
> +-nfsd_path_statfs(const char *pathname, struct statfs *statbuf)
> ++nfsd_path_statfs(const char* pathname, struct statfs* statbuf)
> + {
> +-    if (!nfsd_wq)
> +-            return statfs(pathname, statbuf);
> +-    return nfsd_run_statfs(nfsd_wq, pathname, statbuf);
> +-}
> ++        return nfsd_run_stat(pathname, (struct stat*)statbuf, (int 
> (*)(const char*, struct stat*))statfs);
> ++};
> + 
> +-struct nfsd_realpath_data {
> +-    const char *pathname;
> +-    char *resolved;
> +-    int err;
> ++struct nfsd_realpath_t {
> ++        const char*     path;
> ++        char*           resolved_buf;
> ++        char*           res_ptr;
> + };
> + 
> + static void
> + nfsd_realpathfunc(void *data)
> + {
> +-    struct nfsd_realpath_data *d = data;
> +-
> +-    d->resolved = realpath(d->pathname, d->resolved);
> +-    if (!d->resolved)
> +-            d->err = errno;
> ++        struct nfsd_realpath_t *d = data;
> ++        d->res_ptr = realpath(d->path, d->resolved_buf);
> + }
> + 
> +-char *
> +-nfsd_realpath(const char *path, char *resolved_path)
> ++char*
> ++nfsd_realpath(const char *path, char *resolved_buf)
> + {
> +-    struct nfsd_realpath_data data = {
> +-            path,
> +-            resolved_path,
> +-            0
> +-    };
> +-
> +-    if (!nfsd_wq)
> +-            return realpath(path, resolved_path);
> +-
> +-    xthread_work_run_sync(nfsd_wq, nfsd_realpathfunc, &data);
> +-    if (!data.resolved)
> +-            errno = data.err;
> +-    return data.resolved;
> ++        struct nfsd_realpath_t realpath_buf = {
> ++                .path = path,
> ++                .resolved_buf = resolved_buf
> ++        };
> ++        nfsd_run_task(nfsd_realpathfunc, &realpath_buf);
> ++        return realpath_buf.res_ptr;
> + }
> + 
> +-struct nfsd_read_data {
> +-    int fd;
> +-    char *buf;
> +-    size_t len;
> +-    ssize_t ret;
> +-    int err;
> ++struct nfsd_rw_data {
> ++    int             fd;
> ++    void*           buf;
> ++    size_t          len;
> ++        ssize_t         bytes_read;
> + };
> + 
> + static void
> + nfsd_readfunc(void *data)
> + {
> +-    struct nfsd_read_data *d = data;
> +-
> +-    d->ret = read(d->fd, d->buf, d->len);
> +-    if (d->ret < 0)
> +-            d->err = errno;
> ++        struct nfsd_rw_data* t = (struct nfsd_rw_data*)data;
> ++        t->bytes_read = read(t->fd, t->buf, t->len);
> + }
> + 
> + static ssize_t
> +-nfsd_run_read(struct xthread_workqueue *wq, int fd, char *buf, size_t len)
> ++nfsd_run_read(int fd, void* buf, size_t len)
> + {
> +-    struct nfsd_read_data data = {
> +-            fd,
> +-            buf,
> +-            len,
> +-            0,
> +-            0
> +-    };
> +-    xthread_work_run_sync(wq, nfsd_readfunc, &data);
> +-    if (data.ret < 0)
> +-            errno = data.err;
> +-    return data.ret;
> ++        struct nfsd_rw_data d = { .fd = fd, .buf = buf, .len = len };
> ++        nfsd_run_task(nfsd_readfunc, &d);
> ++    return d.bytes_read;
> + }
> + 
> + ssize_t
> +-nfsd_path_read(int fd, char *buf, size_t len)
> ++nfsd_path_read(int fd, void* buf, size_t len)
> + {
> +-    if (!nfsd_wq)
> +-            return read(fd, buf, len);
> +-    return nfsd_run_read(nfsd_wq, fd, buf, len);
> ++    return nfsd_run_read(fd, buf, len);
> + }
> + 
> +-struct nfsd_write_data {
> +-    int fd;
> +-    const char *buf;
> +-    size_t len;
> +-    ssize_t ret;
> +-    int err;
> +-};
> +-
> + static void
> + nfsd_writefunc(void *data)
> + {
> +-    struct nfsd_write_data *d = data;
> +-
> +-    d->ret = write(d->fd, d->buf, d->len);
> +-    if (d->ret < 0)
> +-            d->err = errno;
> ++    struct nfsd_rw_data* d = data;
> ++    d->bytes_read = write(d->fd, d->buf, d->len);
> + }
> + 
> + static ssize_t
> +-nfsd_run_write(struct xthread_workqueue *wq, int fd, const char *buf, 
> size_t len)
> ++nfsd_run_write(int fd, void* buf, size_t len)
> + {
> +-    struct nfsd_write_data data = {
> +-            fd,
> +-            buf,
> +-            len,
> +-            0,
> +-            0
> +-    };
> +-    xthread_work_run_sync(wq, nfsd_writefunc, &data);
> +-    if (data.ret < 0)
> +-            errno = data.err;
> +-    return data.ret;
> ++        struct nfsd_rw_data d = { .fd = fd, .buf = buf, .len = len };
> ++        nfsd_run_task(nfsd_writefunc, &d);
> ++    return d.bytes_read;
> + }
> + 
> + ssize_t
> +-nfsd_path_write(int fd, const char *buf, size_t len)
> ++nfsd_path_write(int fd, void* buf, size_t len)
> + {
> +-    if (!nfsd_wq)
> +-            return write(fd, buf, len);
> +-    return nfsd_run_write(nfsd_wq, fd, buf, len);
> ++    return nfsd_run_write(fd, buf, len);
> + }
> + 
> + #if defined(HAVE_NAME_TO_HANDLE_AT)
> +@@ -352,23 +260,18 @@ struct nfsd_handle_data {
> +     int *mount_id;
> +     int flags;
> +     int ret;
> +-    int err;
> + };
> + 
> + static void
> + nfsd_name_to_handle_func(void *data)
> + {
> +     struct nfsd_handle_data *d = data;
> +-
> +-    d->ret = name_to_handle_at(d->fd, d->path,
> +-                    d->fh, d->mount_id, d->flags);
> +-    if (d->ret < 0)
> +-            d->err = errno;
> ++    d->ret = name_to_handle_at(d->fd, d->path, d->fh, d->mount_id, 
> d->flags);
> + }
> + 
> + static int
> +-nfsd_run_name_to_handle_at(struct xthread_workqueue *wq,
> +-            int fd, const char *path, struct file_handle *fh,
> ++nfsd_run_name_to_handle_at(int fd, const char *path,
> ++                struct file_handle *fh,
> +             int *mount_id, int flags)
> + {
> +     struct nfsd_handle_data data = {
> +@@ -377,25 +280,19 @@ nfsd_run_name_to_handle_at(struct xthread_workqueue 
> *wq,
> +             fh,
> +             mount_id,
> +             flags,
> +-            0,
> +             0
> +     };
> + 
> +-    xthread_work_run_sync(wq, nfsd_name_to_handle_func, &data);
> +-    if (data.ret < 0)
> +-            errno = data.err;
> ++    nfsd_run_task(nfsd_name_to_handle_func, &data);
> +     return data.ret;
> + }
> + 
> + int
> +-nfsd_name_to_handle_at(int fd, const char *path, struct file_handle *fh,
> ++nfsd_name_to_handle_at(int fd, const char *path,
> ++                struct file_handle *fh,
> +             int *mount_id, int flags)
> + {
> +-    if (!nfsd_wq)
> +-            return name_to_handle_at(fd, path, fh, mount_id, flags);
> +-
> +-    return nfsd_run_name_to_handle_at(nfsd_wq, fd, path, fh,
> +-                    mount_id, flags);
> ++        return nfsd_run_name_to_handle_at(fd, path, fh, mount_id, flags);
> + }
> + #else
> + int
> +diff --git a/support/nfs/exports.c b/support/nfs/exports.c
> +index 15dc574c..c47e3d0a 100644
> +--- a/support/nfs/exports.c
> ++++ b/support/nfs/exports.c
> +@@ -32,6 +32,7 @@
> + #include "xio.h"
> + #include "pseudoflavors.h"
> + #include "reexport.h"
> ++#include "nfsd_path.h"
> + 
> + #define EXPORT_DEFAULT_FLAGS        \
> +   
> (NFSEXP_READONLY|NFSEXP_ROOTSQUASH|NFSEXP_GATHERED_WRITES|NFSEXP_NOSUBTREECHECK)
> +@@ -200,7 +201,7 @@ getexportent(int fromkernel, int fromexports)
> +             return NULL;
> +         }
> +     /* resolve symlinks */
> +-    if (realpath(ee.e_path, rpath) != NULL) {
> ++    if (nfsd_realpath(ee.e_path, rpath) != NULL) {
> +             rpath[sizeof (rpath) - 1] = '\0';
> +             strncpy(ee.e_path, rpath, sizeof (ee.e_path) - 1);
> +             ee.e_path[sizeof (ee.e_path) - 1] = '\0';
> +-- 
> +2.35.6
> +
> diff --git 
> a/meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801-dependent_p2.patch
>  
> b/meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801-dependent_p2.patch
> new file mode 100644
> index 0000000000..f088eadb4b
> --- /dev/null
> +++ 
> b/meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801-dependent_p2.patch
> @@ -0,0 +1,81 @@
> +From a6ddd0e9594884cf61816478e8c561f1b3aac709 Mon Sep 17 00:00:00 2001
> +From: Trond Myklebust <[email protected]>
> +Date: Mon, 10 Nov 2025 11:26:03 -0500
> +Subject: [PATCH] mountd: Minor refactor of get_rootfh()
> +
> +Perform the mountpoint checks before checking the user path.
> +
> +CVE: CVE-2025-12801
> +Upstream-Status: Backport 
> [https://git.linux-nfs.org/?p=steved/nfs-utils.git;a=commit;h=7e8b36522f58657359c6842119fc516c6dd1baa4]
> +
> +Reviewed-by: Jeff Layton <[email protected]>
> +Signed-off-by: Trond Myklebust <[email protected]>
> +Signed-off-by: Steve Dickson <[email protected]>
> +(cherry picked from commit 7e8b36522f58657359c6842119fc516c6dd1baa4)
> +Signed-off-by: Sudhir Dumbhare <[email protected]>
> +---
> + utils/mountd/mountd.c | 34 +++++++++++++++++-----------------
> + 1 file changed, 17 insertions(+), 17 deletions(-)
> +
> +diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c
> +index dbd5546d..39afd4aa 100644
> +--- a/utils/mountd/mountd.c
> ++++ b/utils/mountd/mountd.c
> +@@ -412,6 +412,23 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, 
> nfs_export **expret,
> +             *error = MNT3ERR_ACCES;
> +             return NULL;
> +     }
> ++    if (nfsd_path_stat(exp->m_export.e_path, &estb) < 0) {
> ++            xlog(L_WARNING, "can't stat export point %s: %s",
> ++                 p, strerror(errno));
> ++            *error = MNT3ERR_NOENT;
> ++            return NULL;
> ++    }
> ++    if (exp->m_export.e_mountpoint &&
> ++               !check_is_mountpoint(exp->m_export.e_mountpoint[0]?
> ++                              exp->m_export.e_mountpoint:
> ++                              exp->m_export.e_path,
> ++                              nfsd_path_lstat)) {
> ++            xlog(L_WARNING, "request to export an unmounted filesystem: %s",
> ++                 p);
> ++            *error = MNT3ERR_NOENT;
> ++            return NULL;
> ++    }
> ++
> +     if (nfsd_path_stat(p, &stb) < 0) {
> +             xlog(L_WARNING, "can't stat exported dir %s: %s",
> +                             p, strerror(errno));
> +@@ -426,12 +443,6 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, 
> nfs_export **expret,
> +             *error = MNT3ERR_NOTDIR;
> +             return NULL;
> +     }
> +-    if (nfsd_path_stat(exp->m_export.e_path, &estb) < 0) {
> +-            xlog(L_WARNING, "can't stat export point %s: %s",
> +-                 p, strerror(errno));
> +-            *error = MNT3ERR_NOENT;
> +-            return NULL;
> +-    }
> +     if (estb.st_dev != stb.st_dev
> +         && !(exp->m_export.e_flags & NFSEXP_CROSSMOUNT)) {
> +             xlog(L_WARNING, "request to export directory %s below nearest 
> filesystem %s",
> +@@ -439,17 +450,6 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, 
> nfs_export **expret,
> +             *error = MNT3ERR_ACCES;
> +             return NULL;
> +     }
> +-    if (exp->m_export.e_mountpoint &&
> +-               !check_is_mountpoint(exp->m_export.e_mountpoint[0]?
> +-                              exp->m_export.e_mountpoint:
> +-                              exp->m_export.e_path,
> +-                              nfsd_path_lstat)) {
> +-            xlog(L_WARNING, "request to export an unmounted filesystem: %s",
> +-                 p);
> +-            *error = MNT3ERR_NOENT;
> +-            return NULL;
> +-    }
> +-
> +     /* This will be a static private nfs_export with just one
> +      * address.  We feed it to kernel then extract the filehandle,
> +      */
> +-- 
> +2.44.4
> +
> diff --git 
> a/meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801-dependent_p3.patch
>  
> b/meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801-dependent_p3.patch
> new file mode 100644
> index 0000000000..901069e3b9
> --- /dev/null
> +++ 
> b/meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801-dependent_p3.patch
> @@ -0,0 +1,181 @@
> +From 57732919d26ce523161392d688e3b67d6fc50839 Mon Sep 17 00:00:00 2001
> +From: Trond Myklebust <[email protected]>
> +Date: Mon, 10 Nov 2025 11:28:39 -0500
> +Subject: [PATCH] mountd: Separate lookup of the exported directory and the
> + mount path
> +
> +When the caller asks to mount a path that does not terminate with an
> +exported directory, we want to split up the lookups so that we can
> +look up the exported directory using the mountd privileged credential,
> +and the remaining subdirectory lookups using the RPC caller's
> +credential.
> +
> +CVE: CVE-2025-12801
> +Upstream-Status: Backport 
> [https://git.linux-nfs.org/?p=steved/nfs-utils.git;a=commit;h=42f01e6a78fed98f12437ac8b28cfb12b6bad056]
> +
> +Reviewed-by: Jeff Layton <[email protected]>
> +Signed-off-by: Trond Myklebust <[email protected]>
> +Signed-off-by: Steve Dickson <[email protected]>
> +(cherry picked from commit 42f01e6a78fed98f12437ac8b28cfb12b6bad056)
> +Signed-off-by: Sudhir Dumbhare <[email protected]>
> +---
> + support/include/nfsd_path.h |  1 +
> + support/misc/nfsd_path.c    | 31 ++++++++++++++++++
> + utils/mountd/mountd.c       | 63 +++++++++++++++++++++++++++++++------
> + 3 files changed, 86 insertions(+), 9 deletions(-)
> +
> +diff --git a/support/include/nfsd_path.h b/support/include/nfsd_path.h
> +index f600fb5a..3e5a2f5d 100644
> +--- a/support/include/nfsd_path.h
> ++++ b/support/include/nfsd_path.h
> +@@ -18,6 +18,7 @@ char *             nfsd_path_prepend_dir(const char *dir, 
> const char *pathname);
> + 
> + int                 nfsd_path_stat(const char *pathname, struct stat 
> *statbuf);
> + int                 nfsd_path_lstat(const char *pathname, struct stat 
> *statbuf);
> ++int         nfsd_openat(int dirfd, const char *path, int flags);
> + 
> + int         nfsd_path_statfs(const char *pathname,
> +                                struct statfs *statbuf);
> +diff --git a/support/misc/nfsd_path.c b/support/misc/nfsd_path.c
> +index caec33ca..dfe88e4f 100644
> +--- a/support/misc/nfsd_path.c
> ++++ b/support/misc/nfsd_path.c
> +@@ -203,6 +203,37 @@ nfsd_realpath(const char *path, char *resolved_buf)
> +         return realpath_buf.res_ptr;
> + }
> + 
> ++struct nfsd_openat_t {
> ++    const char *path;
> ++    int dirfd;
> ++    int flags;
> ++    int res_fd;
> ++    int res_error;
> ++};
> ++
> ++static void nfsd_openatfunc(void *data)
> ++{
> ++    struct nfsd_openat_t *d = data;
> ++
> ++    d->res_fd = openat(d->dirfd, d->path, d->flags);
> ++    if (d->res_fd == -1)
> ++            d->res_error = errno;
> ++}
> ++
> ++int nfsd_openat(int dirfd, const char *path, int flags)
> ++{
> ++    struct nfsd_openat_t open_buf = {
> ++            .path = path,
> ++            .dirfd = dirfd,
> ++            .flags = flags,
> ++    };
> ++
> ++    nfsd_run_task(nfsd_openatfunc, &open_buf);
> ++    if (open_buf.res_fd == -1)
> ++            errno = open_buf.res_error;
> ++    return open_buf.res_fd;
> ++}
> ++
> + struct nfsd_rw_data {
> +     int             fd;
> +     void*           buf;
> +diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c
> +index 39afd4aa..f43ebef5 100644
> +--- a/utils/mountd/mountd.c
> ++++ b/utils/mountd/mountd.c
> +@@ -392,7 +392,10 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, 
> nfs_export **expret,
> +     struct nfs_fh_len *fh;
> +     char            rpath[MAXPATHLEN+1];
> +     char            *p = *path;
> ++    char            *subpath;
> +     char            buf[INET6_ADDRSTRLEN];
> ++    size_t          epathlen;
> ++    int             dirfd;
> + 
> +     if (*p == '\0')
> +             p = "/";
> +@@ -412,12 +415,21 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, 
> nfs_export **expret,
> +             *error = MNT3ERR_ACCES;
> +             return NULL;
> +     }
> +-    if (nfsd_path_stat(exp->m_export.e_path, &estb) < 0) {
> +-            xlog(L_WARNING, "can't stat export point %s: %s",
> ++
> ++    dirfd = nfsd_openat(AT_FDCWD, exp->m_export.e_path, O_PATH);
> ++    if (dirfd == -1) {
> ++            xlog(L_WARNING, "can't open export point %s: %s",
> +                  p, strerror(errno));
> +             *error = MNT3ERR_NOENT;
> +             return NULL;
> +     }
> ++    if (fstat(dirfd, &estb) == -1) {
> ++            xlog(L_WARNING, "can't stat export point %s: %s",
> ++                 p, strerror(errno));
> ++            *error = MNT3ERR_ACCES;
> ++            close(dirfd);
> ++            return NULL;
> ++    }
> +     if (exp->m_export.e_mountpoint &&
> +                !check_is_mountpoint(exp->m_export.e_mountpoint[0]?
> +                               exp->m_export.e_mountpoint:
> +@@ -426,18 +438,51 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, 
> nfs_export **expret,
> +             xlog(L_WARNING, "request to export an unmounted filesystem: %s",
> +                  p);
> +             *error = MNT3ERR_NOENT;
> ++            close(dirfd);
> +             return NULL;
> +     }
> + 
> +-    if (nfsd_path_stat(p, &stb) < 0) {
> +-            xlog(L_WARNING, "can't stat exported dir %s: %s",
> +-                            p, strerror(errno));
> +-            if (errno == ENOENT)
> +-                    *error = MNT3ERR_NOENT;
> +-            else
> +-                    *error = MNT3ERR_ACCES;
> ++    epathlen = strlen(exp->m_export.e_path);
> ++    if (epathlen > strlen(p)) {
> ++            xlog(L_WARNING, "raced with change of exported path: %s", p);
> ++            *error = MNT3ERR_NOENT;
> ++            close(dirfd);
> +             return NULL;
> +     }
> ++    subpath = &p[epathlen];
> ++    while (*subpath == '/')
> ++            subpath++;
> ++    if (*subpath != '\0') {
> ++            int fd;
> ++
> ++            /* Just perform a lookup of the path */
> ++            fd = nfsd_openat(dirfd, subpath, O_PATH);
> ++            close(dirfd);
> ++            if (fd == -1) {
> ++                    xlog(L_WARNING, "can't open exported dir %s: %s", p,
> ++                         strerror(errno));
> ++                    if (errno == ENOENT)
> ++                            *error = MNT3ERR_NOENT;
> ++                    else
> ++                            *error = MNT3ERR_ACCES;
> ++                    return NULL;
> ++            }
> ++            if (fstat(fd, &stb) == -1) {
> ++                    xlog(L_WARNING, "can't open exported dir %s: %s", p,
> ++                         strerror(errno));
> ++                    if (errno == ENOENT)
> ++                            *error = MNT3ERR_NOENT;
> ++                    else
> ++                            *error = MNT3ERR_ACCES;
> ++                    close(fd);
> ++                    return NULL;
> ++            }
> ++            close(fd);
> ++    } else {
> ++            close(dirfd);
> ++            stb = estb;
> ++    }
> ++
> +     if (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) {
> +             xlog(L_WARNING, "%s is not a directory or regular file", p);
> +             *error = MNT3ERR_NOTDIR;
> +-- 
> +2.35.6
> +
> diff --git 
> a/meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801-dependent_p4.patch
>  
> b/meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801-dependent_p4.patch
> new file mode 100644
> index 0000000000..4ef529e737
> --- /dev/null
> +++ 
> b/meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801-dependent_p4.patch
> @@ -0,0 +1,468 @@
> +From 7eef498b6bd01adc45415b03ddf321c84f82aa45 Mon Sep 17 00:00:00 2001
> +From: Trond Myklebust <[email protected]>
> +Date: Mon, 10 Nov 2025 12:18:38 -0500
> +Subject: [PATCH] support: Add a mini-library to extract and apply RPC
> + credentials
> +
> +Add server functionality to extract the credentials from the client RPC
> +call, and apply them. This is needed in order to perform access checking
> +on the requested path in the mountd daemon.
> +
> +CVE: CVE-2025-12801
> +Upstream-Status: Backport 
> [https://git.linux-nfs.org/?p=steved/nfs-utils.git;a=commit;h=51738ae56d922d4961e60dad73ad1c2d97d8d99b]
> +
> +Backport Changes:
> +- In support/misc/Makefile.am, the non-essential file.c was omitted
> +  as it does not exist in the current nfs-utils version.
> +
> +Reviewed-by: Jeff Layton <[email protected]>
> +Signed-off-by: Trond Myklebust <[email protected]>
> +Signed-off-by: Steve Dickson <[email protected]>
> +(cherry picked from commit 51738ae56d922d4961e60dad73ad1c2d97d8d99b)
> +Signed-off-by: Sudhir Dumbhare <[email protected]>
> +---
> + aclocal/libtirpc.m4         |  11 +++
> + support/include/Makefile.am |   1 +
> + support/include/nfs_ucred.h |  44 ++++++++++
> + support/misc/Makefile.am    |   2 +-
> + support/misc/ucred.c        | 162 ++++++++++++++++++++++++++++++++++++
> + support/nfs/Makefile.am     |   2 +-
> + support/nfs/ucred.c         | 147 ++++++++++++++++++++++++++++++++
> + 7 files changed, 367 insertions(+), 2 deletions(-)
> + create mode 100644 support/include/nfs_ucred.h
> + create mode 100644 support/misc/ucred.c
> + create mode 100644 support/nfs/ucred.c
> +
> +diff --git a/aclocal/libtirpc.m4 b/aclocal/libtirpc.m4
> +index bddae022..84e18f7e 100644
> +--- a/aclocal/libtirpc.m4
> ++++ b/aclocal/libtirpc.m4
> +@@ -26,6 +26,17 @@ AC_DEFUN([AC_LIBTIRPC], [
> +                                     [Define to 1 if your tirpc library 
> provides libtirpc_set_debug])],,
> +                          [${LIBS}])])
> + 
> ++     AS_IF([test -n "${LIBTIRPC}"],
> ++           [AC_CHECK_LIB([tirpc], [rpc_gss_getcred],
> ++                         [AC_DEFINE([HAVE_TIRPC_GSS_GETCRED], [1],
> ++                                    [Define to 1 if your tirpc library 
> provides rpc_gss_getcred])],,
> ++                         [${LIBS}])])
> ++
> ++     AS_IF([test -n "${LIBTIRPC}"],
> ++           [AC_CHECK_LIB([tirpc], [authdes_getucred],
> ++                         [AC_DEFINE([HAVE_TIRPC_AUTHDES_GETUCRED], [1],
> ++                                    [Define to 1 if your tirpc library 
> provides authdes_getucred])],,
> ++                         [${LIBS}])])
> +   AC_SUBST([AM_CPPFLAGS])
> +   AC_SUBST(LIBTIRPC)
> + 
> +diff --git a/support/include/Makefile.am b/support/include/Makefile.am
> +index 1373891a..631a84f8 100644
> +--- a/support/include/Makefile.am
> ++++ b/support/include/Makefile.am
> +@@ -10,6 +10,7 @@ noinst_HEADERS = \
> +     misc.h \
> +     nfs_mntent.h \
> +     nfs_paths.h \
> ++    nfs_ucred.h \
> +     nfsd_path.h \
> +     nfslib.h \
> +     nfsrpc.h \
> +diff --git a/support/include/nfs_ucred.h b/support/include/nfs_ucred.h
> +new file mode 100644
> +index 00000000..d58b61e4
> +--- /dev/null
> ++++ b/support/include/nfs_ucred.h
> +@@ -0,0 +1,44 @@
> ++#ifndef _NFS_UCRED_H
> ++#define _NFS_UCRED_H
> ++
> ++#include <sys/types.h>
> ++
> ++struct nfs_ucred {
> ++    uid_t uid;
> ++    gid_t gid;
> ++    int ngroups;
> ++    gid_t *groups;
> ++};
> ++
> ++struct svc_req;
> ++struct exportent;
> ++
> ++int nfs_ucred_get(struct nfs_ucred **credp, struct svc_req *rqst,
> ++              const struct exportent *ep);
> ++
> ++void nfs_ucred_squash_groups(struct nfs_ucred *cred,
> ++                         const struct exportent *ep);
> ++int nfs_ucred_reload_groups(struct nfs_ucred *cred, const struct exportent 
> *ep);
> ++int nfs_ucred_swap_effective(const struct nfs_ucred *cred,
> ++                         struct nfs_ucred **savedp);
> ++
> ++static inline void nfs_ucred_free(struct nfs_ucred *cred)
> ++{
> ++    free(cred->groups);
> ++    free(cred);
> ++}
> ++
> ++static inline void nfs_ucred_init_groups(struct nfs_ucred *cred, gid_t 
> *groups,
> ++                                     int ngroups)
> ++{
> ++    cred->groups = groups;
> ++    cred->ngroups = ngroups;
> ++}
> ++
> ++static inline void nfs_ucred_free_groups(struct nfs_ucred *cred)
> ++{
> ++    free(cred->groups);
> ++    nfs_ucred_init_groups(cred, NULL, 0);
> ++}
> ++
> ++#endif /* _NFS_UCRED_H */
> +diff --git a/support/misc/Makefile.am b/support/misc/Makefile.am
> +index 8b0e9db9..ea970064 100644
> +--- a/support/misc/Makefile.am
> ++++ b/support/misc/Makefile.am
> +@@ -2,6 +2,6 @@
> + 
> + noinst_LIBRARIES = libmisc.a
> + libmisc_a_SOURCES = tcpwrapper.c from_local.c mountpoint.c misc.c \
> +-                nfsd_path.c workqueue.c xstat.c
> ++                nfsd_path.c ucred.c workqueue.c xstat.c
> + 
> + MAINTAINERCLEANFILES = Makefile.in
> +diff --git a/support/misc/ucred.c b/support/misc/ucred.c
> +new file mode 100644
> +index 00000000..92d97912
> +--- /dev/null
> ++++ b/support/misc/ucred.c
> +@@ -0,0 +1,162 @@
> ++#ifdef HAVE_CONFIG_H
> ++#include <config.h>
> ++#endif
> ++
> ++#include <alloca.h>
> ++#include <errno.h>
> ++#include <pwd.h>
> ++#include <stdlib.h>
> ++#include <unistd.h>
> ++#include <grp.h>
> ++
> ++#include "exportfs.h"
> ++#include "nfs_ucred.h"
> ++
> ++#include "xlog.h"
> ++
> ++void nfs_ucred_squash_groups(struct nfs_ucred *cred, const struct exportent 
> *ep)
> ++{
> ++    int i;
> ++
> ++    if (!(ep->e_flags & NFSEXP_ROOTSQUASH))
> ++            return;
> ++    if (cred->gid == 0)
> ++            cred->gid = ep->e_anongid;
> ++    for (i = 0; i < cred->ngroups; i++) {
> ++            if (cred->groups[i] == 0)
> ++                    cred->groups[i] = ep->e_anongid;
> ++    }
> ++}
> ++
> ++static int nfs_ucred_init_effective(struct nfs_ucred *cred)
> ++{
> ++    int ngroups = getgroups(0, NULL);
> ++
> ++    if (ngroups > 0) {
> ++            size_t sz = ngroups * sizeof(gid_t);
> ++            gid_t *groups = malloc(sz);
> ++            if (groups == NULL)
> ++                    return ENOMEM;
> ++            if (getgroups(ngroups, groups) == -1) {
> ++                    free(groups);
> ++                    return errno;
> ++            }
> ++            nfs_ucred_init_groups(cred, groups, ngroups);
> ++    } else
> ++            nfs_ucred_init_groups(cred, NULL, 0);
> ++    cred->uid = geteuid();
> ++    cred->gid = getegid();
> ++    return 0;
> ++}
> ++
> ++static size_t nfs_ucred_getpw_r_size_max(void)
> ++{
> ++    long buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
> ++
> ++    if (buflen == -1)
> ++            return 16384;
> ++    return buflen;
> ++}
> ++
> ++int nfs_ucred_reload_groups(struct nfs_ucred *cred, const struct exportent 
> *ep)
> ++{
> ++    struct passwd pwd, *pw;
> ++    uid_t uid = cred->uid;
> ++    gid_t gid = cred->gid;
> ++    size_t buflen;
> ++    char *buf;
> ++    int ngroups = 0;
> ++    int ret;
> ++
> ++    if (ep->e_flags & (NFSEXP_ALLSQUASH | NFSEXP_ROOTSQUASH) &&
> ++        (int)uid == ep->e_anonuid)
> ++            return 0;
> ++    buflen = nfs_ucred_getpw_r_size_max();
> ++    buf = alloca(buflen);
> ++    ret = getpwuid_r(uid, &pwd, buf, buflen, &pw);
> ++    if (ret != 0)
> ++            return ret;
> ++    if (!pw)
> ++            return ENOENT;
> ++    if (getgrouplist(pw->pw_name, gid, NULL, &ngroups) == -1 &&
> ++        ngroups > 0) {
> ++            gid_t *groups = malloc(ngroups * sizeof(groups[0]));
> ++            if (groups == NULL)
> ++                    return ENOMEM;
> ++            if (getgrouplist(pw->pw_name, gid, groups, &ngroups) == -1) {
> ++                    free(groups);
> ++                    return ENOMEM;
> ++            }
> ++            free(cred->groups);
> ++            nfs_ucred_init_groups(cred, groups, ngroups);
> ++            nfs_ucred_squash_groups(cred, ep);
> ++    } else
> ++            nfs_ucred_free_groups(cred);
> ++    return 0;
> ++}
> ++
> ++static int nfs_ucred_set_effective(const struct nfs_ucred *cred,
> ++                               const struct nfs_ucred *saved)
> ++{
> ++    uid_t suid = saved ? saved->uid : geteuid();
> ++    gid_t sgid = saved ? saved->gid : getegid();
> ++    int ret;
> ++
> ++    /* Start with a privileged effective user */
> ++    if (setresuid(-1, 0, -1) < 0) {
> ++            xlog(L_WARNING, "can't change privileged user %u-%u. %s",
> ++                 geteuid(), getegid(), strerror(errno));
> ++            return errno;
> ++    }
> ++
> ++    if (setgroups(cred->ngroups, cred->groups) == -1) {
> ++            xlog(L_WARNING, "can't change groups for user %u-%u. %s",
> ++                 geteuid(), getegid(), strerror(errno));
> ++            return errno;
> ++    }
> ++    if (setresgid(-1, cred->gid, sgid) == -1) {
> ++            xlog(L_WARNING, "can't change gid for user %u-%u. %s",
> ++                 geteuid(), getegid(), strerror(errno));
> ++            ret = errno;
> ++            goto restore_groups;
> ++    }
> ++    if (setresuid(-1, cred->uid, suid) == -1) {
> ++            xlog(L_WARNING, "can't change uid for user %u-%u. %s",
> ++                 geteuid(), getegid(), strerror(errno));
> ++            ret = errno;
> ++            goto restore_gid;
> ++    }
> ++    return 0;
> ++restore_gid:
> ++    if (setresgid(-1, sgid, -1) < 0) {
> ++            xlog(L_WARNING, "can't restore privileged user %u-%u. %s",
> ++                 geteuid(), getegid(), strerror(errno));
> ++    }
> ++restore_groups:
> ++    if (saved)
> ++            setgroups(saved->ngroups, saved->groups);
> ++    else
> ++            setgroups(0, NULL);
> ++    return ret;
> ++}
> ++
> ++int nfs_ucred_swap_effective(const struct nfs_ucred *cred,
> ++                         struct nfs_ucred **savedp)
> ++{
> ++    struct nfs_ucred *saved = malloc(sizeof(*saved));
> ++    int ret;
> ++
> ++    if (saved == NULL)
> ++            return ENOMEM;
> ++    ret = nfs_ucred_init_effective(saved);
> ++    if (ret != 0) {
> ++            free(saved);
> ++            return ret;
> ++    }
> ++    ret = nfs_ucred_set_effective(cred, saved);
> ++    if (savedp == NULL || ret != 0)
> ++            nfs_ucred_free(saved);
> ++    else
> ++            *savedp = saved;
> ++    return ret;
> ++}
> +diff --git a/support/nfs/Makefile.am b/support/nfs/Makefile.am
> +index 2e1577cc..f6921265 100644
> +--- a/support/nfs/Makefile.am
> ++++ b/support/nfs/Makefile.am
> +@@ -7,7 +7,7 @@ libnfs_la_SOURCES = exports.c rmtab.c xio.c rpcmisc.c 
> rpcdispatch.c \
> +                xcommon.c wildmat.c mydaemon.c \
> +                rpc_socket.c getport.c \
> +                svc_socket.c cacheio.c closeall.c nfs_mntent.c \
> +-               svc_create.c atomicio.c strlcat.c strlcpy.c
> ++               svc_create.c atomicio.c strlcat.c strlcpy.c ucred.c
> + libnfs_la_LIBADD = libnfsconf.la
> + libnfs_la_CPPFLAGS = $(AM_CPPFLAGS) $(CPPFLAGS) 
> -I$(top_srcdir)/support/reexport
> + 
> +diff --git a/support/nfs/ucred.c b/support/nfs/ucred.c
> +new file mode 100644
> +index 00000000..6ea8efdf
> +--- /dev/null
> ++++ b/support/nfs/ucred.c
> +@@ -0,0 +1,147 @@
> ++#ifdef HAVE_CONFIG_H
> ++#include <config.h>
> ++#endif
> ++
> ++#include <errno.h>
> ++#include <stdlib.h>
> ++#include <unistd.h>
> ++#include <rpc/rpc.h>
> ++
> ++#include "exportfs.h"
> ++#include "nfs_ucred.h"
> ++
> ++#ifdef HAVE_TIRPC_GSS_GETCRED
> ++#include <rpc/rpcsec_gss.h>
> ++#endif /* HAVE_TIRPC_GSS_GETCRED */
> ++#ifdef HAVE_TIRPC_AUTHDES_GETUCRED
> ++#include <rpc/auth_des.h>
> ++#endif /* HAVE_TIRPC_AUTHDES_GETUCRED */
> ++
> ++static int nfs_ucred_copy_cred(struct nfs_ucred *cred, uid_t uid, gid_t gid,
> ++                           const gid_t *groups, int ngroups)
> ++{
> ++    if (ngroups > 0) {
> ++            size_t sz = ngroups * sizeof(groups[0]);
> ++            cred->groups = malloc(sz);
> ++            if (cred->groups == NULL)
> ++                    return ENOMEM;
> ++            cred->ngroups = ngroups;
> ++            memcpy(cred->groups, groups, sz);
> ++    } else
> ++            nfs_ucred_init_groups(cred, NULL, 0);
> ++    cred->uid = uid;
> ++    cred->gid = gid;
> ++    return 0;
> ++}
> ++
> ++static int nfs_ucred_init_cred_squashed(struct nfs_ucred *cred,
> ++                                    const struct exportent *ep)
> ++{
> ++    cred->uid = ep->e_anonuid;
> ++    cred->gid = ep->e_anongid;
> ++    nfs_ucred_init_groups(cred, NULL, 0);
> ++    return 0;
> ++}
> ++
> ++static int nfs_ucred_init_cred(struct nfs_ucred *cred, uid_t uid, gid_t gid,
> ++                           const gid_t *groups, int ngroups,
> ++                           const struct exportent *ep)
> ++{
> ++    if (ep->e_flags & NFSEXP_ALLSQUASH) {
> ++            nfs_ucred_init_cred_squashed(cred, ep);
> ++    } else if (ep->e_flags & NFSEXP_ROOTSQUASH && uid == 0) {
> ++            nfs_ucred_init_cred_squashed(cred, ep);
> ++            if (gid != 0)
> ++                    cred->gid = gid;
> ++    } else {
> ++            int ret = nfs_ucred_copy_cred(cred, uid, gid, groups, ngroups);
> ++            if (ret != 0)
> ++                    return ret;
> ++            nfs_ucred_squash_groups(cred, ep);
> ++    }
> ++    return 0;
> ++}
> ++
> ++static int nfs_ucred_init_null(struct nfs_ucred *cred,
> ++                           const struct exportent *ep)
> ++{
> ++    return nfs_ucred_init_cred_squashed(cred, ep);
> ++}
> ++
> ++static int nfs_ucred_init_unix(struct nfs_ucred *cred, struct svc_req *rqst,
> ++                           const struct exportent *ep)
> ++{
> ++    struct authunix_parms *aup;
> ++
> ++    aup = (struct authunix_parms *)rqst->rq_clntcred;
> ++    return nfs_ucred_init_cred(cred, aup->aup_uid, aup->aup_gid,
> ++                               aup->aup_gids, aup->aup_len, ep);
> ++}
> ++
> ++#ifdef HAVE_TIRPC_GSS_GETCRED
> ++static int nfs_ucred_init_gss(struct nfs_ucred *cred, struct svc_req *rqst,
> ++                          const struct exportent *ep)
> ++{
> ++    rpc_gss_ucred_t *gss_ucred = NULL;
> ++
> ++    if (!rpc_gss_getcred(rqst, NULL, &gss_ucred, NULL) || gss_ucred == NULL)
> ++            return EINVAL;
> ++    return nfs_ucred_init_cred(cred, gss_ucred->uid, gss_ucred->gid,
> ++                               gss_ucred->gidlist, gss_ucred->gidlen, ep);
> ++}
> ++#endif /* HAVE_TIRPC_GSS_GETCRED */
> ++
> ++#ifdef HAVE_TIRPC_AUTHDES_GETUCRED
> ++int authdes_getucred(struct authdes_cred *adc, uid_t *uid, gid_t *gid,
> ++                 int *grouplen, gid_t *groups);
> ++
> ++static int nfs_ucred_init_des(struct nfs_ucred *cred, struct svc_req *rqst,
> ++                          const struct exportent *ep)
> ++{
> ++    struct authdes_cred *des_cred;
> ++    uid_t uid;
> ++    gid_t gid;
> ++    int grouplen;
> ++    gid_t groups[NGROUPS];
> ++
> ++    des_cred = (struct authdes_cred *)rqst->rq_clntcred;
> ++    if (!authdes_getucred(des_cred, &uid, &gid, &grouplen, &groups[0]))
> ++            return EINVAL;
> ++    return nfs_ucred_init_cred(cred, uid, gid, groups, grouplen, ep);
> ++}
> ++#endif /* HAVE_TIRPC_AUTHDES_GETUCRED */
> ++
> ++int nfs_ucred_get(struct nfs_ucred **credp, struct svc_req *rqst,
> ++              const struct exportent *ep)
> ++{
> ++    struct nfs_ucred *cred = malloc(sizeof(*cred));
> ++    int ret;
> ++
> ++    *credp = NULL;
> ++    if (cred == NULL)
> ++            return ENOMEM;
> ++    switch (rqst->rq_cred.oa_flavor) {
> ++    case AUTH_UNIX:
> ++            ret = nfs_ucred_init_unix(cred, rqst, ep);
> ++            break;
> ++#ifdef HAVE_TIRPC_GSS_GETCRED
> ++    case RPCSEC_GSS:
> ++            ret = nfs_ucred_init_gss(cred, rqst, ep);
> ++            break;
> ++#endif /* HAVE_TIRPC_GSS_GETCRED */
> ++#ifdef HAVE_TIRPC_AUTHDES_GETUCRED
> ++    case AUTH_DES:
> ++            ret = nfs_ucred_init_des(cred, rqst, ep);
> ++            break;
> ++#endif /* HAVE_TIRPC_AUTHDES_GETUCRED */
> ++    default:
> ++            ret = nfs_ucred_init_null(cred, ep);
> ++            break;
> ++    }
> ++    if (ret == 0) {
> ++            *credp = cred;
> ++            return 0;
> ++    }
> ++    free(cred);
> ++    return ret;
> ++}
> +-- 
> +2.44.4
> +
> diff --git 
> a/meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801.patch 
> b/meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801.patch
> new file mode 100644
> index 0000000000..9f01604af0
> --- /dev/null
> +++ b/meta/recipes-connectivity/nfs-utils/nfs-utils/CVE-2025-12801.patch
> @@ -0,0 +1,254 @@
> +From a94b2b6002f31acc5a66893b7c6d368c6b7b8806 Mon Sep 17 00:00:00 2001
> +From: Trond Myklebust <[email protected]>
> +Date: Thu, 5 Mar 2026 10:41:02 -0500
> +Subject: [PATCH] Fix access checks when mounting subdirectories in NFSv3
> +
> +If a NFSv3 client asks to mount a subdirectory of one of the exported
> +directories, then apply the RPC credential together with any root
> +or all squash rules that would apply to the client in question.
> +
> +CVE: CVE-2025-12801
> +Upstream-Status: Backport 
> [https://git.linux-nfs.org/?p=steved/nfs-utils.git;a=commit;h=f36bd900a899088ca1925de079bd58d6205a1f3c]
> +
> +Reviewed-by: Jeff Layton <[email protected]>
> +Signed-off-by: Trond Myklebust <[email protected]>
> +Signed-off-by: Scott Mayhew <[email protected]>
> +Signed-off-by: Steve Dickson <[email protected]>
> +(cherry picked from commit f36bd900a899088ca1925de079bd58d6205a1f3c)
> +Signed-off-by: Sudhir Dumbhare <[email protected]>
> +---
> + nfs.conf                    |  1 +
> + support/include/nfsd_path.h |  9 ++++++++-
> + support/misc/nfsd_path.c    | 32 ++++++++++++++++++++++++++++++--
> + utils/mountd/mountd.c       | 28 ++++++++++++++++++++++++++--
> + utils/mountd/mountd.man     | 26 ++++++++++++++++++++++++++
> + 5 files changed, 91 insertions(+), 5 deletions(-)
> +
> +diff --git a/nfs.conf b/nfs.conf
> +index 323f072b..e08cd9a9 100644
> +--- a/nfs.conf
> ++++ b/nfs.conf
> +@@ -45,6 +45,7 @@
> + # ttl=1800
> + [mountd]
> + # debug="all|auth|call|general|parse"
> ++# apply-root-cred=n
> + # manage-gids=n
> + # descriptors=0
> + # port=0
> +diff --git a/support/include/nfsd_path.h b/support/include/nfsd_path.h
> +index 3e5a2f5d..06c0f2f4 100644
> +--- a/support/include/nfsd_path.h
> ++++ b/support/include/nfsd_path.h
> +@@ -9,6 +9,7 @@
> + struct file_handle;
> + struct statfs;
> + struct nfsd_task_t;
> ++struct nfs_ucred;
> + 
> + void                nfsd_path_init(void);
> + 
> +@@ -18,7 +19,8 @@ char *             nfsd_path_prepend_dir(const char *dir, 
> const char *pathname);
> + 
> + int                 nfsd_path_stat(const char *pathname, struct stat 
> *statbuf);
> + int                 nfsd_path_lstat(const char *pathname, struct stat 
> *statbuf);
> +-int         nfsd_openat(int dirfd, const char *path, int flags);
> ++int         nfsd_cred_openat(const struct nfs_ucred *cred, int dirfd,
> ++                             const char *path, int flags);
> + 
> + int         nfsd_path_statfs(const char *pathname,
> +                                struct statfs *statbuf);
> +@@ -31,4 +33,9 @@ ssize_t            nfsd_path_write(int fd, void* buf, 
> size_t len);
> + int         nfsd_name_to_handle_at(int fd, const char *path,
> +                                    struct file_handle *fh,
> +                                    int *mount_id, int flags);
> ++
> ++static inline int nfsd_openat(int dirfd, const char *path, int flags)
> ++{
> ++    return nfsd_cred_openat(NULL, dirfd, path, flags);
> ++}
> + #endif
> +diff --git a/support/misc/nfsd_path.c b/support/misc/nfsd_path.c
> +index dfe88e4f..6466666d 100644
> +--- a/support/misc/nfsd_path.c
> ++++ b/support/misc/nfsd_path.c
> +@@ -17,6 +17,7 @@
> + #include "xstat.h"
> + #include "nfslib.h"
> + #include "nfsd_path.h"
> ++#include "nfs_ucred.h"
> + #include "workqueue.h"
> + 
> + static struct xthread_workqueue *nfsd_wq = NULL;
> +@@ -204,6 +205,7 @@ nfsd_realpath(const char *path, char *resolved_buf)
> + }
> + 
> + struct nfsd_openat_t {
> ++    const struct nfs_ucred *cred;
> +     const char *path;
> +     int dirfd;
> +     int flags;
> +@@ -220,15 +222,41 @@ static void nfsd_openatfunc(void *data)
> +             d->res_error = errno;
> + }
> + 
> +-int nfsd_openat(int dirfd, const char *path, int flags)
> ++static void nfsd_cred_openatfunc(void *data)
> ++{
> ++    struct nfsd_openat_t *d = data;
> ++    struct nfs_ucred *saved = NULL;
> ++    int ret;
> ++
> ++    ret = nfs_ucred_swap_effective(d->cred, &saved);
> ++    if (ret != 0) {
> ++            d->res_fd = -1;
> ++            d->res_error = ret;
> ++            return;
> ++    }
> ++
> ++    nfsd_openatfunc(data);
> ++
> ++    if (saved != NULL) {
> ++            nfs_ucred_swap_effective(saved, NULL);
> ++            nfs_ucred_free(saved);
> ++    }
> ++}
> ++
> ++int nfsd_cred_openat(const struct nfs_ucred *cred, int dirfd, const char 
> *path,
> ++                 int flags)
> + {
> +     struct nfsd_openat_t open_buf = {
> ++            .cred = cred,
> +             .path = path,
> +             .dirfd = dirfd,
> +             .flags = flags,
> +     };
> + 
> +-    nfsd_run_task(nfsd_openatfunc, &open_buf);
> ++    if (cred)
> ++            nfsd_run_task(nfsd_cred_openatfunc, &open_buf);
> ++    else
> ++            nfsd_run_task(nfsd_openatfunc, &open_buf);
> +     if (open_buf.res_fd == -1)
> +             errno = open_buf.res_error;
> +     return open_buf.res_fd;
> +diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c
> +index f43ebef5..6e6777cd 100644
> +--- a/utils/mountd/mountd.c
> ++++ b/utils/mountd/mountd.c
> +@@ -31,6 +31,7 @@
> + #include "nfsd_path.h"
> + #include "nfslib.h"
> + #include "export.h"
> ++#include "nfs_ucred.h"
> + 
> + extern void my_svc_run(void);
> + 
> +@@ -40,6 +41,7 @@ static struct nfs_fh_len *get_rootfh(struct svc_req *, 
> dirpath *, nfs_export **,
> + 
> + int reverse_resolve = 0;
> + int manage_gids;
> ++int apply_root_cred;
> + int use_ipaddr = -1;
> + 
> + /* PRC: a high-availability callout program can be specified with -H
> +@@ -74,9 +76,10 @@ static struct option longopts[] =
> +     { "log-auth", 0, 0, 'l'},
> +     { "cache-use-ipaddr", 0, 0, 'i'},
> +     { "ttl", 1, 0, 'T'},
> ++    { "apply-root-cred", 0, 0, 'c' },
> +     { NULL, 0, 0, 0 }
> + };
> +-static char shortopts[] = "o:nFd:p:P:hH:N:V:vurs:t:gliT:";
> ++static char shortopts[] = "o:nFd:p:P:hH:N:V:vurs:t:gliT:c";
> + 
> + #define NFSVERSBIT(vers)    (0x1 << (vers - 1))
> + #define NFSVERSBIT_ALL              (NFSVERSBIT(2) | NFSVERSBIT(3) | 
> NFSVERSBIT(4))
> +@@ -453,11 +456,27 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, 
> nfs_export **expret,
> +     while (*subpath == '/')
> +             subpath++;
> +     if (*subpath != '\0') {
> ++            struct nfs_ucred *cred = NULL;
> +             int fd;
> + 
> ++            /* Load the user cred */
> ++            if (!apply_root_cred) {
> ++                    nfs_ucred_get(&cred, rqstp, &exp->m_export);
> ++                    if (cred == NULL) {
> ++                            xlog(L_WARNING, "can't retrieve credential");
> ++                            *error = MNT3ERR_ACCES;
> ++                            close(dirfd);
> ++                            return NULL;
> ++                    }
> ++                    if (manage_gids)
> ++                            nfs_ucred_reload_groups(cred, &exp->m_export);
> ++            }
> ++
> +             /* Just perform a lookup of the path */
> +-            fd = nfsd_openat(dirfd, subpath, O_PATH);
> ++            fd = nfsd_cred_openat(cred, dirfd, subpath, O_PATH);
> +             close(dirfd);
> ++            if (cred)
> ++                    nfs_ucred_free(cred);
> +             if (fd == -1) {
> +                     xlog(L_WARNING, "can't open exported dir %s: %s", p,
> +                          strerror(errno));
> +@@ -681,6 +700,8 @@ read_mountd_conf(char **argv)
> +     ttl = conf_get_num("mountd", "ttl", default_ttl);
> +     if (ttl > 0)
> +             default_ttl = ttl;
> ++    apply_root_cred = conf_get_bool("mountd", "apply-root-cred",
> ++                                    apply_root_cred);
> + }
> + 
> + int
> +@@ -794,6 +815,9 @@ main(int argc, char **argv)
> +                     }
> +                     default_ttl = ttl;
> +                     break;
> ++            case 'c':
> ++                    apply_root_cred = 1;
> ++                    break;
> +             case 0:
> +                     break;
> +             case '?':
> +diff --git a/utils/mountd/mountd.man b/utils/mountd/mountd.man
> +index a206a3e2..f4f1fc23 100644
> +--- a/utils/mountd/mountd.man
> ++++ b/utils/mountd/mountd.man
> +@@ -242,6 +242,32 @@ can support both NFS version 2 and the newer version 3.
> + Print the version of
> + .B rpc.mountd
> + and exit.
> ++.TP
> ++.B \-c " or " \-\-apply-root-cred
> ++When mountd is asked to allow a NFSv3 mount to a subdirectory of the
> ++exported directory, then it will check if the user asking to mount has
> ++lookup rights to the directories below that exported directory. When
> ++performing the check, mountd will apply any root squash or all squash
> ++rules that were specified for that client.
> ++
> ++Performing lookup checks as the user requires that the mountd daemon
> ++be run as root or that it be given CAP_SETUID and CAP_SETGID privileges
> ++so that it can change its own effective user and effective group settings.
> ++When troubleshooting, please also note that LSM frameworks such as SELinux
> ++can sometimes prevent the daemon from changing the effective user/groups
> ++despite the capability settings.
> ++
> ++In earlier versions of mountd, the same checks were performed using the
> ++mountd daemon's root privileges, meaning that it could authorise access
> ++to directories that are not normally accessible to the user requesting
> ++to mount them. This option enables that legacy behaviour.
> ++
> ++.BR Note:
> ++If there is a need to provide access to specific subdirectories that
> ++are not normally accessible to a client, it is always possible to add
> ++export entries that explicitly grant such access. That ability does
> ++not depend on this option being enabled.
> ++
> + .TP
> + .B \-g " or " \-\-manage-gids
> + Accept requests from the kernel to map user id numbers into  lists of
> +-- 
> +2.35.6
> +
> diff --git a/meta/recipes-connectivity/nfs-utils/nfs-utils_2.6.4.bb 
> b/meta/recipes-connectivity/nfs-utils/nfs-utils_2.6.4.bb
> index 2f2644f9a8..91c74fe5ef 100644
> --- a/meta/recipes-connectivity/nfs-utils/nfs-utils_2.6.4.bb
> +++ b/meta/recipes-connectivity/nfs-utils/nfs-utils_2.6.4.bb
> @@ -33,6 +33,12 @@ SRC_URI = 
> "${KERNELORG_MIRROR}/linux/utils/nfs-utils/${PV}/nfs-utils-${PV}.tar.x
>             file://0001-locktest-Makefile.am-Do-not-use-build-flags.patch \
>             file://0001-tools-locktest-Use-intmax_t-to-print-off_t.patch \
>             
> file://0001-reexport.h-Include-unistd.h-to-compile-with-musl.patch \
> +           file://CVE-2025-12801-dependent_p1.patch \
> +           file://CVE-2025-12801-dependent_p2.patch \
> +           file://CVE-2025-12801-dependent_p3.patch \
> +           file://CVE-2025-12801-dependent_p4.patch \
> +           file://CVE-2025-12801.patch \
> +           file://CVE-2025-12801-build-fix.patch \
>             "
>  SRC_URI[sha256sum] = 
> "01b3b0fb9c7d0bbabf5114c736542030748c788ec2fd9734744201e9b0a1119d"
>  


-- 
Yoann Congal
Smile ECS

-=-=-=-=-=-=-=-=-=-=-=-
Links: You receive all messages sent to this group.
View/Reply Online (#239346): 
https://lists.openembedded.org/g/openembedded-core/message/239346
Mute This Topic: https://lists.openembedded.org/mt/119938377/21656
Group Owner: [email protected]
Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub 
[[email protected]]
-=-=-=-=-=-=-=-=-=-=-=-

Reply via email to