[PATCH 0/3] Implement LANDLOCK_RESTRICT_SELF_NNP_ON_EXEC

Justin Suess utilityemal77 at gmail.com
Wed Jul 8 13:39:24 UTC 2026


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.

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

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.

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