Linux Security Modules development
 help / color / mirror / Atom feed
* Re: [PATCH v2] NFSv4.2: fix nfs4_listxattr size accounting
From: Paul Moore @ 2026-07-15 20:41 UTC (permalink / raw)
  To: Scott Mayhew
  Cc: linux-nfs, Anna Schumaker, jlayton, chuck.lever, Stephen Smalley,
	Achilles Gaikwad, Trond Myklebust, linux-security-module, selinux
In-Reply-To: <alaxhbWbFMZyC8VQ@smayhew-thinkpadp1gen4i.remote.csb>

On Tue, Jul 14, 2026 at 6:00 PM Scott Mayhew <smayhew@redhat.com> wrote:
> On Tue, 14 Jul 2026, Paul Moore wrote:
> > On Fri, Jul 10, 2026 at 6:20 PM Paul Moore <paul@paul-moore.com> wrote:
> > >
> > > ... and scratch that, the offending commit was that one.
> > >
> > >    commit 01c2305795a3b6b164df48e72b12022a68fd60c1
> > >    Author: Jeff Layton <jlayton@kernel.org>
> > >    Date:   Wed Mar 25 10:40:32 2026 -0400
> > >
> > >    nfsd: add netlink upcall for the nfsd.fh cache
> > >
> > >    Add netlink-based cache upcall support for the expkey (nfsd.fh) cache,
> > >    following the same pattern as the existing svc_export netlink support.
> > >
> > >    Add expkey to the cache-type enum, a new expkey attribute-set with
> > >    client, fsidtype, fsid, negative, expiry, and path fields, and the
> > >    expkey-get-reqs / expkey-set-reqs operations to the nfsd YAML spec
> > >    and generated headers.
> > >
> > >    Implement nfsd_nl_expkey_get_reqs_dumpit() which snapshots pending
> > >    expkey cache requests and sends each entry's seqno, client name,
> > >    fsidtype, and fsid over netlink.
> > >
> > >    Implement nfsd_nl_expkey_set_reqs_doit() which parses expkey cache
> > >    responses from userspace (client, fsidtype, fsid, expiry, and path
> > >    or negative flag) and updates the cache via svc_expkey_lookup() /
> > >    svc_expkey_update().
> > >
> > >    Wire up the expkey_notify() callback in svc_expkey_cache_template
> > >    so cache misses trigger NFSD_CMD_CACHE_NOTIFY multicast events with
> > >    NFSD_CACHE_TYPE_EXPKEY.
> > >
> > >    Signed-off-by: Jeff Layton <jlayton@kernel.org>
> > >    Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
> >
> > Playing around with it some this morning on a current Fedora Rawhide
> > system, I can reproduce the problem with a simple command line:
> >
> >  %  mount -t nfs localhost:/mnt/test /mnt/nfs_test
> >  mount.nfs: Connection refused for localhost:/mnt/test on /mnt/nfs_test
> >
> > ... adding an explicit "vers={4,4.1,4.2}" has no effect; versions 2
> > and 3 are no supported on my kernel builds.  There is nothing obvious
> > in dmesg.  I've run with SELinux both in permissive mode and disabled
> > and encountered the same problem.  This doesn't appear to be related
> > to SELinux, it may simply be that we are the first ones to hit this.
> >
> > As there was some earlier discussion about this being a wonky
> > interaction with userspace, here are some of the relevant packages on
> > my system:
> >
> > nfs-common-utils-2.9.1-4.rc4.fc45.x86_64
> > nfs-client-utils-2.9.1-4.rc4.fc45.x86_64
> > nfsv4-client-utils-2.9.1-4.rc4.fc45.x86_64
> > nfs-utils-2.9.1-4.rc4.fc45.x86_64
> >
> > My next step is to try disabling portions of the NFS file handle cache
> > upcall to see if that is the issue, but it would be nice if the NFS
> > devs could take a look at this too.  I'm happy to test things out or
> > answer any questions about my test system.
>
> Pardon the dumb question, but are you positive your NFS server is
> running?

I'm slightly embarrassed to admit that your question wasn't as dumb as
one might have thought :)

While my bisection testing used the selinux-testsuite which ensures
the NFS server starts, when I tried to create a simple reproducer to
share with the non-SELinux crowd I neglected to verify that the NFS
server was running, the directory, was exported, etc.  My apologies
for the confusion.  However, the problem was real, even if my
reproducer and error message were junk.

> FWIW the netlink upcall requires CAP_NET_ADMIN, which is blocked by the
> current selinux-policy on rawhide (and I think it's a dontaudit rule)...

As a quick test I added a quick hack to my policy (allow rule below)
and everything appears to be working again.  Thanks for the pointer!

 allow nfsd_t nfsd_t:capability { net_admin };

-- 
paul-moore.com

^ permalink raw reply

* Re: [PATCH] apparmor: replace decompress_zstd() prototype with its entity
From: Georgia Garcia @ 2026-07-15 12:14 UTC (permalink / raw)
  To: Tetsuo Handa, Maxime Bélair, John Johansen; +Cc: linux-security-module
In-Reply-To: <df2830d0-5ae8-4bb9-b11c-6051fc3784ea@I-love.SAKURA.ne.jp>

Hello,

On Sun, 2026-07-12 at 19:17 +0900, Tetsuo Handa wrote:
> Fix "undefined symbol: decompress_zstd" error caused by decompress_zstd()
> being guarded by CONFIG_SECURITY_APPARMOR_EXPORT_BINARY=y.
> 

Acked-by: Georgia Garcia <georgia.garcia@canonical.com>

> Reported-by: syzbot+1f14a35d0c73d31555e4@syzkaller.appspotmail.com
> Closes: https://syzkaller.appspot.com/bug?extid=1f14a35d0c73d31555e4
> Fixes: 17b5758bf35c ("apparmor: Initial support for compressed policies")
> Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> ---
>  security/apparmor/apparmorfs.c | 72 +++++++++++++++++-----------------
>  1 file changed, 36 insertions(+), 36 deletions(-)
> 
> diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
> index 2ae9ab94a5a91..152c7967ff1ba 100644
> --- a/security/apparmor/apparmorfs.c
> +++ b/security/apparmor/apparmorfs.c
> @@ -483,7 +483,42 @@ static struct aa_loaddata *aa_simple_write_to_buffer(const char __user *userbuf,
>  
>  	return data;
>  }
> -static int decompress_zstd(char *src, size_t slen, char *dst, size_t dlen);
> +
> +static int decompress_zstd(char *src, size_t slen, char *dst, size_t dlen)
> +{
> +	if (slen < dlen) {
> +		const size_t wksp_len = zstd_dctx_workspace_bound();
> +		zstd_dctx *ctx;
> +		void *wksp;
> +		size_t out_len;
> +		int ret = 0;
> +
> +		wksp = kvzalloc(wksp_len, GFP_KERNEL);
> +		if (!wksp) {
> +			ret = -ENOMEM;
> +			goto cleanup;
> +		}
> +		ctx = zstd_init_dctx(wksp, wksp_len);
> +		if (ctx == NULL) {
> +			ret = -ENOMEM;
> +			goto cleanup;
> +		}
> +		out_len = zstd_decompress_dctx(ctx, dst, dlen, src, slen);
> +		if (zstd_is_error(out_len)) {
> +			ret = -EINVAL;
> +			goto cleanup;
> +		}
> +cleanup:
> +		kvfree(wksp);
> +		return ret;
> +	}
> +
> +	if (dlen < slen)
> +		return -EINVAL;
> +	memcpy(dst, src, slen);
> +	return 0;
> +}
> +
>  /**
>   * aa_get_data_from_compressed - common routine for getting compressed policy
>   * from user and get both compressed and uncompressed version.
> @@ -1517,41 +1552,6 @@ SEQ_RAWDATA_FOPS(revision);
>  SEQ_RAWDATA_FOPS(hash);
>  SEQ_RAWDATA_FOPS(compressed_size);
>  
> -static int decompress_zstd(char *src, size_t slen, char *dst, size_t dlen)
> -{
> -	if (slen < dlen) {
> -		const size_t wksp_len = zstd_dctx_workspace_bound();
> -		zstd_dctx *ctx;
> -		void *wksp;
> -		size_t out_len;
> -		int ret = 0;
> -
> -		wksp = kvzalloc(wksp_len, GFP_KERNEL);
> -		if (!wksp) {
> -			ret = -ENOMEM;
> -			goto cleanup;
> -		}
> -		ctx = zstd_init_dctx(wksp, wksp_len);
> -		if (ctx == NULL) {
> -			ret = -ENOMEM;
> -			goto cleanup;
> -		}
> -		out_len = zstd_decompress_dctx(ctx, dst, dlen, src, slen);
> -		if (zstd_is_error(out_len)) {
> -			ret = -EINVAL;
> -			goto cleanup;
> -		}
> -cleanup:
> -		kvfree(wksp);
> -		return ret;
> -	}
> -
> -	if (dlen < slen)
> -		return -EINVAL;
> -	memcpy(dst, src, slen);
> -	return 0;
> -}
> -
>  static ssize_t rawdata_read(struct file *file, char __user *buf, size_t size,
>  			    loff_t *ppos)
>  {


^ permalink raw reply

* Re: [BUG] Landlock denies LANDLOCK_ACCESS_FS_EXECUTE despite a correctly-anchored PathBeneath rule (contradicts selftest layout1.execute)
From: Günther Noack @ 2026-07-15  8:39 UTC (permalink / raw)
  To: Ken Grimes
  Cc: linux-security-module@vger.kernel.org, Mickaël Salaün,
	landlock
In-Reply-To: <P8RYlSi7C7oum2lk1F3PumYSvaUyd0Sn_VwUYAcWwjbQxSt_Rau30Aa1oLIEnLZ2iafcsFmEG5C4LTeBWCyd9vCx94FYeTX_OJdHoERcWSI=@kengrimes.com>

Hello Ken!

(Also adding landlock@lists.linux.dev to CC)

On Tue, Jul 14, 2026 at 09:07:56PM +0000, Ken Grimes wrote:
> Hey all, this is my first bug report for linux. The issue was discovered 
> alongside llm-assisted coding on a downstream project. The 
> investigation/testing of the bug was a mostly manual process so I could 
> be sure this was something real. Please let me know if I can provide any
> further details or assistance. Hope this is helpful, thank you for all of your
> hard work!

Welcome and thanks for reporting your first issue!

I believe the issue you are observing is that the /bin/true program you are
starting is a dynamically linked executable.  As such, executing it requires
both the LANDLOCK_ACCESS_FS_EXECUTE right on the binary itself and on
the system's dynamic loader binary, which usually lives in /lib/ld-linux.so.*
(but there are symlinks and 32/64-bit differences at play as well, which
influence the actual final location).

You can try this out with the following experiments:

(1) Compile a "true" program statically and try using that:

    $ echo 'int main() { return 0; }' > true.c
    $ CFLAGS=-static make true

    This can be started with the test you have,
    unlike the dynamically linked version.

(2) Alternatively, add execute permissions for the dynamic loader:

    Add an additional "path beneath" rule that allow-lists the execution
    access right on /lib/ld-linux.so.2, /lib64/ld-linux-x86-64.so.2 or
    wherever else your dynamic loader is.  (You can discover the actual
    location using "ldd /bin/true".)

With either one of these two changes, your standalone reproducer program
starts working again.  Or at least it does on my machine.  If it still doesn't
work on your end that way, please let us know. :)

I admit that we should probably point this out in the Landlock documentation
for the LANDLOCK_ACCESS_FS_EXECUTE right, as dynamic linking is common and it
is a potential issue that many people might run into.

For background on the dynamic loading mechanism, see the man page ld.so(8) [1]
and the LWN article "How programs get run: ELF binaries" [2] (specifically the
section "Dynamically linked programs").

—Günther


[1] https://man7.org/linux/man-pages/man8/ld.so.8.html
[2] https://lwn.net/Articles/631631/


^ permalink raw reply

* Re: [PATCH v2] NFSv4.2: fix nfs4_listxattr size accounting
From: Scott Mayhew @ 2026-07-14 22:00 UTC (permalink / raw)
  To: Paul Moore
  Cc: linux-nfs, Anna Schumaker, jlayton, chuck.lever, Stephen Smalley,
	Achilles Gaikwad, Trond Myklebust, linux-security-module, selinux
In-Reply-To: <CAHC9VhR+pWFnt=1pBJTky78br7NsfVcZE=V4UkfkfTbMvBo+mQ@mail.gmail.com>

On Tue, 14 Jul 2026, Paul Moore wrote:

> On Fri, Jul 10, 2026 at 6:20 PM Paul Moore <paul@paul-moore.com> wrote:
> >
> > ... and scratch that, the offending commit was that one.
> >
> >    commit 01c2305795a3b6b164df48e72b12022a68fd60c1
> >    Author: Jeff Layton <jlayton@kernel.org>
> >    Date:   Wed Mar 25 10:40:32 2026 -0400
> >
> >    nfsd: add netlink upcall for the nfsd.fh cache
> >
> >    Add netlink-based cache upcall support for the expkey (nfsd.fh) cache,
> >    following the same pattern as the existing svc_export netlink support.
> >
> >    Add expkey to the cache-type enum, a new expkey attribute-set with
> >    client, fsidtype, fsid, negative, expiry, and path fields, and the
> >    expkey-get-reqs / expkey-set-reqs operations to the nfsd YAML spec
> >    and generated headers.
> >
> >    Implement nfsd_nl_expkey_get_reqs_dumpit() which snapshots pending
> >    expkey cache requests and sends each entry's seqno, client name,
> >    fsidtype, and fsid over netlink.
> >
> >    Implement nfsd_nl_expkey_set_reqs_doit() which parses expkey cache
> >    responses from userspace (client, fsidtype, fsid, expiry, and path
> >    or negative flag) and updates the cache via svc_expkey_lookup() /
> >    svc_expkey_update().
> >
> >    Wire up the expkey_notify() callback in svc_expkey_cache_template
> >    so cache misses trigger NFSD_CMD_CACHE_NOTIFY multicast events with
> >    NFSD_CACHE_TYPE_EXPKEY.
> >
> >    Signed-off-by: Jeff Layton <jlayton@kernel.org>
> >    Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
> 
> Playing around with it some this morning on a current Fedora Rawhide
> system, I can reproduce the problem with a simple command line:
> 
>  %  mount -t nfs localhost:/mnt/test /mnt/nfs_test
>  mount.nfs: Connection refused for localhost:/mnt/test on /mnt/nfs_test
> 
> ... adding an explicit "vers={4,4.1,4.2}" has no effect; versions 2
> and 3 are no supported on my kernel builds.  There is nothing obvious
> in dmesg.  I've run with SELinux both in permissive mode and disabled
> and encountered the same problem.  This doesn't appear to be related
> to SELinux, it may simply be that we are the first ones to hit this.
> 
> As there was some earlier discussion about this being a wonky
> interaction with userspace, here are some of the relevant packages on
> my system:
> 
> nfs-common-utils-2.9.1-4.rc4.fc45.x86_64
> nfs-client-utils-2.9.1-4.rc4.fc45.x86_64
> nfsv4-client-utils-2.9.1-4.rc4.fc45.x86_64
> nfs-utils-2.9.1-4.rc4.fc45.x86_64
> 
> My next step is to try disabling portions of the NFS file handle cache
> upcall to see if that is the issue, but it would be nice if the NFS
> devs could take a look at this too.  I'm happy to test things out or
> answer any questions about my test system.

Pardon the dumb question, but are you positive your NFS server is
running?  No errors from 'systemctl status nfs-server.service' or
'systemctl status nfs-mountd.service'?  A connection refused error
typically indicates the server isn't running.

What if you add 'no-netlink=1' to the mountd stanza in /etc/nfs.conf and
restart nfs-server.service?

FWIW the netlink upcall requires CAP_NET_ADMIN, which is blocked by the
current selinux-policy on rawhide (and I think it's a dontaudit rule)...
but you said you've disabled SELinux and reproduced your problem, so I'm
scratching my head.  Plus the symptom I ran into was the mount command
hanging, not a 'connection refused' error.  At any rate I filed
https://github.com/fedora-selinux/selinux-policy/pull/3278 to update the
selinux-policy (no movement on that yet) and the most recent mountd
patch improves the fallback to the old proc interface, but I don't think
it's been added to Fedora yet either.

-Scott

> 
> -- 
> paul-moore.com
> 


^ permalink raw reply

* [BUG] Landlock denies LANDLOCK_ACCESS_FS_EXECUTE despite a correctly-anchored PathBeneath rule (contradicts selftest layout1.execute)
From: Ken Grimes @ 2026-07-14 21:07 UTC (permalink / raw)
  To: linux-security-module@vger.kernel.org
  Cc: Mickaël Salaün, Günther Noack


[-- Attachment #1.1.1: Type: text/plain, Size: 16855 bytes --]

Hey all, this is my first bug report for linux. The issue was discovered 
alongside llm-assisted coding on a downstream project. The 
investigation/testing of the bug was a mostly manual process so I could 
be sure this was something real. Please let me know if I can provide any
further details or assistance. Hope this is helpful, thank you for all of your
hard work!


Summary
-------

On Linux 7.1.3 (Fedora 44, Landlock ABI version 9), a minimal, correctly
constructed Landlock ruleset denies execve()/execveat() of that file with
EACCES. This directly contradicts the expected behavior of the official
selftest assertion: tools/testing/selftests/landlock/fs_test.c's
TEST_F_FORK(layout1, execute), specifically the file1_s1d2 case,
expects exactly this shape of rule to permit execution
(test_execute(_metadata, 0, file1_s1d2)).

This was confirmed two ways: a minimal standalone reproduction (below),
and by building and running your own unmodified
tools/testing/selftests/landlock/fs_test.c from the v7.1.3 tag (exact
match to the running kernel and its kernel-headers package) against this
kernel. layout1.execute fails on this kernel:

    #  RUN           layout1.execute ...
    # fs_test.c:1949:execute:Expected err ? -1 : 0 (0) == execve(path, argv, NULL) (-1)
    # fs_test.c:1951:execute:Failed to execute "tmp/s1d1/s1d2/f1": Permission denied
    # execute: Test terminated by assertion
    #          FAIL  layout1.execute
    not ok 29 layout1.execute

tmp/s1d1/s1d2/f1 is file1_s1d2 is the file living directly inside
dir_s1d2, which is the directory the test's own PathBeneath rule
anchors on (rules[0] = { .path = dir_s1d2, .access =
LANDLOCK_ACCESS_FS_EXECUTE }). The failing assertion is
test_execute(_metadata, 0, file1_s1d2) (fs_test.c:1949 in the v7.1.3
tree, quoted below). The test's author expects this call to succeed
(err == 0), and on this kernel it doesn't. Full raw test output is
attached (official-selftest-output.log).

Relevant excerpt of the actual test body (identical across the v7.1.3
tag and current master):

    TEST_F_FORK(layout1, execute)
    {
    const struct rule rules[] = {
    {
    .path = dir_s1d2,
    .access = LANDLOCK_ACCESS_FS_EXECUTE,
    },
    {},
    };

    copy_file(_metadata, bin_true, file1_s1d1);
    copy_file(_metadata, bin_true, file1_s1d2);
    copy_file(_metadata, bin_true, file1_s1d3);

    /* Checks before file1_s1d1 being denied. */
    test_execute(_metadata, 0, file1_s1d1);
    test_check_exec(_metadata, 0, file1_s1d1);

    enforce_fs(_metadata, rules[0].access, rules);

    ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
    ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));
    test_execute(_metadata, EACCES, file1_s1d1);
    test_check_exec(_metadata, EACCES, file1_s1d1);

    ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));
    ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
    test_execute(_metadata, 0, file1_s1d2);        /* <-- fails here */
    test_check_exec(_metadata, 0, file1_s1d2);
    ...

