[PATCH 0/3] Implement LANDLOCK_RESTRICT_SELF_NNP_ON_EXEC

Mickaël Salaün mic at digikod.net
Fri Jul 31 21:28:37 UTC 2026


On Fri, Jul 10, 2026 at 09:56:25AM -0400, Justin Suess wrote:
> 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?

No because the Landlock NNP can only be stronger than the
always-one-thread NNP.

> 2. Should we allow enforcement with ruleset_fd being -1 (would basically
> be equivalent to the prctl version of set no new privs)?

I don't think so, see my other reply.

> 
> 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  

Something is missing but you sent a dedicated series, so let's continue
there.

> > > 
> > > 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