[PATCH 0/3] Implement LANDLOCK_RESTRICT_SELF_NNP_ON_EXEC
Justin Suess
utilityemal77 at gmail.com
Fri Jul 10 13:56:25 UTC 2026
On Thu, Jul 09, 2026 at 12:09:25PM +0200, Mickaël Salaün wrote:
> Hi Justin,
>
> Thanks for this patch series, I like the underlying idea, but I'm not
> convince we should mix NNP and ON_EXEC. Why not a
> LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS flag instead?
>
tldr I think your idea is better, see below.
> One issue with this NNP_ON_EXEC is that it might be confusing for one
> thread to set it and expect the next exec to be NNP, but if the exec is
> requested by another thread that would not happen. With a
> LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS flag, we could have an atomic
> Landlock and NNP enforcement, which can also be combined with the TSYNC
> flag, both useful cases.
>
> If we want an enforcement to happen at exec time, we could add a
> dedicated flag, similar to the TSYNC one, but I'm not sure it would be
> useful for now.
>
> On Wed, Jul 08, 2026 at 09:39:24AM -0400, Justin Suess wrote:
> > Good morning,
> >
> > This series adds a new landlock_restrict_self(2) flag:
> > LANDLOCK_RESTRICT_SELF_NNP_ON_EXEC.
> >
> > This flag stages a bit in the Landlock credentials indicating that the
> > next successful execution will set the no_new_privs attribute while
> > committing its new credentials.
> >
> > Differences from prctl(PR_SET_NO_NEW_PRIVS):
> >
> > PR_SET_NO_NEW_PRIVS takes effect immediately: it prevents gaining
> > privileges through set-user-ID, set-group-ID and file capabilities
> > starting with the very next execve(2).
> >
> > LANDLOCK_RESTRICT_SELF_NNP_ON_EXEC instead only sets the task's
> > no_new_privs attribute once the next execve(2) is guaranteed to succeed
> > (past its point of no return, in bprm_committing_creds), after any
> > privilege gain through set-user-ID, set-group-ID or file capabilities
> > has already taken place. The executed program then runs with
> > no_new_privs, so subsequent executions cannot gain privileges. A
> > failed execve(2) leaves no_new_privs untouched.
> >
> > Use cases:
> >
> > Enforcing a Landlock ruleset requires that the calling process either
> > already has no_new_privs set or possesses CAP_SYS_ADMIN. This series
> > does not grant any exception to this rule; for a caller that already
> > has no_new_privs set, the flag is effectively a no-op. It is therefore
> > mostly useful for CAP_SYS_ADMIN callers that need to execute programs
> > that legitimately escalate privileges (e.g. transition to another
> > user), while ensuring that further executions cannot gain privileges.
>
> That's a good point, but why not just set NNP for the whole process at
> landlock_restrict_self() call time? There is a partial answer below
> but...
>
Atomically with respect to the ruleset enforcement I presume?
I guess that makes this even easier... just run it at the end after all
possible errors in the landlock_restrict_self path.
In this case then, we must consider whether this would be sufficient to
serve as a substitute for NNP || cap_sys_admin check.
> >
> > Consider a sandbox launcher that depends on a set-user-ID helper, such
> > as launching applications through bubblewrap on distributions where
> > unprivileged user namespaces are disabled and bwrap is installed
> > set-user-ID root. The launcher cannot set PR_SET_NO_NEW_PRIVS before
> > the execution, as that would neuter the very helper it depends on:
> >
> > sandbox launcher (CAP_SYS_ADMIN)
> > |
> > | landlock_restrict_self(-1, LANDLOCK_RESTRICT_SELF_NNP_ON_EXEC)
> > V
> > /usr/bin/bwrap (set-user-ID root: honored for this execution;
> > | no_new_privs set once the execution is past its
> > | point of no return)
>
> This should only be allowed if the launcher has CAP_SYS_ADMIN, otherwise
> it would be like changing the state of a more privileged process.
> Anyway, even with this safeguard, this approach looks risky.
>
Yes, and it does have CAP_SYS_ADMIN in this example. It "feels" risky
though to mess with further escalations. But if you are CAP_SYS_ADMIN,
you can load kernel modules, potentially write to any memory, etc.
So that is less the argument I would make.
I think the main argument that hasn't been highlighted yet against *my
proposal* (I like to red team my own proposals) is that this whole
thing has nothing to do with Landlock. My feature doesn't touch Landlock
rulesets, or anything Landlock specific, so if this were implemented,
why not just make it a prctl? Why tie it to an LSM at all?
So I like your proposal better (atomic nnp with Landlock) because it's
actually relevant to Landlock rulesets.
The last remaining questions:
1. Shall the NNP || cap_sys_admin check remain in place for the NNP Landlock flag?
2. Should we allow enforcement with ruleset_fd being -1 (would basically
be equivalent to the prctl version of set no new privs)?
Justin
> > V
> > sandboxed application (runs with no_new_privs; cannot gain
> > privileges through any further execution, and
> > may enforce its own Landlock ruleset without
> > CAP_SYS_ADMIN)
> >
> > This flag also closes a gap for CAP_SYS_ADMIN callers of
> > landlock_restrict_self(2) itself. The no_new_privs/CAP_SYS_ADMIN
> > requirement exists to keep set-user-ID programs from running confused
> > inside a sandbox they do not expect. However, a privileged process
> > that enforces a domain without setting no_new_privs leaves that hole
> > open for all of its descendants: anything running in the domain may
> > still execute a set-user-ID binary, which then runs privileged under a
> > restricted view of the system. A privileged process that needs one
> > legitimate set-user-ID/set-group-ID transition currently has to choose
> > between breaking that transition (setting no_new_privs first) or
> > leaving the hole open for the lifetime of the domain.
> > LANDLOCK_RESTRICT_SELF_NNP_ON_EXEC allows the one intended transition
> > and then closes the hole.
>
> In a nutshell, not setting NNP might be risky, even when not strictly
> needed. We might want to update the Landlock doc with that.
>
Yes, I
> >
> > Design:
> >
> > This flag is implemented simply: a bit is stored in the Landlock
> > credential blob (struct landlock_cred_security) indicating whether the
> > next execution should set no_new_privs when it commits its new
> > credentials.
>
> Also, if we don't have the ON_EXEC part, there is no need to store
> anything in the cred.
>
> >
> > The bit is not coupled to any ruleset and, like
> > LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF, may be passed with no
> > ruleset (i.e. ruleset_fd = -1). It may also be combined with
> > LANDLOCK_RESTRICT_SELF_TSYNC to propagate the staged state to sibling
> > threads, each thread then setting no_new_privs at its own next
> > execve(2).
> >
> > The staged bit is inherited across fork(2) and persists in the
> > credentials until the next successful execution. To consume it,
> > Landlock handles the bprm_committing_creds hook, which runs while the
> > credentials of the new program are being committed: if the bit is set,
> > task_set_no_new_privs(current) is called and the bit is cleared.
> >
> > Again, this flag does not bypass the requirement to either have
> > CAP_SYS_ADMIN or no_new_privs already set to call
> > landlock_restrict_self(2).
> >
> > The Landlock ABI version is bumped to 11.
> >
> > Test coverage:
> >
> > The new nnp_on_exec fixture generates a shell script that reads the
> > NoNewPrivs value from /proc/self/status and exits with it. The
> > variants select the conditions under which the flag is tested (with
> > and without a ruleset, with and without CAP_SYS_ADMIN/no_new_privs
> > already set, with and without TSYNC, etc.), then compare no_new_privs
> > before the execution and in the executed script. The ruleset variants
> > also check that a ruleset passed along the flag is enforced
> > immediately, unlike the staged no_new_privs.
> >
> > Justin Suess (3):
> > landlock: Add LANDLOCK_RESTRICT_SELF_NNP_ON_EXEC
> > selftests/landlock: Test LANDLOCK_RESTRICT_SELF_NNP_ON_EXEC
> > landlock: Document LANDLOCK_RESTRICT_SELF_NNP_ON_EXEC
> >
> > Documentation/userspace-api/landlock.rst | 21 +-
> > include/uapi/linux/landlock.h | 36 ++-
> > security/landlock/cred.c | 22 ++
> > security/landlock/cred.h | 8 +
> > security/landlock/limits.h | 2 +-
> > security/landlock/syscalls.c | 54 +++-
> > tools/testing/selftests/landlock/base_test.c | 259 ++++++++++++++++++-
> > 7 files changed, 382 insertions(+), 20 deletions(-)
> >
> > --
> > 2.54.0
> >
> >
More information about the Linux-security-module-archive
mailing list