(Reproducing the official fixture required running from a
world-writable directory: prepare_layout_opt() calls disable_caps()
which drops CAP_DAC_OVERRIDE from the otherwise-root test process,
before creating its working directory, so it must be run from
somewhere an unprivileged-DAC root can still write, e.g. under /tmp
directly. This is unrelated to the actual bug, just a note for anyone
else reproducing it.)

Environment
-----------

- Kernel: 7.1.3-200.fc44.x86_64 (Fedora 44 Workstation)
- Landlock ABI version: 9 (via landlock_create_ruleset(NULL, 0,
  LANDLOCK_CREATE_RULESET_VERSION))
- kernel-headers package: kernel-headers-7.1.3-200.fc44.x86_64
  (exact version match to the running kernel; the reproduction below was
  built against these headers)
- No root/CAP_SYS_ADMIN involved anywhere in the reproduction -- Landlock
  is unprivileged by design, and this reproduces identically as an
  ordinary user.

Reproduction
------------

Official selftest (strongest evidence):

    $ git clone --branch v7.1.3 --depth 1 https://github.com/gregkh/linux
    $ cd linux/tools/testing/selftests/landlock
    $ gcc -O0 -g -Wall -o fs_test fs_test.c -lcap -lpthread
    $ gcc -O0 -o true true.c
    $ mkdir -p /tmp/lltest && chmod 1777 /tmp/lltest
    $ cp fs_test true /tmp/lltest/ && cd /tmp/lltest
    $ sudo ./fs_test -f layout1 -t execute

(Run from a world-writable directory. See the note above about
disable_caps()) Full output attached as official-selftest-output.log.

Minimal standalone reproduction (self-contained, no external
dependencies beyond libc and the system's own <linux/landlock.h>,
unprivileged, attached as repro.c):

    /*
     * Minimal standalone reproduction of a Landlock EXECUTE-right denial that
     * contradicts the upstream kernel's own selftest expectation.
     *
     * This mirrors tools/testing/selftests/landlock/fs_test.c's
     * TEST_F_FORK(layout1, execute) -- specifically the file1_s1d2 case: a
     * PathBeneath rule granting only LANDLOCK_ACCESS_FS_EXECUTE, anchored on a
     * directory, is expected to permit execve() of a file directly inside that
     * directory (upstream asserts test_execute(_metadata, 0, file1_s1d2), i.e.
     * "no error"). On this kernel it is denied with EACCES instead.
     *
     * No root/CAP_SYS_ADMIN required -- Landlock is designed for unprivileged
     * use, and this reproduces the same way as an ordinary user.
     *
     * Build:   gcc -O0 -g -Wall -o repro repro.c
     * Run:     ./repro
     *
     * Observed on: Linux 7.1.3-200.fc44.x86_64 (Fedora 44 Workstation),
     * Landlock ABI version 9 (per landlock_create_ruleset(NULL, 0,
     * LANDLOCK_CREATE_RULESET_VERSION)).
     */

    #define _GNU_SOURCE
    #include <errno.h>
    #include <fcntl.h>
    #include <linux/landlock.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/prctl.h>
    #include <sys/stat.h>
    #include <sys/syscall.h>
    #include <sys/wait.h>
    #include <unistd.h>

    static int landlock_create_ruleset(const struct landlock_ruleset_attr *attr,
       size_t size, __u32 flags)
    {
    return syscall(SYS_landlock_create_ruleset, attr, size, flags);
    }

    static int landlock_add_rule(int ruleset_fd, enum landlock_rule_type type,
         const void *attr, __u32 flags)
    {
    return syscall(SYS_landlock_add_rule, ruleset_fd, type, attr, flags);
    }

    static int landlock_restrict_self(int ruleset_fd, __u32 flags)
    {
    return syscall(SYS_landlock_restrict_self, ruleset_fd, flags);
    }

    static void die(const char *what)
    {
    fprintf(stderr, "%s: %s\n", what, strerror(errno));
    exit(1);
    }

    /* Runs `path` via a fresh execve() in a child, prints and returns its result. */
    static int try_execve(const char *path)
    {
    pid_t pid = fork();
    if (pid < 0)
    die("fork");
    if (pid == 0) {
    char *const argv[] = { (char *)path, NULL };
    execve(path, argv, NULL);
    /* Only reached on failure. */
    fprintf(stderr, "execve(%s) failed: %s (errno %d)\n", path,
    strerror(errno), errno);
    _exit(1);
    }
    int status;
    waitpid(pid, &status, 0);
    if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
    return 0;
    return 1;
    }

    int main(void)
    {
    int abi = landlock_create_ruleset(NULL, 0,
      LANDLOCK_CREATE_RULESET_VERSION);
    printf("Landlock ABI version: %d\n", abi);
    if (abi < 0)
    die("landlock_create_ruleset(VERSION)");

    /* Build tmp/dir/exe, matching layout1's dir_s1d2/file1_s1d2 shape:
    * a rule anchored on the parent directory, target file directly
    * inside it (zero levels of extra nesting). */
    if (mkdir("repro-dir", 0700) < 0 && errno != EEXIST)
    die("mkdir repro-dir");

    /* Copy /bin/true in as our target executable. */
    {
    char cmd[256];
    snprintf(cmd, sizeof(cmd),
    "cp /bin/true repro-dir/exe && chmod 755 repro-dir/exe");
    if (system(cmd) != 0)
    die("cp /bin/true");
    }

    /* Baseline: confirm it executes fine before Landlock is involved. */
    if (try_execve("repro-dir/exe") != 0) {
    fprintf(stderr, "baseline execve failed even without Landlock -- "
    "environment problem, not a Landlock issue\n");
    return 1;
    }
    printf("Baseline (no Landlock): execve succeeded, as expected.\n");

    /* Minimal ruleset: EXECUTE only, matching the official selftest's
    * rules[0].access = LANDLOCK_ACCESS_FS_EXECUTE for layout1.execute. */
    struct landlock_ruleset_attr ruleset_attr = {
    .handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE,
    };
    int ruleset_fd = landlock_create_ruleset(&ruleset_attr,
     sizeof(ruleset_attr), 0);
    if (ruleset_fd < 0)
    die("landlock_create_ruleset");

    int parent_fd = open("repro-dir", O_PATH);
    if (parent_fd < 0)
    die("open repro-dir O_PATH");

    struct landlock_path_beneath_attr path_beneath = {
    .allowed_access = LANDLOCK_ACCESS_FS_EXECUTE,
    .parent_fd = parent_fd,
    };
    if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
          &path_beneath, 0) != 0)
    die("landlock_add_rule");
    close(parent_fd);

    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0)
    die("prctl(PR_SET_NO_NEW_PRIVS)");

    if (landlock_restrict_self(ruleset_fd, 0) != 0)
    die("landlock_restrict_self");
    close(ruleset_fd);

    printf("Landlock ruleset installed: EXECUTE-only, one PathBeneath "
          "rule anchored on repro-dir (the target file's own parent).\n");

    int result = try_execve("repro-dir/exe");
    if (result == 0) {
    printf("PASS: execve succeeded, as the upstream selftest "
          "(layout1.execute, file1_s1d2 case) expects.\n");
    } else {
    printf("FAIL: execve was denied (EACCES) despite an "
          "explicit, correctly-anchored PathBeneath rule "
          "granting LANDLOCK_ACCESS_FS_EXECUTE on the file's "
          "own parent directory -- this contradicts "
          "tools/testing/selftests/landlock/fs_test.c's "
          "TEST_F_FORK(layout1, execute) expectation for the "
          "file1_s1d2 case.\n");
    }

    system("rm -rf repro-dir");
    return result;
    }

Build and run:

    $ gcc -O0 -g -Wall -o repro repro.c
    $ ./repro
    Landlock ABI version: 9
    Baseline (no Landlock): execve succeeded, as expected.
    Landlock ruleset installed: EXECUTE-only, one PathBeneath rule anchored on repro-dir (the target file's own parent).
    execve(repro-dir/exe) failed: Permission denied (errno 13)
    FAIL: execve was denied (EACCES) despite an explicit, correctly-anchored PathBeneath rule granting LANDLOCK_ACCESS_FS_EXECUTE on the file's own parent directory -- this contradicts tools/testing/selftests/landlock/fs_test.c's TEST_F_FORK(layout1, execute) expectation for the file1_s1d2 case.

What was ruled out before concluding this is a kernel-level issue
--------------------------------------------------------------------

This was investigated extensively in the course of debugging a real
downstream project (a Rust codebase using the landlock crate 0.4.5) that
hit this exact denial. Before concluding it's a kernel bug rather than a
userspace mistake, the following were each tested and ruled out as the
cause:

1. SELinux: this host runs SELinux Enforcing, but the reproduction
   above runs entirely under the interactive shell's unconfined_t
   domain, and the identical target file executes fine via a plain
   (non-Landlock) execve() beforehand in the same process. Only adding
   the Landlock ruleset flips the result to EACCES.

2. IPE (Integrity Policy Enforcement): enforce=1 on this host, but
   /sys/kernel/security/ipe/policies/ is empty (no policy loaded), and
   no IPE-related denial appears in the audit log around the failure. A
   plain execve() of the same file with no Landlock domain active
   succeeds, which would not be the case if IPE were unconditionally
   denying this file.

3. Kernel lockdown mode: active at "integrity" level on this host
   (Fedora enables this automatically under Secure Boot), but lockdown's
   documented restrictions (module signing, /dev/mem, hibernation,
   kexec, debugfs, etc.) govern kernel-image-integrity operations, not
   ordinary unprivileged LSM policy decisions like Landlock's own
   filesystem checks, and no documented interaction between the two was
   found.

4. tmpfs vs. a real on-disk filesystem: the denial reproduces
   identically whether the target directory is on tmpfs (/tmp) or a
   real on-disk btrfs filesystem (the root filesystem).

5. fd-based (execveat+AT_EMPTY_PATH) vs. path-based execve(): both
   fail identically; this is not specific to executing via an
   already-open file descriptor.

6. Direct child vs. nested subdirectory: a file nested one level deeper
   than the anchored rule fails identically to a file directly in the
   anchored directory.

7. O_PATH vs. plain open() for the rule's anchor fd: no difference
   (the landlock Rust crate's own documentation warns plain File opens
   "may lead to unexpected errors," but explicitly testing O_PATH
   produced the identical denial).

8. Ruleset rights breadth: tested with handled_access_fs as EXECUTE-only
   as the full V3-era 15-right set, and with an additional explicit
   file-anchored rule (correctly scoped to file-valid rights) layered
   alongside the directory rule. All identical.

9. landlock_ruleset_attr struct size: tested with the struct passed at
   24, 32, 48, and 64 bytes (padded with zeroed trailing fields, in case
   of an extensible-struct zero-extension gap for fields introduced at
   ABI 8/9, e.g. the quiet_access_fs/quiet_access_net/quiet_scoped
   fields added around ABI 7-9). No difference; the reproduction
   attached uses sizeof(ruleset_attr) against the real system header,
   matching upstream selftest convention exactly.

10. Published errata: checked all three currently-documented entries
    in security/landlock/errata/ (disconnected-directory rename
    widening, TCP socket protocol misclassification, thread signal
    scoping). None match this symptom.

11. Direct read of security/landlock/fs.c on torvalds/linux master:
    the is_access_to_paths_allowed()/current_check_access_path()/
    hook_file_open() hierarchy-walk logic appears correct for this
    simple case (walk starts at the target file's own dentry, checks
    find_rule() at each level while walking up via dget_parent(), and
    our anchor is exactly one level up). No obvious logic defect was
    found by inspection, which is part of why this is being reported
    rather than fixed downstream.

Given (11), if the master-branch logic is correct, this may already be
fixed in a tree newer than 7.1.3, or the regression window may be
narrow but as of this exact kernel, the discrepancy against the
selftest's own documented expectation is real and reproducible.

Impact
------

This affects any unprivileged (or privileged, self-restricting) process
that installs a minimal Landlock domain and then tries to execute a file
covered by a directory-anchored rule which is a common Landlock usage
pattern (matching the kernel's own selftest).
Downstream, it silently breaks a security-hardening code path
(Landlock-based filesystem confinement for a privileged process tree)
with no diagnostic beyond a generic EACCES, since the process attempting
the exec has already dropped privileges and cannot meaningfully report
the failure back to a parent through advanced means.

Request
-------

Could you confirm whether this is a known/already-fixed issue, and if
not, whether the attached reproduction is sufficient to track down the
regression? Let me know if you'd like me test a patch or provide more
instrumentation from this exact kernel build if useful.

Attachments
-----------

- repro.c - minimal standalone reproduction (also inlined above)
- official-selftest-output.log - full raw TAP output from running the
  unmodified v7.1.3 fs_test -f layout1 -t execute against this kernel

[-- Attachment #1.1.2.1: Type: text/html, Size: 27985 bytes --]

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.2: repro.c --]
[-- Type: text/x-csrc; filename="repro.c"; name="repro.c", Size: 5066 bytes --]

/*
 * Minimal standalone reproduction of a Landlock EXECUTE-right denial that
 * contradicts the upstream kernel's own selftest expectation.
 *
 * This mirrors tools/testing/selftests/landlock/fs_test.c's
 * TEST_F_FORK(layout1, execute) -- specifically the file1_s1d2 case: a
 * PathBeneath rule granting only LANDLOCK_ACCESS_FS_EXECUTE, anchored on a
 * directory, is expected to permit execve() of a file directly inside that
 * directory (upstream asserts test_execute(_metadata, 0, file1_s1d2), i.e.
 * "no error"). On this kernel it is denied with EACCES instead.
 *
 * No root/CAP_SYS_ADMIN required -- Landlock is designed for unprivileged
 * use, and this reproduces the same way as an ordinary user.
 *
 * Build:   gcc -O0 -g -Wall -o repro repro.c
 * Run:     ./repro
 *
 * Observed on: Linux 7.1.3-200.fc44.x86_64 (Fedora 44 Workstation),
 * Landlock ABI version 9 (per landlock_create_ruleset(NULL, 0,
 * LANDLOCK_CREATE_RULESET_VERSION)).
 */

#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <linux/landlock.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/wait.h>
#include <unistd.h>

static int landlock_create_ruleset(const struct landlock_ruleset_attr *attr,
				    size_t size, __u32 flags)
{
	return syscall(SYS_landlock_create_ruleset, attr, size, flags);
}

static int landlock_add_rule(int ruleset_fd, enum landlock_rule_type type,
			      const void *attr, __u32 flags)
{
	return syscall(SYS_landlock_add_rule, ruleset_fd, type, attr, flags);
}

static int landlock_restrict_self(int ruleset_fd, __u32 flags)
{
	return syscall(SYS_landlock_restrict_self, ruleset_fd, flags);
}

static void die(const char *what)
{
	fprintf(stderr, "%s: %s\n", what, strerror(errno));
	exit(1);
}

/* Runs `path` via a fresh execve() in a child, prints and returns its result. */
static int try_execve(const char *path)
{
	pid_t pid = fork();
	if (pid < 0)
		die("fork");
	if (pid == 0) {
		char *const argv[] = { (char *)path, NULL };
		execve(path, argv, NULL);
		/* Only reached on failure. */
		fprintf(stderr, "execve(%s) failed: %s (errno %d)\n", path,
			strerror(errno), errno);
		_exit(1);
	}
	int status;
	waitpid(pid, &status, 0);
	if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
		return 0;
	return 1;
}

int main(void)
{
	int abi = landlock_create_ruleset(NULL, 0,
					   LANDLOCK_CREATE_RULESET_VERSION);
	printf("Landlock ABI version: %d\n", abi);
	if (abi < 0)
		die("landlock_create_ruleset(VERSION)");

	/* Build tmp/dir/exe, matching layout1's dir_s1d2/file1_s1d2 shape:
	 * a rule anchored on the parent directory, target file directly
	 * inside it (zero levels of extra nesting). */
	if (mkdir("repro-dir", 0700) < 0 && errno != EEXIST)
		die("mkdir repro-dir");

	/* Copy /bin/true in as our target executable. */
	{
		char cmd[256];
		snprintf(cmd, sizeof(cmd),
			 "cp /bin/true repro-dir/exe && chmod 755 repro-dir/exe");
		if (system(cmd) != 0)
			die("cp /bin/true");
	}

	/* Baseline: confirm it executes fine before Landlock is involved. */
	if (try_execve("repro-dir/exe") != 0) {
		fprintf(stderr, "baseline execve failed even without Landlock -- "
				"environment problem, not a Landlock issue\n");
		return 1;
	}
	printf("Baseline (no Landlock): execve succeeded, as expected.\n");

	/* Minimal ruleset: EXECUTE only, matching the official selftest's
	 * rules[0].access = LANDLOCK_ACCESS_FS_EXECUTE for layout1.execute. */
	struct landlock_ruleset_attr ruleset_attr = {
		.handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE,
	};
	int ruleset_fd = landlock_create_ruleset(&ruleset_attr,
						  sizeof(ruleset_attr), 0);
	if (ruleset_fd < 0)
		die("landlock_create_ruleset");

	int parent_fd = open("repro-dir", O_PATH);
	if (parent_fd < 0)
		die("open repro-dir O_PATH");

	struct landlock_path_beneath_attr path_beneath = {
		.allowed_access = LANDLOCK_ACCESS_FS_EXECUTE,
		.parent_fd = parent_fd,
	};
	if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
			       &path_beneath, 0) != 0)
		die("landlock_add_rule");
	close(parent_fd);

	if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0)
		die("prctl(PR_SET_NO_NEW_PRIVS)");

	if (landlock_restrict_self(ruleset_fd, 0) != 0)
		die("landlock_restrict_self");
	close(ruleset_fd);

	printf("Landlock ruleset installed: EXECUTE-only, one PathBeneath "
	       "rule anchored on repro-dir (the target file's own parent).\n");

	int result = try_execve("repro-dir/exe");
	if (result == 0) {
		printf("PASS: execve succeeded, as the upstream selftest "
		       "(layout1.execute, file1_s1d2 case) expects.\n");
	} else {
		printf("FAIL: execve was denied (EACCES) despite an "
		       "explicit, correctly-anchored PathBeneath rule "
		       "granting LANDLOCK_ACCESS_FS_EXECUTE on the file's "
		       "own parent directory -- this contradicts "
		       "tools/testing/selftests/landlock/fs_test.c's "
		       "TEST_F_FORK(layout1, execute) expectation for the "
		       "file1_s1d2 case.\n");
	}

	system("rm -rf repro-dir");
	return result;
}

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.3: official-selftest-output.log --]
[-- Type: text/x-log; filename="official-selftest-output.log"; name="official-selftest-output.log", Size: 9001 bytes --]

TAP version 13
1..68
# Starting 68 tests from 1 test cases.
#  RUN           layout1.no_restriction ...
#            OK  layout1.no_restriction
ok 1 layout1.no_restriction
#  RUN           layout1.inval ...
#            OK  layout1.inval
ok 2 layout1.inval
#  RUN           layout1.file_and_dir_access_rights ...
#            OK  layout1.file_and_dir_access_rights
ok 3 layout1.file_and_dir_access_rights
#  RUN           layout1.rule_with_unhandled_access ...
#            OK  layout1.rule_with_unhandled_access
ok 4 layout1.rule_with_unhandled_access
#  RUN           layout1.effective_access ...
#            OK  layout1.effective_access
ok 5 layout1.effective_access
#  RUN           layout1.unhandled_access ...
#            OK  layout1.unhandled_access
ok 6 layout1.unhandled_access
#  RUN           layout1.ruleset_overlap ...
#            OK  layout1.ruleset_overlap
ok 7 layout1.ruleset_overlap
#  RUN           layout1.layer_rule_unions ...
#            OK  layout1.layer_rule_unions
ok 8 layout1.layer_rule_unions
#  RUN           layout1.non_overlapping_accesses ...
#            OK  layout1.non_overlapping_accesses
ok 9 layout1.non_overlapping_accesses
#  RUN           layout1.interleaved_masked_accesses ...
#            OK  layout1.interleaved_masked_accesses
ok 10 layout1.interleaved_masked_accesses
#  RUN           layout1.inherit_subset ...
#            OK  layout1.inherit_subset
ok 11 layout1.inherit_subset
#  RUN           layout1.inherit_superset ...
#            OK  layout1.inherit_superset
ok 12 layout1.inherit_superset
#  RUN           layout1.empty_or_same_ruleset ...
#            OK  layout1.empty_or_same_ruleset
ok 13 layout1.empty_or_same_ruleset
#  RUN           layout1.rule_on_mountpoint ...
#            OK  layout1.rule_on_mountpoint
ok 14 layout1.rule_on_mountpoint
#  RUN           layout1.rule_over_mountpoint ...
#            OK  layout1.rule_over_mountpoint
ok 15 layout1.rule_over_mountpoint
#  RUN           layout1.rule_over_root_allow_then_deny ...
#            OK  layout1.rule_over_root_allow_then_deny
ok 16 layout1.rule_over_root_allow_then_deny
#  RUN           layout1.rule_over_root_deny ...
#            OK  layout1.rule_over_root_deny
ok 17 layout1.rule_over_root_deny
#  RUN           layout1.rule_inside_mount_ns ...
#            OK  layout1.rule_inside_mount_ns
ok 18 layout1.rule_inside_mount_ns
#  RUN           layout1.mount_and_pivot ...
#            OK  layout1.mount_and_pivot
ok 19 layout1.mount_and_pivot
#  RUN           layout1.move_mount ...
#            OK  layout1.move_mount
ok 20 layout1.move_mount
#  RUN           layout1.topology_changes_with_net_only ...
#            OK  layout1.topology_changes_with_net_only
ok 21 layout1.topology_changes_with_net_only
#  RUN           layout1.topology_changes_with_net_and_fs ...
#            OK  layout1.topology_changes_with_net_and_fs
ok 22 layout1.topology_changes_with_net_and_fs
#  RUN           layout1.release_inodes ...
#            OK  layout1.release_inodes
ok 23 layout1.release_inodes
#  RUN           layout1.covered_rule ...
#            OK  layout1.covered_rule
ok 24 layout1.covered_rule
#  RUN           layout1.relative_open ...
#            OK  layout1.relative_open
ok 25 layout1.relative_open
#  RUN           layout1.relative_chdir ...
#            OK  layout1.relative_chdir
ok 26 layout1.relative_chdir
#  RUN           layout1.relative_chroot_only ...
#            OK  layout1.relative_chroot_only
ok 27 layout1.relative_chroot_only
#  RUN           layout1.relative_chroot_chdir ...
#            OK  layout1.relative_chroot_chdir
ok 28 layout1.relative_chroot_chdir
#  RUN           layout1.execute ...
# fs_test.c:1949:execute:Expected err ? -1 : 0 (0) == execve(path, argv, NULL) (-1)
# fs_test.c:1951:execute:Failed to execute "tmp/s1d1/s1d2/f1": Permission denied
# execute: Test terminated by assertion
#          FAIL  layout1.execute
not ok 29 layout1.execute
#  RUN           layout1.umount_sandboxer ...
# fs_test.c:1929:umount_sandboxer:Expected 0 (0) <= src_fd (-1)
# fs_test.c:1931:umount_sandboxer:Failed to open "./sandbox-and-launch": No such file or directory
# umount_sandboxer: Test terminated by assertion
#          FAIL  layout1.umount_sandboxer
not ok 30 layout1.umount_sandboxer
#  RUN           layout1.link ...
#            OK  layout1.link
ok 31 layout1.link
#  RUN           layout1.rename_file ...
#            OK  layout1.rename_file
ok 32 layout1.rename_file
#  RUN           layout1.rename_dir ...
#            OK  layout1.rename_dir
ok 33 layout1.rename_dir
#  RUN           layout1.reparent_refer ...
#            OK  layout1.reparent_refer
ok 34 layout1.reparent_refer
#  RUN           layout1.refer_denied_by_default1 ...
#            OK  layout1.refer_denied_by_default1
ok 35 layout1.refer_denied_by_default1
#  RUN           layout1.refer_denied_by_default2 ...
#            OK  layout1.refer_denied_by_default2
ok 36 layout1.refer_denied_by_default2
#  RUN           layout1.refer_denied_by_default3 ...
#            OK  layout1.refer_denied_by_default3
ok 37 layout1.refer_denied_by_default3
#  RUN           layout1.refer_denied_by_default4 ...
#            OK  layout1.refer_denied_by_default4
ok 38 layout1.refer_denied_by_default4
#  RUN           layout1.refer_mount_root_deny ...
#            OK  layout1.refer_mount_root_deny
ok 39 layout1.refer_mount_root_deny
#  RUN           layout1.refer_part_mount_tree_is_allowed ...
#            OK  layout1.refer_part_mount_tree_is_allowed
ok 40 layout1.refer_part_mount_tree_is_allowed
#  RUN           layout1.reparent_link ...
#            OK  layout1.reparent_link
ok 41 layout1.reparent_link
#  RUN           layout1.reparent_rename ...
#            OK  layout1.reparent_rename
ok 42 layout1.reparent_rename
#  RUN           layout1.reparent_exdev_layers_rename1 ...
#            OK  layout1.reparent_exdev_layers_rename1
ok 43 layout1.reparent_exdev_layers_rename1
#  RUN           layout1.reparent_exdev_layers_rename2 ...
#            OK  layout1.reparent_exdev_layers_rename2
ok 44 layout1.reparent_exdev_layers_rename2
#  RUN           layout1.reparent_exdev_layers_exchange1 ...
#            OK  layout1.reparent_exdev_layers_exchange1
ok 45 layout1.reparent_exdev_layers_exchange1
#  RUN           layout1.reparent_exdev_layers_exchange2 ...
#            OK  layout1.reparent_exdev_layers_exchange2
ok 46 layout1.reparent_exdev_layers_exchange2
#  RUN           layout1.reparent_exdev_layers_exchange3 ...
#            OK  layout1.reparent_exdev_layers_exchange3
ok 47 layout1.reparent_exdev_layers_exchange3
#  RUN           layout1.reparent_remove ...
#            OK  layout1.reparent_remove
ok 48 layout1.reparent_remove
#  RUN           layout1.reparent_dom_superset ...
#            OK  layout1.reparent_dom_superset
ok 49 layout1.reparent_dom_superset
#  RUN           layout1.remove_dir ...
#            OK  layout1.remove_dir
ok 50 layout1.remove_dir
#  RUN           layout1.remove_file ...
#            OK  layout1.remove_file
ok 51 layout1.remove_file
#  RUN           layout1.make_char ...
#            OK  layout1.make_char
ok 52 layout1.make_char
#  RUN           layout1.make_block ...
#            OK  layout1.make_block
ok 53 layout1.make_block
#  RUN           layout1.make_reg_1 ...
#            OK  layout1.make_reg_1
ok 54 layout1.make_reg_1
#  RUN           layout1.make_reg_2 ...
#            OK  layout1.make_reg_2
ok 55 layout1.make_reg_2
#  RUN           layout1.make_sock ...
#            OK  layout1.make_sock
ok 56 layout1.make_sock
#  RUN           layout1.make_fifo ...
#            OK  layout1.make_fifo
ok 57 layout1.make_fifo
#  RUN           layout1.make_sym ...
#            OK  layout1.make_sym
ok 58 layout1.make_sym
#  RUN           layout1.make_dir ...
#            OK  layout1.make_dir
ok 59 layout1.make_dir
#  RUN           layout1.proc_unlinked_file ...
#            OK  layout1.proc_unlinked_file
ok 60 layout1.proc_unlinked_file
#  RUN           layout1.proc_pipe ...
#            OK  layout1.proc_pipe
ok 61 layout1.proc_pipe
#  RUN           layout1.truncate_unhandled ...
#            OK  layout1.truncate_unhandled
ok 62 layout1.truncate_unhandled
#  RUN           layout1.truncate ...
#            OK  layout1.truncate
ok 63 layout1.truncate
#  RUN           layout1.ftruncate ...
#            OK  layout1.ftruncate
ok 64 layout1.ftruncate
#  RUN           layout1.o_path_ftruncate_and_ioctl ...
#            OK  layout1.o_path_ftruncate_and_ioctl
ok 65 layout1.o_path_ftruncate_and_ioctl
#  RUN           layout1.blanket_permitted_ioctls ...
#            OK  layout1.blanket_permitted_ioctls
ok 66 layout1.blanket_permitted_ioctls
#  RUN           layout1.named_pipe_ioctl ...
#            OK  layout1.named_pipe_ioctl
ok 67 layout1.named_pipe_ioctl
#  RUN           layout1.named_unix_domain_socket_ioctl ...
#            OK  layout1.named_unix_domain_socket_ioctl
ok 68 layout1.named_unix_domain_socket_ioctl
# FAILED: 66 / 68 tests passed.
# Totals: pass:66 fail:2 xfail:0 xpass:0 skip:0 error:0

[-- Attachment #1.4: publickey - ken@kengrimes.com - 0xEDFCC5BC.asc --]
[-- Type: application/pgp-keys, Size: 645 bytes --]

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 343 bytes --]

^ permalink raw reply

* Re: [PATCH v2] NFSv4.2: fix nfs4_listxattr size accounting
From: Paul Moore @ 2026-07-14 18:20 UTC (permalink / raw)
  To: linux-nfs, Anna Schumaker, jlayton, chuck.lever
  Cc: Stephen Smalley, Achilles Gaikwad, Trond Myklebust,
	linux-security-module, selinux
In-Reply-To: <CAHC9VhQy4LOgQg0mk+s5sjHOzMe1sxPUgJ2W7vRT8ms4znZp+Q@mail.gmail.com>

On Fri, Jul 10, 2026 at 6:20 PM Paul Moore <paul@paul-moore.com> wrote:
>
> ... and scratch that, the offending commit was that one.
>
>    commit 01c2305795a3b6b164df48e72b12022a68fd60c1
>    Author: Jeff Layton <jlayton@kernel.org>
>    Date:   Wed Mar 25 10:40:32 2026 -0400
>
>    nfsd: add netlink upcall for the nfsd.fh cache
>
>    Add netlink-based cache upcall support for the expkey (nfsd.fh) cache,
>    following the same pattern as the existing svc_export netlink support.
>
>    Add expkey to the cache-type enum, a new expkey attribute-set with
>    client, fsidtype, fsid, negative, expiry, and path fields, and the
>    expkey-get-reqs / expkey-set-reqs operations to the nfsd YAML spec
>    and generated headers.
>
>    Implement nfsd_nl_expkey_get_reqs_dumpit() which snapshots pending
>    expkey cache requests and sends each entry's seqno, client name,
>    fsidtype, and fsid over netlink.
>
>    Implement nfsd_nl_expkey_set_reqs_doit() which parses expkey cache
>    responses from userspace (client, fsidtype, fsid, expiry, and path
>    or negative flag) and updates the cache via svc_expkey_lookup() /
>    svc_expkey_update().
>
>    Wire up the expkey_notify() callback in svc_expkey_cache_template
>    so cache misses trigger NFSD_CMD_CACHE_NOTIFY multicast events with
>    NFSD_CACHE_TYPE_EXPKEY.
>
>    Signed-off-by: Jeff Layton <jlayton@kernel.org>
>    Signed-off-by: Chuck Lever <chuck.lever@oracle.com>

Playing around with it some this morning on a current Fedora Rawhide
system, I can reproduce the problem with a simple command line:

 %  mount -t nfs localhost:/mnt/test /mnt/nfs_test
 mount.nfs: Connection refused for localhost:/mnt/test on /mnt/nfs_test

... adding an explicit "vers={4,4.1,4.2}" has no effect; versions 2
and 3 are no supported on my kernel builds.  There is nothing obvious
in dmesg.  I've run with SELinux both in permissive mode and disabled
and encountered the same problem.  This doesn't appear to be related
to SELinux, it may simply be that we are the first ones to hit this.

As there was some earlier discussion about this being a wonky
interaction with userspace, here are some of the relevant packages on
my system:

nfs-common-utils-2.9.1-4.rc4.fc45.x86_64
nfs-client-utils-2.9.1-4.rc4.fc45.x86_64
nfsv4-client-utils-2.9.1-4.rc4.fc45.x86_64
nfs-utils-2.9.1-4.rc4.fc45.x86_64

My next step is to try disabling portions of the NFS file handle cache
upcall to see if that is the issue, but it would be nice if the NFS
devs could take a look at this too.  I'm happy to test things out or
answer any questions about my test system.

-- 
paul-moore.com

^ permalink raw reply

* Re: [PATCH] apparmor: fix cred UAF caused by begin_current_label_crit_section()
From: Jann Horn @ 2026-07-14 15:49 UTC (permalink / raw)
  To: John Johansen, John Johansen, Georgia Garcia, apparmor,
	Paul Moore, Serge E. Hallyn
  Cc: James Morris, Christian Brauner, Al Viro, Peter Zijlstra (Intel),
	linux-security-module, kernel list, stable
In-Reply-To: <20260714-fix-apparmor-cred-uaf-v1-1-be40e8c83b90@google.com>

On Tue, Jul 14, 2026 at 5:39 PM Jann Horn <jannh@google.com> wrote:
> I have a test case where I run aa-disable on a profile while a process
> using that profile is blocked on splice() from a FUSE passthrough file into
> a full pipe; after the profile update, the pipe becomes empty, splice()
> resumes, the credentials go out of sync, and a subsequent getuid() syscall
> results in a KASAN UAF splat.

To test this, you should run a kernel with KASAN.
CONFIG_RCU_STRICT_GRACE_PERIOD=y might also be necessary to trigger a
KASAN warning.

Open two terminals A and B. In terminal A, write the following policy
into /etc/apparmor.d/credbug-test:
```
abi <abi/4.0>,
include <tunables/global>

profile credbug /tmp/credbug {
  /** rwm,
  mount,
  umount,
  capability sys_admin setuid,
}
```
and enable it:
```
user@vm:~$ sudo aa-enforce /tmp/credbug
Setting /tmp/credbug to enforce mode.
Warning: profile credbug represents multiple programs
user@vm:~$
```

In terminal B, build the reproducer and launch it with root privileges:
```
user@vm:~/apparmor-replace-label$ cat > credbug.c
#define _GNU_SOURCE
#include <pthread.h>
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/fsuid.h>
#include <sys/mount.h>
#include <sys/prctl.h>
#include <linux/fuse.h>

#define SYSCHK(x) ({          \
  typeof(x) __res = (x);      \
  if (__res == (typeof(x))-1) \
    err(1, "SYSCHK(" #x ")"); \
  __res;                      \
})

static int p[2];
static int fuse_fd;
static volatile int fuse_ready = 0;
static volatile int backing_id = -1;
static int last_credref_fd = -1;
static int drop_last_credref = 0;

static void *splice_thread_fn(void *dummy) {
  int backing_fd = SYSCHK(open("/tmp/credbug", O_RDONLY));

  // unshare creds
  setfsuid(1);
  setfsuid(0);

  last_credref_fd = SYSCHK(open("/", O_PATH));

  while (!fuse_ready) /*spin*/;
  struct fuse_backing_map backing_arg = { .fd = backing_fd  };
  backing_id = SYSCHK(ioctl(fuse_fd, FUSE_DEV_IOC_BACKING_OPEN, &backing_arg));

  int passthrough_fd = SYSCHK(open("/tmp/mntfile", O_RDONLY));
  off_t off0 = 0;
  // creds are changed in the middle of this
  splice(passthrough_fd, &off0, p[1], NULL, 1, 0);
  close(passthrough_fd);

  SYSCHK(ioctl(fuse_fd, FUSE_DEV_IOC_BACKING_CLOSE, &backing_id));
  drop_last_credref = 1;
  while (drop_last_credref) /*spin*/;
  sleep(1);
  getuid();
  return NULL;
}

#define READ(_obj) if (read(fuse_fd, &(_obj), sizeof(_obj)) !=
sizeof(_obj)) err(1, "failed to read " #_obj)
#define WRITE(_obj) if (write(fuse_fd, &(_obj), (_obj).h.len) !=
(_obj).h.len) err(1, "failed to write " #_obj)
static void *fuse_thread_fn(void *dummy) {
  while (1) {
    struct {
      struct fuse_in_header inh;
      union {
        struct fuse_init_in init_in;
        struct fuse_open_in open_in;
        struct fuse_read_in read_in;
        char pad[10000];
      };
    } buf;
    ssize_t read_res = read(fuse_fd, &buf, sizeof(buf));
    if (read_res == -1) {
      if (errno == ENODEV)
        return NULL;
    }
    assert(read_res >= sizeof(buf.inh));
    if (buf.inh.opcode == FUSE_INIT) {
      printf("fuse: init\n");
      struct {
        struct fuse_out_header h;
        struct fuse_init_out b;
      } reply = {
        .h = { .len = sizeof(reply), .error = 0, .unique = buf.inh.unique },
        .b = {
          .major = buf.init_in.major, .minor = buf.init_in.minor,
          .max_stack_depth=1, .flags=FUSE_INIT_EXT, .flags2=FUSE_PASSTHROUGH>>32
        }
      };
      WRITE(reply);
      fuse_ready = 1;
    } else if (buf.inh.opcode == FUSE_GETATTR) {
      printf("fuse: getattr\n");
      struct {
        struct fuse_out_header h;
        struct fuse_attr_out b;
      } reply = {
        .h = { .len = sizeof(reply), .error = 0, .unique = buf.inh.unique },
        .b = {
          .attr_valid = FATTR_SIZE | FATTR_MODE,
          .attr = {
            .size = 0x1,
            .mode = 0100777
          }
        }
      };
      WRITE(reply);
    } else if (buf.inh.opcode == FUSE_OPEN) {
      printf("fuse: open node 0x%lu\n", (unsigned long)buf.inh.nodeid);
      while (backing_id == -1) /*spin*/;
      struct {
        struct fuse_out_header h;
        struct fuse_open_out b;
      } reply = {
        .h = { .len = sizeof(reply), .error = 0, .unique = buf.inh.unique },
        .b = { .open_flags = FOPEN_PASSTHROUGH, .backing_id = backing_id }
      };
      WRITE(reply);
    } else {
      printf("FUSE_<%d> unhandled\n", buf.inh.opcode);
      struct {
        struct fuse_out_header h;
      } reply = {
        .h = { .len = sizeof(reply), .error = -ENOSYS, .unique =
buf.inh.unique },
      };
      WRITE(reply);
    }
  }
}

int main(void) {
  // create a FUSE mount, and handle requests in a thread
  SYSCHK(close(SYSCHK(open("/tmp/mntfile", O_RDONLY|O_CREAT, 0666))));
  fuse_fd = SYSCHK(open("/dev/fuse", O_RDWR));
  char mount_data[4096];
  sprintf(mount_data, "fd=%d,rootmode=0100777,user_id=%d,group_id=%d",
fuse_fd, getuid(), getgid());
  SYSCHK(mount("blah", "/tmp/mntfile", "fuse", MS_NODEV|MS_NOSUID, mount_data));
  pthread_t fuse_thread;
  pthread_create(&fuse_thread, NULL, fuse_thread_fn, NULL);

  SYSCHK(pipe(p));
  SYSCHK(fcntl(p[1], F_SETPIPE_SZ, 0x1000));
  char buf[0x1000] = {};
  SYSCHK(write(p[1], buf, 0x1000));
  pthread_t splice_thread;
  pthread_create(&splice_thread, NULL, splice_thread_fn, NULL);
  getchar();
  read(p[0], buf, 0x1000);

  while (!drop_last_credref) /*spin*/;
  close(last_credref_fd);
  drop_last_credref = 0;

  pthread_join(splice_thread, NULL);
  SYSCHK(umount2("/tmp/mntfile", MNT_DETACH));
  pthread_join(fuse_thread, NULL);
}
user@vm:~/apparmor-replace-label$ gcc -o /tmp/credbug credbug.c
user@vm:~/apparmor-replace-label$ sudo /tmp/credbug
fuse: init
fuse: open node 0x1
```

While the reproducer is blocked, use terminal A to disable its profile:
```
user@vm:~$ sudo aa-disable /tmp/credbug
Disabling /tmp/credbug.
user@vm:~$
```

Then hit enter in terminal B to let the reproducer continue:
```

FUSE_<25> unhandled
FUSE_<18> unhandled
user@vm:~/apparmor-replace-label$
```

Now you should see a KASAN UAF splat in dmesg, when getuid() tries
accessing the creds:
```
==================================================================
BUG: KASAN: slab-use-after-free in __ia32_sys_getuid+0x3d/0x80
Read of size 8 at addr ffff8881269a8950 by task credbug/696

CPU: 2 UID: 0 PID: 696 Comm: credbug Not tainted
7.2.0-rc3-00038-g3b029c035b34 #55 PREEMPT
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS
1.17.0-debian-1.17.0-1 04/01/2014
Call Trace:
 <TASK>
 __dump_stack+0x21/0x30
 dump_stack_lvl+0x76/0xa0
 print_address_description+0x7b/0x1f0
 print_report+0x5b/0x70
 kasan_report+0x16d/0x1a0
[...]
 __asan_load8+0x98/0xa0
 __ia32_sys_getuid+0x3d/0x80
 x64_sys_call+0x1cc1/0x3030
 do_syscall_64+0xd8/0x380
[...]
 entry_SYSCALL_64_after_hwframe+0x76/0x7e
[...]
 </TASK>

Allocated by task 696:
 kasan_save_track+0x3a/0x70
 kasan_save_alloc_info+0x3c/0x50
 __kasan_slab_alloc+0x4e/0x60
 kmem_cache_alloc_noprof+0x25f/0x570
 prepare_creds+0x2b/0x4d0
 __sys_setfsuid+0x8b/0x190
 __x64_sys_setfsuid+0x1e/0x30
 x64_sys_call+0x955/0x3030
 do_syscall_64+0xd8/0x380
 entry_SYSCALL_64_after_hwframe+0x76/0x7e

Freed by task 70:
 kasan_save_track+0x3a/0x70
 kasan_save_free_info+0x46/0x60
 __kasan_slab_free+0x43/0x70
 kmem_cache_free+0x182/0x500
 put_cred_rcu+0x19e/0x210
 rcu_core+0x877/0xfd0
 rcu_core_si+0x9/0x10
 handle_softirqs+0x19f/0x550
 __irq_exit_rcu+0xab/0x180
 irq_exit_rcu+0x9/0x20
 sysvec_call_function+0x73/0x80
 asm_sysvec_call_function+0x1b/0x20

Last potentially related work creation:
 kasan_save_stack+0x3a/0x60
 kasan_record_aux_stack+0x99/0xb0
 call_rcu+0x51/0x5c0
 __put_cred+0x9a/0xc0
 __fput+0x425/0x540
 fput_close_sync+0x8a/0x140
 __x64_sys_close+0x55/0xe0
 x64_sys_call+0x26ce/0x3030
 do_syscall_64+0xd8/0x380
 entry_SYSCALL_64_after_hwframe+0x76/0x7e

The buggy address belongs to the object at ffff8881269a88c0
 which belongs to the cache cred of size 184
The buggy address is located 144 bytes inside of
 freed 184-byte region [ffff8881269a88c0, ffff8881269a8978)
[...]
==================================================================
```

^ permalink raw reply

* [PATCH] apparmor: fix cred UAF caused by begin_current_label_crit_section()
From: Jann Horn @ 2026-07-14 15:38 UTC (permalink / raw)
  To: John Johansen, John Johansen, Georgia Garcia, apparmor,
	Paul Moore, Serge E. Hallyn
  Cc: James Morris, Christian Brauner, Al Viro, Peter Zijlstra (Intel),
	linux-security-module, kernel list, stable, Jann Horn

AppArmor's begin_current_label_crit_section() is a scary function called
from lots of LSM hooks (in particular VFS/socket-related ones) that checks
if the label referenced by the current creds is marked FLAG_STALE, and if
so, attempts to use aa_replace_current_label() to replace the creds with an
updated version that uses a new label.

The first problem with this is that it would directly lead to UAF of
`struct cred` if anything in the kernel takes a pointer to the current
creds and accesses these past a security hook invocation that replaces
creds, like so:
```
const struct cred *cred = current_cred();
alloc_file_pseudo(...);
uid_t uid = cred->euid;
```
I don't know if anything in the kernel actually does this, but I think it
is very surprising that this pattern could lead to UAF.

The second problem is that things go wrong when aa_replace_current_label()
runs with overridden credentials. aa_replace_current_label() bails out if
`current_cred() != current_real_cred()` (mirroring the check in
proc_pid_attr_write()), but this check can't actually reliably detect
overridden credentials because the overridden creds can be the same as the
objective creds.

So in approximately the following scenario, things go wrong:

1. task begins with <creds A> (as both objective and subjective creds),
   with refcount=2
2. task grabs an extra reference on <creds A> for overriding
3. task calls override_creds(<creds A>), which returns a pointer to the old
   subjective creds (<creds A>)
4. task enters AppArmor LSM hook
5. AppArmor checks that objective/subjective creds are equal
6. AppArmor replaces both cred pointers with <creds B> and drops 2 refs on
   <creds A>
7. task leaves AppArmor LSM hook
8. task calls revert_creds(<creds A>)
9. now task->cred is <creds A> while task->real_cred is <creds B>, but the
   task_struct logically holds two references to <creds B>
10. another task drops the extra reference on <creds A> that was used for
    overriding, refcount drops to 0
11. now task->real_cred points to freed creds

At this point, any access to current_cred() will be UAF.

I have a test case where I run aa-disable on a profile while a process
using that profile is blocked on splice() from a FUSE passthrough file into
a full pipe; after the profile update, the pipe becomes empty, splice()
resumes, the credentials go out of sync, and a subsequent getuid() syscall
results in a KASAN UAF splat.

To fix this, instead of directly replacing creds, do it via task_work that
will run at the end of the current syscall. (The point in time at which the
cred replacement happens should have no correctness impact; it is just a
performance optimization to avoid unnecessarily touching the refcount of
the new label.)

Note that AppArmor still performs direct cred replacements in the
sb_pivotroot LSM hook after this change, and that direct cred replacements
can still happen in VFS ->write() callbacks via proc_pid_attr_write().

Cc: stable@vger.kernel.org
Fixes: c75afcd153f6 ("AppArmor: contexts used in attaching policy to system objects")
Signed-off-by: Jann Horn <jannh@google.com>
---
 include/linux/task_work.h        |  1 +
 kernel/task_work.c               | 14 ++++++++++++++
 security/apparmor/include/cred.h |  6 +-----
 security/apparmor/include/task.h |  1 +
 security/apparmor/task.c         | 29 +++++++++++++++++++++++++++++
 5 files changed, 46 insertions(+), 5 deletions(-)

diff --git a/include/linux/task_work.h b/include/linux/task_work.h
index 0646804860ff..ce19fc14060c 100644
--- a/include/linux/task_work.h
+++ b/include/linux/task_work.h
@@ -33,6 +33,7 @@ struct callback_head *task_work_cancel_match(struct task_struct *task,
 	bool (*match)(struct callback_head *, void *data), void *data);
 struct callback_head *task_work_cancel_func(struct task_struct *, task_work_func_t);
 bool task_work_cancel(struct task_struct *task, struct callback_head *cb);
+bool task_work_has_func(struct task_struct *task, task_work_func_t func);
 void task_work_run(void);
 
 static inline void exit_task_work(struct task_struct *task)
diff --git a/kernel/task_work.c b/kernel/task_work.c
index 0f7519f8e7c9..f83d1528e0bc 100644
--- a/kernel/task_work.c
+++ b/kernel/task_work.c
@@ -189,6 +189,20 @@ bool task_work_cancel(struct task_struct *task, struct callback_head *cb)
 	return ret == cb;
 }
 
+bool task_work_has_func(struct task_struct *task, task_work_func_t func)
+{
+	struct callback_head *work;
+
+	if (!task_work_pending(task))
+		return false;
+	guard(raw_spinlock_irqsave)(&task->pi_lock);
+	for (work = READ_ONCE(task->task_works); work; work = READ_ONCE(work->next)) {
+		if (work->func == func)
+			return true;
+	}
+	return false;
+}
+
 /**
  * task_work_run - execute the works added by task_work_add()
  *
diff --git a/security/apparmor/include/cred.h b/security/apparmor/include/cred.h
index 2b6098149b15..0e8b67159f56 100644
--- a/security/apparmor/include/cred.h
+++ b/security/apparmor/include/cred.h
@@ -222,13 +222,9 @@ static inline struct aa_label *begin_current_label_crit_section(void)
 {
 	struct aa_label *label = aa_current_raw_label();
 
-	might_sleep();
-
 	if (label_is_stale(label)) {
 		label = aa_get_newest_label(label);
-		if (aa_replace_current_label(label) == 0)
-			/* task cred will keep the reference */
-			aa_put_label(label);
+		aa_schedule_stale_label_replacement();
 	}
 
 	return label;
diff --git a/security/apparmor/include/task.h b/security/apparmor/include/task.h
index b1aaaf60fa8b..4e49a4142777 100644
--- a/security/apparmor/include/task.h
+++ b/security/apparmor/include/task.h
@@ -30,6 +30,7 @@ struct aa_task_ctx {
 };
 
 int aa_replace_current_label(struct aa_label *label);
+void aa_schedule_stale_label_replacement(void);
 void aa_set_current_onexec(struct aa_label *label, bool stack);
 int aa_set_current_hat(struct aa_label *label, u64 token);
 int aa_restore_previous_label(u64 cookie);
diff --git a/security/apparmor/task.c b/security/apparmor/task.c
index b9fb3738124e..8e368f6278f5 100644
--- a/security/apparmor/task.c
+++ b/security/apparmor/task.c
@@ -14,6 +14,7 @@
 
 #include <linux/gfp.h>
 #include <linux/ptrace.h>
+#include <linux/task_work.h>
 
 #include "include/path.h"
 #include "include/audit.h"
@@ -89,6 +90,34 @@ int aa_replace_current_label(struct aa_label *label)
 	return 0;
 }
 
+static void aa_replace_stale_label_tw_func(struct callback_head *tw)
+{
+	struct aa_label *label;
+
+	kfree(tw);
+	label = aa_current_raw_label();
+	if (!label_is_stale(label))
+		return;
+	label = aa_get_newest_label(label);
+	aa_replace_current_label(label);
+	aa_put_label(label);
+}
+
+/* replace the current task's stale label on syscall return */
+void aa_schedule_stale_label_replacement(void)
+{
+	struct callback_head *tw;
+
+	if (task_work_has_func(current, aa_replace_stale_label_tw_func))
+		return;
+	tw = kmalloc_obj(struct callback_head);
+	if (!tw)
+		return;
+	init_task_work(tw, aa_replace_stale_label_tw_func);
+	if (task_work_add(current, tw, TWA_RESUME))
+		kfree(tw);
+}
+
 
 /**
  * aa_set_current_onexec - set the tasks change_profile to happen onexec

---
base-commit: 3b029c035b34bbc693405ddf759f0e9b920c27f1
change-id: 20260714-fix-apparmor-cred-uaf-cc38ec2b38b7

Best regards,
--  
Jann Horn <jannh@google.com>


^ permalink raw reply related

* [PATCH v2 3/3] assoc_array: trim the final shortcut word using the current chunk end
From: Michael Bommarito @ 2026-07-14 11:54 UTC (permalink / raw)
  To: David Howells, Jarkko Sakkinen
  Cc: Andrew Morton, Paul Moore, James Morris, Serge E . Hallyn,
	keyrings, linux-security-module, linux-kernel
In-Reply-To: <20260714115451.3773164-1-michael.bommarito@gmail.com>

assoc_array_walk() masks off the bits past shortcut->skip_to_level in the
word that contains skip_to_level, gated on
round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > skip_to_level.

That guard is wrong in two opposite ways:

 - When sc_level is word-aligned (every word after the first) round_up()
   is a no-op, so the guard is sc_level > skip_to_level and never fires for
   the word that holds skip_to_level.  A shortcut that spans more than one
   word and ends in the middle of its last word leaves that word untrimmed,
   and its stale high bits leak into the dissimilarity word and can steer
   the walk down the wrong descendant.

 - When sc_level is unaligned (the first word) and skip_to_level sits on
   the next chunk boundary, sc_level + CHUNK would exceed skip_to_level and
   fire the trim with shift = skip_to_level & CHUNK_MASK == 0, which clears
   the whole dissimilarity word and makes a differing shortcut compare
   equal.

Use the end of the chunk that contains sc_level instead:

	skip_to_level < round_down(sc_level, CHUNK) + CHUNK

For an aligned sc_level whose word holds skip_to_level this now fires (the
first bug); for an unaligned sc_level with skip_to_level on the following
boundary it does not, so shift is never 0 when the branch runs and the trim
never clears the whole word.

Fixes: 3cb989501c26 ("Add a generic associative array implementation.")
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
---
 lib/assoc_array.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lib/assoc_array.c b/lib/assoc_array.c
index bcc6e0a013eb8..b6c9723e12ced 100644
--- a/lib/assoc_array.c
+++ b/lib/assoc_array.c
@@ -255,7 +255,8 @@ assoc_array_walk(const struct assoc_array *array,
 		sc_segments = shortcut->index_key[sc_level >> ASSOC_ARRAY_KEY_CHUNK_SHIFT];
 		dissimilarity = segments ^ sc_segments;
 
-		if (round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > shortcut->skip_to_level) {
+		if (shortcut->skip_to_level < round_down(sc_level,
+				ASSOC_ARRAY_KEY_CHUNK_SIZE) + ASSOC_ARRAY_KEY_CHUNK_SIZE) {
 			/* Trim segments that are beyond the shortcut */
 			int shift = shortcut->skip_to_level & ASSOC_ARRAY_KEY_CHUNK_MASK;
 			dissimilarity &= ~(ULONG_MAX << shift);
-- 
2.53.0


^ permalink raw reply related

* [PATCH v2 2/3] keys: make keyring key-chunk byte order agree with keyring_diff_objects()
From: Michael Bommarito @ 2026-07-14 11:54 UTC (permalink / raw)
  To: David Howells, Jarkko Sakkinen
  Cc: Andrew Morton, Paul Moore, James Morris, Serge E . Hallyn,
	keyrings, linux-security-module, linux-kernel
In-Reply-To: <20260714115451.3773164-1-michael.bommarito@gmail.com>

keyring_get_key_chunk() loads description bytes into the index chunk low
address first, while keyring_diff_objects() numbers the first differing
bit from the low end and folds the absolute byte index into the level
without removing the inline-prefix offset the level already carries.
The two disagree on byte order and bit position, so the array can be
told two keys first differ at a bit that does not differ in the chunk
the walker uses, letting crafted descriptions collide into one node.

Load the chunk in the order keyring_diff_objects() assumes and drop the
inline-prefix length when folding the byte index into the level.  This
only changes the in-memory ordering used to place keys within a keyring;
add, search and read of non-colliding keys are unaffected.

Fixes: f771fde82051 ("keys: Simplify key description management")
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
---
 security/keys/keyring.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index 1739373172ad5..e7066893e6ffc 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -292,9 +292,10 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)
 		desc_len -= offset;
 		if (desc_len > n)
 			desc_len = n;
+		d += desc_len;
 		do {
 			chunk <<= 8;
-			chunk |= *d++;
+			chunk |= *--d;
 		} while (--desc_len > 0);
 		return chunk;
 	}
@@ -375,7 +376,7 @@ static int keyring_diff_objects(const void *object, const void *data)
 	return -1;
 
 differ_plus_i:
-	level += i;
+	level += i - (int)sizeof(a->desc);
 differ:
 	i = level * 8 + __ffs(seg_a ^ seg_b);
 	return i;
-- 
2.53.0


^ permalink raw reply related

* [PATCH v2 1/3] keys: fix out-of-bounds read in keyring_get_key_chunk()
From: Michael Bommarito @ 2026-07-14 11:54 UTC (permalink / raw)
  To: David Howells, Jarkko Sakkinen
  Cc: Andrew Morton, Paul Moore, James Morris, Serge E . Hallyn,
	keyrings, linux-security-module, linux-kernel
In-Reply-To: <20260714115451.3773164-1-michael.bommarito@gmail.com>

For description-level chunks keyring_get_key_chunk() advances the read
pointer by level * sizeof(long) past the inline prefix but only
bounds-checks the prefix, so a long enough key description is read past
its kmemdup(desc, desc_len + 1) allocation.  Compute the full byte
offset and bounds-check the description against it before reading.

The walk only reaches a description-level chunk when two keys collide
through the hash, x, type and domain_tag chunks, so this is reached from
an unprivileged add_key(2) with a crafted pair of same-type keys whose
index hashes collide; KASAN reports a slab-out-of-bounds read.

Fixes: f771fde82051 ("keys: Simplify key description management")
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
---
 security/keys/keyring.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

KASAN, x86_64: add_key(2) of a crafted hash-colliding "user"-key pair
(~63-byte descriptions) reports

  BUG: KASAN: slab-out-of-bounds in keyring_get_key_chunk
  keyring_get_key_chunk <- assoc_array_insert <- __key_link_begin
    <- __do_sys_add_key

reading one byte past the description allocation; the same trigger is
KASAN-clean with this patch.  On a kernel built without init-on-alloc,
reading the colliding keyring back with KEYCTL_READ returns
uninitialized slab until patches 2 and 3 are applied too.  Trigger
available off-list.

diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index 7a2ee0ded7c93..1739373172ad5 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -270,7 +270,7 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)
 	const struct keyring_index_key *index_key = data;
 	unsigned long chunk = 0;
 	const u8 *d;
-	int desc_len = index_key->desc_len, n = sizeof(chunk);
+	int desc_len = index_key->desc_len, n = sizeof(chunk), offset;
 
 	level /= ASSOC_ARRAY_KEY_CHUNK_SIZE;
 	switch (level) {
@@ -284,12 +284,12 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)
 		return (unsigned long)index_key->domain_tag;
 	default:
 		level -= 4;
-		if (desc_len <= sizeof(index_key->desc))
+		offset = sizeof(index_key->desc) + level * sizeof(long);
+		if (desc_len <= offset)
 			return 0;
 
-		d = index_key->description + sizeof(index_key->desc);
-		d += level * sizeof(long);
-		desc_len -= sizeof(index_key->desc);
+		d = index_key->description + offset;
+		desc_len -= offset;
 		if (desc_len > n)
 			desc_len = n;
 		do {
-- 
2.53.0


^ permalink raw reply related

* [PATCH v2 0/3] keys: fix keyring assoc-array out-of-bounds read and index inconsistency
From: Michael Bommarito @ 2026-07-14 11:54 UTC (permalink / raw)
  To: David Howells, Jarkko Sakkinen
  Cc: Andrew Morton, Paul Moore, James Morris, Serge E . Hallyn,
	keyrings, linux-security-module, linux-kernel

An unprivileged keyring whose keys collide through the description-chunk
path can drive assoc_array node splitting into an out-of-bounds slot write.
Patch 1 stops the out-of-bounds read in keyring_get_key_chunk(); patch 2
makes the chunk byte order agree with keyring_diff_objects(); patch 3 fixes
the shortcut-walk trim so the walk cannot be steered down the wrong
descendant.

v2 changes (patch 3 only; patches 1 and 2 are unchanged):
As sashiko pointed out, the v1 patch-3 guard (sc_level + CHUNK >
skip_to_level) fixed the word-aligned leak but wrongly fired for an
unaligned first word whose skip_to_level sits on the next chunk
boundary: shift = skip_to_level & CHUNK_MASK is then 0 and the trim clears
the whole dissimilarity word, making a differing shortcut compare equal.  v2
keys the trim on the end of the chunk that contains sc_level,
round_down(sc_level, CHUNK) + CHUNK, which matches a brute-force oracle over
every sc_level/skip_to_level pair; the original round_up guard and the v1
guard each disagree with the oracle in one regime.

v1: https://lore.kernel.org/keyrings/20260712014500.480410-1-michael.bommarito@gmail.com/

Michael Bommarito (3):
  keys: fix out-of-bounds read in keyring_get_key_chunk()
  keys: make keyring key-chunk byte order agree with keyring_diff_objects()
  assoc_array: trim the final shortcut word using the current chunk end

 lib/assoc_array.c       |  3 ++-
 security/keys/keyring.c | 15 ++++++++-------
 2 files changed, 10 insertions(+), 8 deletions(-)

-- 
2.53.0

^ permalink raw reply

* Re: [PATCH -next] ima: add cond_resched() in ima_calc_file_hash_tfm loop
From: cuigaosheng @ 2026-07-14  9:28 UTC (permalink / raw)
  To: Roberto Sassu, lujialin4, zohar, roberto.sassu, dmitry.kasatkin,
	eric.snowberg, paul, jmorris, serge
  Cc: linux-integrity, linux-security-module
In-Reply-To: <8c757ecb48c7dd733eb10c3927e9db94cb056fb0.camel@huaweicloud.com>

On 2026/7/13 20:06, Roberto Sassu wrote:
> On Mon, 2026-07-13 at 11:10 +0000, Gaosheng Cui wrote:
>> When hashing large files, the while loop in ima_calc_file_hash_tfm
>> processes PAGE_SIZE chunks without any scheduling point, which can
>> cause soft lockup warnings:
>> watchdog: BUG: soft lockup - CPU#0 stuck for 50s!
>> Call Trace:
>>    _sha256_update+0x12d/0x1a0
>>    ima_calc_file_hash_tfm+0xfb/0x150
>>    ima_calc_file_hash+0x6e/0x160
>>    ima_collect_measurement+0x202/0x340
>>    process_measurement+0x3a9/0xb30
>>    ima_file_check+0x56/0xa0
>>    do_open+0x11b/0x250
>>    path_openat+0x10b/0x1d0
>>    do_filp_open+0xa9/0x150
>>    do_sys_openat2+0x223/0x2a0
>>    __x64_sys_openat+0x54/0xa0
>>    do_syscall_64+0x59/0x110
>>    entry_SYSCALL_64_after_hwframe+0x78/0xe2
>>
>> Add cond_resched() at the end of each loop iteration to voluntarily
>> yield the CPU when needed.
>>
>> Fixes: 3323eec921ef ("integrity: IMA as an integrity service provider")
>> Signed-off-by: Gaosheng Cui <cuigaosheng1@huawei.com>
>> ---
>>   security/integrity/ima/ima_crypto.c | 2 ++
>>   1 file changed, 2 insertions(+)
>>
>> diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c
>> index 0d72b48249ee..3b7d41a9fd18 100644
>> --- a/security/integrity/ima/ima_crypto.c
>> +++ b/security/integrity/ima/ima_crypto.c
>> @@ -233,6 +233,8 @@ static int ima_calc_file_hash_tfm(struct file *file,
>>   		rc = crypto_shash_update(shash, rbuf, rbuf_len);
>>   		if (rc)
>>   			break;
>> +
>> +		cond_resched();
> I would recommend not doing at every loop but every 4 MB at least:
>
> if (IS_ALIGNED(offset, SZ_4M))
>          cond_resched();
>
> Thanks
>
> Roberto

Thanks,

I've updated to patch v2,call cond_resched() every 4MB.

>>   	}
>>   	kfree(rbuf);
>>   out:
> .

^ permalink raw reply

* Re: [PATCH -next,v2] ima: add cond_resched() in ima_calc_file_hash_tfm loop
From: Roberto Sassu @ 2026-07-14  9:26 UTC (permalink / raw)
  To: Gaosheng Cui, lujialin4, zohar, roberto.sassu, dmitry.kasatkin,
	eric.snowberg, paul, jmorris, serge
  Cc: linux-integrity, linux-security-module
In-Reply-To: <20260714091708.2802272-1-cuigaosheng1@huawei.com>

On Tue, 2026-07-14 at 17:17 +0800, Gaosheng Cui wrote:
> When hashing large files, the while loop in ima_calc_file_hash_tfm
> processes PAGE_SIZE chunks without any scheduling point, which can
> cause soft lockup warnings:
> watchdog: BUG: soft lockup - CPU#0 stuck for 50s!
> Call Trace:
>   _sha256_update+0x12d/0x1a0
>   ima_calc_file_hash_tfm+0xfb/0x150
>   ima_calc_file_hash+0x6e/0x160
>   ima_collect_measurement+0x202/0x340
>   process_measurement+0x3a9/0xb30
>   ima_file_check+0x56/0xa0
>   do_open+0x11b/0x250
>   path_openat+0x10b/0x1d0
>   do_filp_open+0xa9/0x150
>   do_sys_openat2+0x223/0x2a0
>   __x64_sys_openat+0x54/0xa0
>   do_syscall_64+0x59/0x110
>   entry_SYSCALL_64_after_hwframe+0x78/0xe2
> 
> Call cond_resched() every 4MB to yield the CPU when needed, rather
> than at every loop iteration, to reduce overhead.
> 
> Fixes: 3323eec921ef ("integrity: IMA as an integrity service provider")
> Signed-off-by: Gaosheng Cui <cuigaosheng1@huawei.com>

Reviewed-by: Roberto Sassu <roberto.sassu@huawei.com>

Thanks

Roberto

> ---
> v2: call cond_resched() every 4MB to yield the CPU when needed
>  security/integrity/ima/ima_crypto.c | 3 +++
>  1 file changed, 3 insertions(+)
> 
> diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c
> index 0d72b48249ee..aab2349c0c33 100644
> --- a/security/integrity/ima/ima_crypto.c
> +++ b/security/integrity/ima/ima_crypto.c
> @@ -233,6 +233,9 @@ static int ima_calc_file_hash_tfm(struct file *file,
>  		rc = crypto_shash_update(shash, rbuf, rbuf_len);
>  		if (rc)
>  			break;
> +
> +		if (IS_ALIGNED(offset, SZ_4M))
> +			cond_resched();
>  	}
>  	kfree(rbuf);
>  out:


^ permalink raw reply

* [PATCH -next,v2] ima: add cond_resched() in ima_calc_file_hash_tfm loop
From: Gaosheng Cui @ 2026-07-14  9:17 UTC (permalink / raw)
  To: cuigaosheng1, lujialin4, zohar, roberto.sassu, dmitry.kasatkin,
	eric.snowberg, paul, jmorris, serge
  Cc: linux-integrity, linux-security-module

When hashing large files, the while loop in ima_calc_file_hash_tfm
processes PAGE_SIZE chunks without any scheduling point, which can
cause soft lockup warnings:
watchdog: BUG: soft lockup - CPU#0 stuck for 50s!
Call Trace:
  _sha256_update+0x12d/0x1a0
  ima_calc_file_hash_tfm+0xfb/0x150
  ima_calc_file_hash+0x6e/0x160
  ima_collect_measurement+0x202/0x340
  process_measurement+0x3a9/0xb30
  ima_file_check+0x56/0xa0
  do_open+0x11b/0x250
  path_openat+0x10b/0x1d0
  do_filp_open+0xa9/0x150
  do_sys_openat2+0x223/0x2a0
  __x64_sys_openat+0x54/0xa0
  do_syscall_64+0x59/0x110
  entry_SYSCALL_64_after_hwframe+0x78/0xe2

Call cond_resched() every 4MB to yield the CPU when needed, rather
than at every loop iteration, to reduce overhead.

Fixes: 3323eec921ef ("integrity: IMA as an integrity service provider")
Signed-off-by: Gaosheng Cui <cuigaosheng1@huawei.com>
---
v2: call cond_resched() every 4MB to yield the CPU when needed
 security/integrity/ima/ima_crypto.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c
index 0d72b48249ee..aab2349c0c33 100644
--- a/security/integrity/ima/ima_crypto.c
+++ b/security/integrity/ima/ima_crypto.c
@@ -233,6 +233,9 @@ static int ima_calc_file_hash_tfm(struct file *file,
 		rc = crypto_shash_update(shash, rbuf, rbuf_len);
 		if (rc)
 			break;
+
+		if (IS_ALIGNED(offset, SZ_4M))
+			cond_resched();
 	}
 	kfree(rbuf);
 out:
-- 
2.43.0


^ permalink raw reply related

* Re: [PATCH 0/3] keys: fix keyring assoc-array out-of-bounds read and index inconsistency
From: Andrew Morton @ 2026-07-14  2:11 UTC (permalink / raw)
  To: Michael Bommarito
  Cc: David Howells, Jarkko Sakkinen, Paul Moore, James Morris,
	Serge E . Hallyn, keyrings, linux-security-module, linux-kernel
In-Reply-To: <20260712014500.480410-1-michael.bommarito@gmail.com>

On Sat, 11 Jul 2026 21:44:57 -0400 Michael Bommarito <michael.bommarito@gmail.com> wrote:

> keyring_get_key_chunk() advances the description read pointer by
> level * sizeof(long) past the inline prefix but only bounds-checks the
> prefix, so once the associative-array walk reaches a description-level
> chunk it reads past the kmemdup(desc, desc_len + 1) description
> allocation.  Reaching that depth needs two keys that collide through the
> hash, x, type and domain_tag chunks, which an unprivileged add_key(2)
> can arrange with a crafted pair of same-type keys.
> 
> An unprivileged user can thus read up to sizeof(long) bytes past a
> keyring key's description; on kernels built without init-on-alloc the
> same collision, read back with KEYCTL_READ, returns uninitialized kernel
> slab.
> 
> Patch 1 is the memory-safety fix and stands alone.  Patches 2 and 3 fix
> two index-key consistency bugs that let the crafted keys collide into a
> single malformed node in the first place, which is what enables the
> KEYCTL_READ disclosure.
> 
> The KASAN reproduction is on patch 1. Trigger is available off-list.

fyi, AI review might have found things, some pre-existing:
	https://sashiko.dev/#/patchset/20260712014500.480410-1-michael.bommarito@gmail.com

David, you might wish to take a look at the first assoc_array.c issue.


^ permalink raw reply

* [PATCH 2/2] doc: LSM: fix module ordering description for /sys/kernel/security/lsm
From: Lincoln Wallace @ 2026-07-14  1:38 UTC (permalink / raw)
  To: paul, corbet
  Cc: skhan, linux-doc, linux-kernel, linux-security-module,
	penguin-kernel, rdunlap, Lincoln Wallace
In-Reply-To: <20260714013832.977443-1-locnnil0@gmail.com>

The LSM usage document states that the capability module will always
be first in /sys/kernel/security/lsm, followed by any "minor" modules
and then the one "major" module.

This does not match the current LSM infrastructure:

 - When CONFIG_SECURITY_LOCKDOWN_LSM_EARLY is enabled, lockdown is
   initialized as an early LSM, before all other modules including
   capability, and appears first in the list.

 - The integrity modules (e.g. IMA and EVM) register with
   LSM_ORDER_LAST and are always placed at the end of the list,
   regardless of the position of the major module.

 - The relative order of the remaining modules is not fixed by the
   framework; it follows CONFIG_LSM or the "lsm=" kernel command
   line parameter.

Rewrite the paragraph to describe the actual ordering: lockdown
first when early lockdown is enabled, capability otherwise,
integrity modules at the end, and the remaining modules in the
configured order.

Signed-off-by: Lincoln Wallace <locnnil0@gmail.com>
---
 Documentation/admin-guide/LSM/index.rst | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/Documentation/admin-guide/LSM/index.rst b/Documentation/admin-guide/LSM/index.rst
index c24310c709dc..9518495edfbc 100644
--- a/Documentation/admin-guide/LSM/index.rst
+++ b/Documentation/admin-guide/LSM/index.rst
@@ -27,9 +27,15 @@ man-pages project.
 A list of the active security modules can be found by reading
 ``/sys/kernel/security/lsm``. This is a comma separated list, and
 will always include the capability module. The list reflects the
-order in which checks are made. The capability module will always
-be first, followed by any "minor" modules (e.g. Yama) and then
-the one "major" module (e.g. SELinux) if there is one configured.
+order in which checks are made. The capability module will be
+first, unless CONFIG_SECURITY_LOCKDOWN_LSM_EARLY is enabled, in
+which case the lockdown module will precede it. The integrity
+modules (e.g. IMA and EVM), if enabled in the kernel
+configuration, are always placed at the end of the list. Any
+other "minor" modules (e.g. Yama) and the one "major" module
+(e.g. SELinux), if there is one configured, appear in between,
+in the order given by CONFIG_LSM or the ``"lsm=..."`` kernel
+command line parameter.
 
 Process attributes associated with "major" security modules should
 be accessed and maintained using the special files in ``/proc/.../attr``.
-- 
2.53.0


^ permalink raw reply related

* [PATCH 1/2] doc: LSM: describe CONFIG_LSM and lsm= as the selection mechanism
From: Lincoln Wallace @ 2026-07-14  1:38 UTC (permalink / raw)
  To: paul, corbet
  Cc: skhan, linux-doc, linux-kernel, linux-security-module,
	penguin-kernel, rdunlap, Lincoln Wallace
In-Reply-To: <20260714013832.977443-1-locnnil0@gmail.com>

The LSM usage document states that security modules are selectable at
build time via CONFIG_DEFAULT_SECURITY and can be overridden at boot
time via the "security=..." kernel command line argument.

CONFIG_DEFAULT_SECURITY no longer exists: LSMs are enabled via
CONFIG_LSM, an ordered list of the LSMs to initialize, which can be
overridden at boot time with the "lsm=" parameter. The "security="
parameter remains as a deprecated way to choose a legacy "major"
security module, and is ignored when "lsm=" is specified; see commit
89a9684ea158 ("LSM: Ignore "security=" when "lsm=" is specified").

A previous attempt replaced "security=" with "lsm=" in place [1],
which was rejected because the parameters are not equivalent:
"security=" selects a single major module while the built-in
CONFIG_LSM list otherwise remains active, whereas "lsm=" must list
every LSM to enable.

Update the paragraph to describe CONFIG_LSM and "lsm=" as the current
selection mechanism, keeping "security=" documented as the deprecated
legacy option, matching the wording in kernel-parameters.txt.

Link: https://lore.kernel.org/r/20250114225156.10458-1-rdunlap@infradead.org [1]

Signed-off-by: Lincoln Wallace <locnnil0@gmail.com>
---
 Documentation/admin-guide/LSM/index.rst | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/Documentation/admin-guide/LSM/index.rst b/Documentation/admin-guide/LSM/index.rst
index b44ef68f6e4d..c24310c709dc 100644
--- a/Documentation/admin-guide/LSM/index.rst
+++ b/Documentation/admin-guide/LSM/index.rst
@@ -6,9 +6,11 @@ The Linux Security Module (LSM) framework provides a mechanism for
 various security checks to be hooked by new kernel extensions. The name
 "module" is a bit of a misnomer since these extensions are not actually
 loadable kernel modules. Instead, they are selectable at build-time via
-CONFIG_DEFAULT_SECURITY and can be overridden at boot-time via the
-``"security=..."`` kernel command line argument, in the case where multiple
-LSMs were built into a given kernel.
+CONFIG_LSM, an ordered list of the LSMs to enable, and can be
+overridden at boot-time via the ``"lsm=..."`` kernel command line
+argument. The ``"security=..."`` kernel command line argument remains
+available to choose a legacy "major" security module, but has been
+deprecated by the ``"lsm=..."`` parameter.
 
 The primary users of the LSM interface are Mandatory Access Control
 (MAC) extensions which provide a comprehensive security policy. Examples
-- 
2.53.0


^ permalink raw reply related

* [PATCH 0/2] doc: LSM: update usage document for current LSM stacking
From: Lincoln Wallace @ 2026-07-14  1:38 UTC (permalink / raw)
  To: paul, corbet
  Cc: skhan, linux-doc, linux-kernel, linux-security-module,
	penguin-kernel, rdunlap, Lincoln Wallace

The LSM usage document (Documentation/admin-guide/LSM/index.rst) has
not kept up with the LSM stacking infrastructure. It still describes
CONFIG_DEFAULT_SECURITY, which no longer exists, and its description
of the module ordering in /sys/kernel/security/lsm does not match
what the framework actually does.

Patch 1 updates the selection mechanism description to CONFIG_LSM and
the "lsm=" parameter, keeping "security=" documented as the deprecated
legacy option. This revisits an earlier attempt by Randy Dunlap [1]
that was rejected for treating the two parameters as equivalent; the
new text keeps them distinct.

Patch 2 fixes the ordering description: lockdown precedes capability
when CONFIG_SECURITY_LOCKDOWN_LSM_EARLY is enabled, the integrity
modules are always placed at the end of the list, and the remaining
modules follow the order given by CONFIG_LSM or "lsm=".

[1] https://lore.kernel.org/r/20250114225156.10458-1-rdunlap@infradead.org

Lincoln Wallace (2):
  doc: LSM: describe CONFIG_LSM and lsm= as the selection mechanism
  doc: LSM: fix module ordering description for /sys/kernel/security/lsm

 Documentation/admin-guide/LSM/index.rst | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

-- 
2.53.0


^ permalink raw reply

* Re: [PATCH v2 0/7] fs/9p: Reuse inode based on path (in addition to qid)
From: Tingmao Wang @ 2026-07-14  1:14 UTC (permalink / raw)
  To: Greg Kurz, Christian Schoenebeck, Mickaël Salaün,
	Dominique Martinet
  Cc: qemu-devel, Eric Van Hensbergen, Latchesar Ionkov, v9fs,
	Günther Noack, linux-security-module, Jan Kara,
	Amir Goldstein, Matthew Bobrowski, Al Viro, Christian Brauner,
	linux-fsdevel, Justin Suess
In-Reply-To: <20251013112424.6b93659c@bahia>

Hi Christian, Greg, Dominique,

Trying to revive this discussion, some answers to Greg's questions below,
then more thoughts on this series and alternatives.

On 10/13/25 10:24, Greg Kurz wrote:
> On Mon, 29 Sep 2025 15:06:59 +0200
> Christian Schoenebeck <linux_oss@crudebyte.com> wrote:
> 
>> On Sunday, September 21, 2025 6:24:49 PM CEST Tingmao Wang wrote:
>>> On 9/17/25 16:00, Mickaël Salaün wrote:
>> [...]
>>
>> Hi Greg,
>>
> 
> Hi Christian,
> 
>> I'd appreciate comments from your side as well, as you are much on longer on
>> the QEMU 9p front than me.
>>
>> I know you won't have the time to read up on the entire thread so I try to
>> summarize: basically this is yet another user-after-unlink issue, this time on
>> directories instead of files.
>>
> 
> Thread that never landed in my mailbox actually and it is quite
> hard to understand the root problem with the content of this
> e-mail actually ;-)
> 
>>> So I did some quick debugging and realized that I had a wrong
>>> understanding of how fids relates to opened files on the host, under QEMU.
>>> It turns out that in QEMU's 9p server implementation, a fid does not
>>> actually correspond to any opened file descriptors - it merely represents
>>> a (string-based) path that QEMU stores internally.  It only opens the
>>> actual file if the client actually does an T(l)open, which is in fact
>>> separate from acquiring the fid with T(l)walk.  The reason why renaming
>>> file/dirs from the client doesn't break those fids is because QEMU will
>>> actually fix those paths when a rename request is processed - c.f.
>>> v9fs_fix_fid_paths [1].
>>
>> Correct, that's based on what the 9p protocols define: a FID does not exactly
>> translate to what a file handle is on a local system. Even after acquiring a
>> new FID by sending a Twalk request, subsequently client would still need to
>> send a Topen for server to actually open that file/directory.
>>
>> And yes, QEMU's 9p server "fixes" the path string of a FID if it was moved
>> upon client request. If the move happened on host side, outside of server's
>> knowledge, then this won't happen ATM and hence it would break your use
>> case.
>>
>>> It turns out that even if a guest process opens the file with O_PATH, that
>>> file descriptor does not cause an actual Topen, and therefore QEMU does
>>> not open the file on the host, and later on reopening that fd with another
>>> mode (via e.g. open("/proc/self/fd/...", O_RDONLY)) will fail if the file
>>> has moved on the host without QEMU's knowledge.  Also, openat will fail if
>>> provided with a dir fd that "points" to a moved directory, regardless of
>>> whether the fd is opened with O_PATH or not, since path walk in QEMU is
>>> completely string-based and does not actually issue openat on the host fs
>>> [2].
>>
>> I don't think the problem here is the string based walk per se, but rather
>> that the string based walk always starts from the export root:
>>
>> https://github.com/qemu/qemu/blob/4975b64efb5aa4248cbc3760312bbe08d6e71638/hw/9pfs/9p-local.c#L64
>>
>> I guess that's something that could be changed in QEMU such that the walk
>> starts from FID's fs point, as the code already uses openat() to walk relative
>> to a file descriptor (for security reasons actually), Greg?
>>
> 
> Yes this was introduced for security reasons. In a nutshell, the idea is
> to *not* follow symlinks in any element of the path being opened. It thus
> naturally starts at the export root for which we have an fd.
> 
>> That alone would still not fix your use case though: things being moved on
>> host side. For this to work, it would require to already have a fd open on
>> host for the FID. This could be done by server for each FID as you suggested,
>> or it could be done by client by opening the FID.
>>
> 
> Can you elaborate on the "things being move on host side" ? With
> an example of code that breaks on the client side ?

There are two cases where things break: openat() with a previously opened
dir fd, and re-open()ing an O_PATH fd as readable or writable with
/proc/self/fd.

For the first case,
https://fileshare.maowtm.org/9pfs-landlock-fix/20250921/openat.c
is an example program that demonstrates that in QEMU, path walk always
starts from the export root.  The breakage is when the dir is moved on the
host, even if the file still exists within the dir with the same name, the
openat() with the opened dir fd (which translates to a fid in v9fs)
breaks:

    (Host tmpfs exported as 9p to guest /tmp/9p)
    root@7-2-0-rc2-dev:/# ./openat /tmp/9p/dir file
    [   12.264880][  T183] 9pnet: (00000183) >>> TWALK fids 1,2 nwname 1d wname[0] dir
    [   12.265156][  T183] 9pnet: (00000183) >>> size=22 type: 110 tag: 0
    [   12.265466][  T183] 9pnet: (00000183) <<< size=22 type: 111 tag: 0
    [   12.265590][  T183] 9pnet: (00000183) <<< RWALK nwqid 1:
    [   12.265675][  T183] 9pnet: (00000183) <<<     [0] 80.6d10.6a53fe73
    [   12.265781][  T183] 9pnet: (00000183) >>> TGETATTR fid 2, request_mask 6143
    [   12.265903][  T183] 9pnet: (00000183) >>> size=19 type: 24 tag: 0
    [   12.266062][  T183] 9pnet: (00000183) <<< size=160 type: 25 tag: 0
    [   12.266148][  T183] 9pnet: (00000183) <<< RGETATTR st_result_mask=6143
    [   12.266148][  T183] <<< qid=80.6d10.6a53fe73
    [   12.266148][  T183] <<< st_mode=000041e8 st_nlink=2
    [   12.266148][  T183] <<< st_uid=1000 st_gid=1000
    [   12.266148][  T183] <<< st_rdev=0 st_size=3c st_blksize=131072 st_blocks=0
    [   12.266148][  T183] <<< st_atime_sec=1783874216 st_atime_nsec=302475718
    [   12.266148][  T183] <<< st_mtime_sec=1783874163 st_mtime_nsec=254338030
    [   12.266148][  T183] <<< st_ctime_sec=1783874163 st_ctime_nsec=254338030
    [   12.266148][  T183] <<< st_btime_sec=0 st_btime_nsec=0
    [   12.266148][  T183] <<< st_gen=0 st_data_version=0
    [   12.266932][  T183] 9pnet: (00000183) >>> TWALK fids 2,3 nwname 0d wname[0] (null)
    [   12.267038][  T183] 9pnet: (00000183) >>> size=17 type: 110 tag: 0
    [   12.267189][  T183] 9pnet: (00000183) <<< size=9 type: 111 tag: 0
    [   12.267300][  T183] 9pnet: (00000183) <<< RWALK nwqid 0:
    [   12.267368][  T183] 9pnet: (00000183) >>> TLOPEN fid 3 mode 98304
    [   12.267530][  T183] 9pnet: (00000183) >>> size=15 type: 12 tag: 0
    [   12.267779][  T183] 9pnet: (00000183) <<< size=24 type: 13 tag: 0
    [   12.267867][  T183] 9pnet: (00000183) <<< RLOPEN qid 80.6d10.6a53fe73 iounit 0
    Opened /tmp/9p/dir with fd 3
    [   12.268713][  T183] 9pnet: (00000183) >>> TWALK fids 2,4 nwname 1d wname[0] file
    [   12.268816][  T183] 9pnet: (00000183) >>> size=23 type: 110 tag: 0
    [   12.268961][  T183] 9pnet: (00000183) <<< size=22 type: 111 tag: 0
    [   12.269051][  T183] 9pnet: (00000183) <<< RWALK nwqid 1:
    [   12.269116][  T183] 9pnet: (00000183) <<<     [0] 0.6d11.6a53c273
    [   12.269198][  T183] 9pnet: (00000183) >>> TGETATTR fid 4, request_mask 6143
    [   12.269305][  T183] 9pnet: (00000183) >>> size=19 type: 24 tag: 0
    [   12.269454][  T183] 9pnet: (00000183) <<< size=160 type: 25 tag: 0
    [   12.269538][  T183] 9pnet: (00000183) <<< RGETATTR st_result_mask=6143
    [   12.269538][  T183] <<< qid=0.6d11.6a53c273
    [   12.269538][  T183] <<< st_mode=000081a0 st_nlink=1
    [   12.269538][  T183] <<< st_uid=1000 st_gid=1000
    [   12.269538][  T183] <<< st_rdev=0 st_size=0 st_blksize=131072 st_blocks=0
    [   12.269538][  T183] <<< st_atime_sec=1783874265 st_atime_nsec=602603686
    [   12.269538][  T183] <<< st_mtime_sec=1783874163 st_mtime_nsec=254338030
    [   12.269538][  T183] <<< st_ctime_sec=1783874163 st_ctime_nsec=254338030
    [   12.269538][  T183] <<< st_btime_sec=0 st_btime_nsec=0
    [   12.269538][  T183] <<< st_gen=0 st_data_version=0
    [   12.270149][  T183] 9pnet: (00000183) >>> TWALK fids 4,5 nwname 0d wname[0] (null)
    [   12.270249][  T183] 9pnet: (00000183) >>> size=17 type: 110 tag: 0
    [   12.270406][  T183] 9pnet: (00000183) <<< size=9 type: 111 tag: 0
    [   12.270497][  T183] 9pnet: (00000183) <<< RWALK nwqid 0:
    [   12.270561][  T183] 9pnet: (00000183) >>> TLOPEN fid 5 mode 32768
    [   12.270696][  T183] 9pnet: (00000183) >>> size=15 type: 12 tag: 0
    [   12.270879][  T183] 9pnet: (00000183) <<< size=24 type: 13 tag: 0
    [   12.270962][  T183] 9pnet: (00000183) <<< RLOPEN qid 0.6d11.6a53c273 iounit 20000
    Opened file with fd 4
    [   12.271278][  T183] 9pnet: (00000183) >>> TREAD fid 5 offset 0 255
    [   12.271393][  T183] 9pnet: (00000183) >>> size=23 type: 116 tag: 0
    [   12.271577][  T183] 9pnet: (00000183) <<< size=11 type: 117 tag: 0
    [   12.271667][  T183] 9pnet: (00000183) <<< RREAD count 0
    Read 0 bytes:

    [   12.271843][  T183] 9pnet: (00000183) >>> TCLUNK fid 5 (try 0)
    [   12.271978][  T183] 9pnet: (00000183) >>> size=11 type: 120 tag: 0
    [   12.272128][  T183] 9pnet: (00000183) <<< size=7 type: 121 tag: 0
    [   12.272212][  T183] 9pnet: (00000183) <<< RCLUNK fid 5
    [   12.272309][  T183] 9pnet: (00000183) >>> TCLUNK fid 4 (try 0)
    [   12.272407][  T183] 9pnet: (00000183) >>> size=11 type: 120 tag: 0
    [   12.272535][  T183] 9pnet: (00000183) <<< size=7 type: 121 tag: 0
    [   12.272618][  T183] 9pnet: (00000183) <<< RCLUNK fid 4
    Press Enter to openat again or Ctrl-C to exit

    (at this point dir is moved to dir1 on the host, then Enter is pressed here)

    [   32.441881][  T183] 9pnet: (00000183) >>> TWALK fids 2,4 nwname 1d wname[0] file
    [   32.442038][  T183] 9pnet: (00000183) >>> size=23 type: 110 tag: 0
    [   32.442330][  T183] 9pnet: (00000183) <<< size=11 type: 7 tag: 0
    [   32.442438][  T183] 9pnet: (00000183) <<< RLERROR (-2)
    openat: No such file or directory
    [   32.443619][  T183] 9pnet: (00000183) >>> TCLUNK fid 3 (try 0)
    [   32.443790][  T183] 9pnet: (00000183) >>> size=11 type: 120 tag: 0
    [   32.443942][  T183] 9pnet: (00000183) <<< size=7 type: 121 tag: 0
    [   32.444037][  T183] 9pnet: (00000183) <<< RCLUNK fid 3
    [   32.444127][  T183] 9pnet: (00000183) >>> TCLUNK fid 2 (try 0)
    [   32.444235][  T183] 9pnet: (00000183) >>> size=11 type: 120 tag: 0
    [   32.444364][  T183] 9pnet: (00000183) <<< size=7 type: 121 tag: 0
    [   32.444450][  T183] 9pnet: (00000183) <<< RCLUNK fid 2

In this case QEMU does hold a fd for the dir (since the dir is opened with
O_RDONLY, causing a TLOPEN), but as Christian mentioned, it always starts
the path walk from root, rather than using that fd.

QEMU git commit a759542a2c (today's master)

For the second case,
https://fileshare.maowtm.org/9pfs-landlock-fix/20250921/open_procselffd.c
reproduces it:

    (Host tmpfs exported as 9p to guest /tmp/9p)
    root@7-2-0-rc2-dev:/# ./open_procselffd /tmp/9p/dir/file
    [   24.243001][  T188] 9pnet: (00000188) >>> TWALK fids 1,2 nwname 1d wname[0] dir
    [   24.243305][  T188] 9pnet: (00000188) >>> size=22 type: 110 tag: 0
    [   24.243659][  T188] 9pnet: (00000188) <<< size=22 type: 111 tag: 0
    [   24.243855][  T188] 9pnet: (00000188) <<< RWALK nwqid 1:
    [   24.243983][  T188] 9pnet: (00000188) <<<     [0] 80.6d10.6a53fe73
    [   24.244124][  T188] 9pnet: (00000188) >>> TGETATTR fid 2, request_mask 6143
    [   24.244270][  T188] 9pnet: (00000188) >>> size=19 type: 24 tag: 0
    [   24.244460][  T188] 9pnet: (00000188) <<< size=160 type: 25 tag: 0
    [   24.244599][  T188] 9pnet: (00000188) <<< RGETATTR st_result_mask=6143
    [   24.244599][  T188] <<< qid=80.6d10.6a53fe73
    [   24.244599][  T188] <<< st_mode=000041e8 st_nlink=2
    [   24.244599][  T188] <<< st_uid=1000 st_gid=1000
    [   24.244599][  T188] <<< st_rdev=0 st_size=3c st_blksize=131072 st_blocks=0
    [   24.244599][  T188] <<< st_atime_sec=1783874216 st_atime_nsec=302475718
    [   24.244599][  T188] <<< st_mtime_sec=1783874163 st_mtime_nsec=254338030
    [   24.244599][  T188] <<< st_ctime_sec=1783875959 st_ctime_nsec=665487467
    [   24.244599][  T188] <<< st_btime_sec=0 st_btime_nsec=0
    [   24.244599][  T188] <<< st_gen=0 st_data_version=0
    [   24.245601][  T188] 9pnet: (00000188) >>> TWALK fids 2,3 nwname 1d wname[0] file
    [   24.245754][  T188] 9pnet: (00000188) >>> size=23 type: 110 tag: 0
    [   24.245936][  T188] 9pnet: (00000188) <<< size=22 type: 111 tag: 0
    [   24.246070][  T188] 9pnet: (00000188) <<< RWALK nwqid 1:
    [   24.246165][  T188] 9pnet: (00000188) <<<     [0] 0.6d11.6a53c273
    [   24.246287][  T188] 9pnet: (00000188) >>> TGETATTR fid 3, request_mask 6143
    [   24.246484][  T188] 9pnet: (00000188) >>> size=19 type: 24 tag: 0
    [   24.246661][  T188] 9pnet: (00000188) <<< size=160 type: 25 tag: 0
    [   24.246780][  T188] 9pnet: (00000188) <<< RGETATTR st_result_mask=6143
    [   24.246780][  T188] <<< qid=0.6d11.6a53c273
    [   24.246780][  T188] <<< st_mode=000081a0 st_nlink=1
    [   24.246780][  T188] <<< st_uid=1000 st_gid=1000
    [   24.246780][  T188] <<< st_rdev=0 st_size=0 st_blksize=131072 st_blocks=0
    [   24.246780][  T188] <<< st_atime_sec=1783875507 st_atime_nsec=502397563
    [   24.246780][  T188] <<< st_mtime_sec=1783874163 st_mtime_nsec=254338030
    [   24.246780][  T188] <<< st_ctime_sec=1783874163 st_ctime_nsec=254338030
    [   24.246780][  T188] <<< st_btime_sec=0 st_btime_nsec=0
    [   24.246780][  T188] <<< st_gen=0 st_data_version=0
    Opened /tmp/9p/dir/file with fd 3 (O_PATH)
    Press Enter to reopen

    (at this point dir/file is moved to dir/file2 on the host, then Enter is pressed here)
    (result is the same if instead dir is renamed)

    [   34.725233][  T188] 9pnet: (00000188) >>> TWALK fids 3,4 nwname 0d wname[0] (null)
    [   34.725619][  T188] 9pnet: (00000188) >>> size=17 type: 110 tag: 0
    [   34.726067][  T188] 9pnet: (00000188) <<< size=11 type: 7 tag: 0
    [   34.726241][  T188] 9pnet: (00000188) <<< RLERROR (-2)
    Failed to open /proc/self/fd/3: No such file or directory
    Press Enter to exit

With this case, there is no TLOPEN, only TWALK, so QEMU does not hold a fd
for the file.  (This means that, for Landlock, merely holding a fid is not
enough to prevent inode number reuse on QEMU)

> 
>> Also keep in mind: once the open file descriptor limit on host is exhausted,
>> QEMU is forced to close older open file desciptors to keep the QEMU process
>> alive. So this might still break what you are trying to achieve there.
>>
> 
> Correct.
> 
>> Having said that, I wonder whether it'd be simpler for server to track for
>> file tree changes (inotify API) and fix the pathes accordingly for host
>> side changes as well?
>>
> 
> The problem is how to have the guest know about such changes, e.g. in
> order to invalidate a stale cache entry. 9P doesn't provide any way for
> host->client notification.

I think there are two separate problems here - the server using inotify to
"fixup" its own tracked path of each fids, and the client being notified
of changes.  With what Christian suggested, the above two cases will not
break even without any client notification, since the client (in this case
the kernel) already re-uses previously obtained fids to avoid redoing the
path walk from root.

Having client notification might be useful for other cases tho.  Aside
from supporting things like inotify triggered from server-side
notification, it also looks like if a fd is passed to another user, on
access=user or access=client 9pfs will in fact re-do the path walk from
root (v9fs_fid_lookup_with_uid), and so having notification could let us
know what the expected path has changed to in order to correctly re-open a
(stale) dentry that may have been moved on the host.

> 
>> /Christian
>>
>>
> 
> Cheers,
> 

I think overall there are still largely two directions, and I'm unclear
which way is best, but some recent testing has made me realize that the
second approach might be simpler than I thought:

- One is to continue with this path-based approach implemented in this RFC
  series.  The main issue I can see of doing this is the somewhat unwieldy
  path tracking, and handling of parent renames (with the current
  implementation it would need to recursively update all child ino_paths).
  However, from a correctness point of view, inodeident=path proposed here
  really means that both the path, qid.path, and inode type has to match,
  and so the risk of "false inode matches" like the kind observed earlier,
  leading to the revert of 724a08450f74 ("fs/9p: simplify iget to remove
  unnecessary paths") and various related commits, should be low.

  This also has the benefit of making i/fanotify on a cache=none 9pfs work
  again (for local changes), aside from Landlock.

- The other is to have special handling in Landlock: Landlock would key
  its rules on 9pfs based on some 9p-exposed identifier, such as the
  fhandle as proposed by Dominique.  Through testing I realized that we
  could use st_gen to distinguish inodes with the same ino, and QEMU does
  expose the st_gen to the guest when the backing fs is something like
  ext4, so in fact this approach might be simplified (although I've only
  realized this today and probably need to try this out on more
  filesystems):

                                     v qid.path
                                       vvvvvvvv qid.version aka mtime
    [   78.617306][  T183] <<< qid=0.e.6a558647
    [   78.617306][  T183] <<< st_mode=000081a0 st_nlink=1
    [   78.617306][  T183] <<< st_uid=1000 st_gid=1000
    [   78.617306][  T183] <<< st_rdev=0 st_size=0 st_blksize=131072 st_blocks=0
    [   78.617306][  T183] <<< st_atime_sec=1783989831 st_atime_nsec=576593826
    [   78.617306][  T183] <<< st_mtime_sec=1783989831 st_mtime_nsec=576593826
    [   78.617306][  T183] <<< st_ctime_sec=1783989831 st_ctime_nsec=576593826
    [   78.617306][  T183] <<< st_btime_sec=0 st_btime_nsec=0
    [   78.617306][  T183] <<< st_gen=3634407333 st_data_version=0

  After rm + touch:

                                     v qid.path
                                       vvvvvvvv qid.version aka mtime
    [  100.234598][  T184] <<< qid=0.e.6a558659
    [  100.234598][  T184] <<< st_mode=000081a0 st_nlink=1
    [  100.234598][  T184] <<< st_uid=1000 st_gid=1000
    [  100.234598][  T184] <<< st_rdev=0 st_size=0 st_blksize=131072 st_blocks=0
    [  100.234598][  T184] <<< st_atime_sec=1783989849 st_atime_nsec=5600077
    [  100.234598][  T184] <<< st_mtime_sec=1783989849 st_mtime_nsec=5600077
    [  100.234598][  T184] <<< st_ctime_sec=1783989849 st_ctime_nsec=5600077
    [  100.234598][  T184] <<< st_btime_sec=0 st_btime_nsec=0
    [  100.234598][  T184] <<< st_gen=4062134779 st_data_version=0
                                      ^ this is different

  Previously I proposed that Landlock would need to hold a fid to keep the
  inode alive on the server side, to prevent inode number reuse when using
  qid.path as the rule key, but if the 9pfs-exposed fhandle includes this
  inode generation number, and Landlock uses that instead, then inode
  reuse is no longer an issue, without Landlock having to do anything
  special.

  (we can't use qid.version since that is mtime, which will change on
  modification / files added or removed in a directory)
  
Does anyone have preferences / suggestions on whether to keep going with
the path-tracking based approach (perhaps subject to figuring out a way to
handle parent renames), or an approach where Landlock simply stores the
fhandle (or (i_ino, i_generation (but this is u32 whereas st_gen within 9p
is u64))) as the rule key?

If I find some time over the next couple of weeks I will try out the
second approach since it might be turning out to be the simpler option,
but the drawback is that it doesn't enable i/fanotify on 9pfs, which I
originally hoped to achieve together with fixing Landlock on 9pfs.

Kind regards,
Tingmao

^ permalink raw reply

* Re: [PATCH 11/13 RFC net-next] net: cipso: guard IPv4 packet manipulation functions
From: Fernando Fernandez Mancera @ 2026-07-13 14:03 UTC (permalink / raw)
  To: Paul Moore
  Cc: netdev, davem, edumazet, kuba, pabeni, dsahern, horms, idosch,
	linux-security-module, linux-kernel
In-Reply-To: <CAHC9VhTA8Y+BLOcLcv6X9u3nRU=X7NQz-u4kZuVNL-Yz1mWKxw@mail.gmail.com>

On 7/12/26 6:22 PM, Paul Moore wrote:
> On Sat, Jul 11, 2026 at 9:41 PM Fernando Fernandez Mancera
> <fmancera@suse.de> wrote:
>>
>> To enable compiling the network stack without IPv4, the CIPSO functions
>> that manipulate IPv4 options and generate ICMP errors must be bypassed.
>>
>> Ideally, CIPSO should not be compiled when IPv4 is disabled but
>> currently it is too integrated within netlabel, so let's just bypassed
>> the relevant functions.
>>
>> Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
>> ---
>>   net/ipv4/cipso_ipv4.c | 10 ++++++++++
>>   1 file changed, 10 insertions(+)
> 
> I think I would prefer to make CONFIG_NETLABEL dependent on
> CONFIG_IPV4 at this point in time.  This will keep the code cleaner
> and allow time to do the proper work of wrapping the CIPSO code with
> CONFIG_CIPSO (or similar) and making that dependent on CONFIG_IPV4.
> 

Fair, that works for me too. I am gonna drop this on the v1.

Thank you!
Fernando.

^ permalink raw reply

* Re: [PATCH v3] hardening: Default randstruct off with rust for better allmodconfig support
From: Mark Brown @ 2026-07-13 14:02 UTC (permalink / raw)
  To: Miguel Ojeda
  Cc: Gary Guo, Kees Cook, Gustavo A. R. Silva, Paul Moore,
	James Morris, Serge E. Hallyn, Miguel Ojeda, Boqun Feng,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, linux-hardening,
	linux-security-module, linux-kernel, rust-for-linux
In-Reply-To: <CANiq72kNYRBW88VwYyDX8_2b1L7dLv_gDwgE5SruMELR7KPmig@mail.gmail.com>

[-- Attachment #1: Type: text/plain, Size: 734 bytes --]

On Mon, Jul 13, 2026 at 03:58:11PM +0200, Miguel Ojeda wrote:
> On Mon, Jul 13, 2026 at 3:55 PM Gary Guo <gary@garyguo.net> wrote:

> > I thought Miguel wants to add support to properly support RANDSTRUCT with Clang?

It would be great to have randstruct support for clang and Rust but we
do not currently have that support.

> I am happy either way, i.e. whatever the hardening folks prefer, but
> regardless of what we do, I think recovering the linux-next coverage
> is a high priority at the moment.

Yeah, when randstruct support is added we can remove all the constraints
around it and clang/Rust - this patch is about the making the current
situation work more usefully, it's not intended to be a final state.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply

* Re: [PATCH v3] hardening: Default randstruct off with rust for better allmodconfig support
From: Miguel Ojeda @ 2026-07-13 13:58 UTC (permalink / raw)
  To: Gary Guo
  Cc: Mark Brown, Kees Cook, Gustavo A. R. Silva, Paul Moore,
	James Morris, Serge E. Hallyn, Miguel Ojeda, Boqun Feng,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, linux-hardening,
	linux-security-module, linux-kernel, rust-for-linux
In-Reply-To: <DJXHTB615I2J.POTXT6L2LM5K@garyguo.net>

On Mon, Jul 13, 2026 at 3:55 PM Gary Guo <gary@garyguo.net> wrote:
>
> I thought Miguel wants to add support to properly support RANDSTRUCT with Clang?

I am happy either way, i.e. whatever the hardening folks prefer, but
regardless of what we do, I think recovering the linux-next coverage
is a high priority at the moment.

Cheers,
Miguel

^ permalink raw reply

* Re: [PATCH v3] hardening: Default randstruct off with rust for better allmodconfig support
From: Gary Guo @ 2026-07-13 13:54 UTC (permalink / raw)
  To: Mark Brown, Kees Cook, Gustavo A. R. Silva, Paul Moore,
	James Morris, Serge E. Hallyn, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich
  Cc: linux-hardening, linux-security-module, linux-kernel,
	rust-for-linux
In-Reply-To: <7cdb2407-924c-44a1-8c68-917efadd4e92@sirena.org.uk>

On Mon Jul 13, 2026 at 1:45 PM BST, Mark Brown wrote:
> On Thu, Jul 02, 2026 at 06:37:08PM +0100, Mark Brown wrote:
>> Currently randstruct does not support rust so we have Kconfig dependencies
>> which prevent rust being enabled when randstruct is. Unfortunately this
>> prevents rust being enabled in allmodconfig, our standard coverage build.
>> randstruct gets turned on by default, then the dependency on !RANDSTRUCT
>> causes rust to get disabled.
>
> Any news on this?  I'm flying blind doing -next merges of rust code with
> build coverage gone, and I suspect a bunch of other people think they
> have build coverage in their tests but it's silently vanished (I know at
> least one person ran into that already).

I thought Miguel wants to add support to properly support RANDSTRUCT with Clang?

Best,
Gary

^ permalink raw reply

* Re: [PATCH v3] hardening: Default randstruct off with rust for better allmodconfig support
From: Mark Brown @ 2026-07-13 12:45 UTC (permalink / raw)
  To: Kees Cook, Gustavo A. R. Silva, Paul Moore, James Morris,
	Serge E. Hallyn, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich
  Cc: linux-hardening, linux-security-module, linux-kernel,
	rust-for-linux
In-Reply-To: <20260702-rust-reverse-randstruct-dep-v3-1-e4e09c50014e@kernel.org>

[-- Attachment #1: Type: text/plain, Size: 651 bytes --]

On Thu, Jul 02, 2026 at 06:37:08PM +0100, Mark Brown wrote:
> Currently randstruct does not support rust so we have Kconfig dependencies
> which prevent rust being enabled when randstruct is. Unfortunately this
> prevents rust being enabled in allmodconfig, our standard coverage build.
> randstruct gets turned on by default, then the dependency on !RANDSTRUCT
> causes rust to get disabled.

Any news on this?  I'm flying blind doing -next merges of rust code with
build coverage gone, and I suspect a bunch of other people think they
have build coverage in their tests but it's silently vanished (I know at
least one person ran into that already).

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox