From nobody Fri Oct 2 07:45:28 2026 Received: from mail-yx1-f41.google.com (mail-yx1-f41.google.com [74.125.224.41]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E7EBB247291 for ; Mon, 3 Aug 2026 22:31:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.224.41 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785796278; cv=none; b=eeWY2JI7l1GUc7pvNksByvzntWJIwd684GO2GUcQ0E46YarbXFgRsE4MOOyYBOJFakdayeedwaIdl6+3RQU8HFG1HOp+W9kC63lmi3tHClCs2XI/CvCl5/FaGMb1v1y98aiFULOWLIifb5Tjx+qAY4DmAJJbu0iL6zM8s8fSEQQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785796278; c=relaxed/simple; bh=1+xNfeGufAfHIJ1KzuMNrtv9bAGT3aklmWVl5McL6ZU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=oPdSwCLi2JQ4OcHjEw+Dn8TWWhWvvTXTM0Hs2np8hvMKYwyLIsxbY82TIN81uTNtoWsxxw2foqN6nxGHK9TDXhUJCbSkkpvJlsdy5E8gURHmyudfH3xCrQE0MbcnQE1xzlgmjW4X6P724GIPXBzXDyZrfQxJZ4THQa1mtCSeLXY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=RUvSxo/l; arc=none smtp.client-ip=74.125.224.41 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="RUvSxo/l" Received: by mail-yx1-f41.google.com with SMTP id 956f58d0204a3-6688acd1a51so5699121d50.3 for ; Mon, 03 Aug 2026 15:31:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785796276; x=1786401076; darn=vger.kernel.org; h=content-transfer-encoding:content-type:mime-version:references :in-reply-to:message-id:date:subject:cc:to:from:from:to:cc:subject :date:message-id:reply-to:content-type; bh=afm3+c41sVqLp4fqwT0idATUxQKf9FXqPjm27Wnr+iM=; b=RUvSxo/lb82rK+L14opSMz5zml/R350Wh1/7lW1ZsndWxgyOpFweKTmi9lDaqQCLik Aanwe/AJvcIBGXI6dRjYzQSCzaQ21yNO4VDyTN7RCs4IBvCeyxyPh3XAJCro8Wt6tdyn QM6Plmnk5lxk+MkLctQrvRel1UeaBfIOSX5oVwpdSQNahhV3V4Qt1fvL4zjVyXzKqmyb 8elUOD3xacBufNXQkAuQUXAjb7W5VAbC0YH2H7blOZ/Vd7VtS5HSOAaWbt/nCMordLiQ 9Ob0oSC+tKZYjL6ytxEIoZdcic4f6TNUDR+0E3dNAhwVbx+dxh+b5VEW+6+6W72ba8vu DNdQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785796276; x=1786401076; h=content-transfer-encoding:content-type:mime-version:references :in-reply-to:message-id:date:subject:cc:to:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=afm3+c41sVqLp4fqwT0idATUxQKf9FXqPjm27Wnr+iM=; b=S21JifScvKABwGwoeQSldbQz+ELsacwV+plggm2Fnu5XcnVQi5Mw+itySCS/fnLPk5 shZ253OSpNGMHrqtRKVlIVKlt2SAWw2ZvaUJdJXOCka8RdYRwUpft0N/E9fsCuGPSj1q hlyyatMdHY9Ho6AViFVRtSSyV6qMX7SXe8vtxDwvqMIWnk5ATE1TU5F+KCzdiXduv7Wn oF89TFEOZNlbt1LiJI0KysdLdLxLkK/MpfnS6eDVkRWbz2MHTYsKEzEorAKIR268noiV lf4Cdi2sH7F4ylrepRzR4DAkUefYfUfXWJdwjesduBjfnElDBIHOZZ33Ana4gWsgtpT6 SHcw== X-Gm-Message-State: AOJu0YxRtH2B0dzCURADhc5xBgcvsEk7wh1Zd2MgWFGn4WtRIZ/T2sO2 TnBTrKyOdEC5p+e3mJSeLEtPI5cY8/Tm9wXIgzjYkAQFD5voCkKBjmq8 X-Gm-Gg: AR+sD10iM3EwO/OfvYLxWc5PpAu4rkDez50aiymY4eQla4M31qLkjbi5awT45LWljd8 nfjKdUkhlwB5ZI0YLAI3gHrZwdYDVX/XLyFtXbPf/p0IXMrjdf+DfM0NluM7zj/lpFLqB5AhtGC rZRYPFEOOlTegIUXCOcMIkUVwpCKHlS3RSKybYwPrFIyKxaN7YDdIiBi9uNufGRDujQDL8vj1ej 1vzrvon4y6Kv0ENNZE7mtQ7L9TVk8LkZH+8V2FpW1MgrNziuVmadIZtb1wZiH20Yg2lI2cg5YZ3 sk/lmZvPax2MGWupsKYEegRvi7sQCmZLvrrAVRaA7mrVv9MHU5BvX/Kzp0RZqHzd48YKEjCAB8c mIN0WFs+SCIa/9i0MEPcHeSTdouijXFYlqk3Yk37yZ/WAQVUqSpA+thTqITjdO1suouwcjn6AB5 YA5/7x08qNldw5mMo2Om/ZCZNEZnqvn5nWCarlMBgJmhh0GBi8yBcnvjApWkMDqpZZOLPYEBL6+ +WIJbmrgg1jCS1+lvilq3I= X-Received: by 2002:a05:690e:810:10b0:668:9b6a:652d with SMTP id 956f58d0204a3-6694efe0eb1mr11750066d50.3.1785796275754; Mon, 03 Aug 2026 15:31:15 -0700 (PDT) Received: from zenbox ([2600:1700:18fb:6011:6253:b407:801c:a745]) by smtp.gmail.com with ESMTPSA id 956f58d0204a3-6694903782fsm6420774d50.21.2026.08.03.15.31.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 03 Aug 2026 15:31:15 -0700 (PDT) From: Justin Suess To: gnoack3000@gmail.com, mic@digikod.net Cc: linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org, Justin Suess Subject: [PATCH v3 1/4] landlock: Add LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS Date: Mon, 3 Aug 2026 18:31:05 -0400 Message-ID: <20260803223109.707353-2-utilityemal77@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260803223109.707353-1-utilityemal77@gmail.com> References: <20260803223109.707353-1-utilityemal77@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Add a landlock_restrict_self(2) flag to set the no_new_privs attribute of the calling thread only after enforcement of the ruleset: no_new_privs is set if and only if the call succeeds. This removes the need for a prior prctl(2) PR_SET_NO_NEW_PRIVS call and guarantees that a failed enforcement leaves the attribute unchanged. Because no_new_privs is set by the call itself, the no_new_privs / CAP_SYS_ADMIN requirement of landlock_restrict_self(2) is fulfilled by construction, and the related EPERM check is skipped. As a consequence, an unprivileged caller passing unknown flags along with this flag gets EINVAL instead of EPERM. Unlike LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF, this flag always requires a valid ruleset: with a ruleset_fd of -1, such a call would be nothing more than a Landlock-flavored prctl(2) PR_SET_NO_NEW_PRIVS, and there is no valid use case for setting no_new_privs (possibly with LANDLOCK_RESTRICT_SELF_TSYNC) without also enforcing Landlock restrictions. Rejecting these calls also keeps the option of giving them a meaning later. The attribute is only set past the last point of failure, just before committing the new credentials. When combined with LANDLOCK_RESTRICT_SELF_TSYNC, no_new_privs is set on the sibling threads as well, in their commit phase, with the same ordering. Bump the Landlock ABI version to 11. Cc: Micka=C3=ABl Sala=C3=BCn Signed-off-by: Justin Suess --- Notes: v2->v3: - Reword "atomically" to the ordering guarantee (no_new_privs is only s= et once enforcement succeeded) in the commit message and both kdocs - Explain in the commit message why the flag requires a valid ruleset include/uapi/linux/landlock.h | 13 +++++++++++++ security/landlock/limits.h | 2 +- security/landlock/syscalls.c | 28 +++++++++++++++++++++------- security/landlock/tsync.c | 8 ++++++-- security/landlock/tsync.h | 4 +++- 5 files changed, 44 insertions(+), 11 deletions(-) diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h index 27ae3f39cafb..11bf600698f0 100644 --- a/include/uapi/linux/landlock.h +++ b/include/uapi/linux/landlock.h @@ -191,12 +191,25 @@ struct landlock_ruleset_attr { * * If the calling thread is running with no_new_privs, this operation * enables no_new_privs on the sibling threads as well. + * + * The following flag ties the no_new_privs attribute to the ruleset + * enforcement: + * + * %LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS + * Sets the no_new_privs attribute of the calling thread only once the + * enforcement of the ruleset succeeded: no_new_privs is set if and on= ly + * if sys_landlock_restrict_self() succeeds. This removes the need fo= r a + * prior :manpage:`prctl(2)` ``PR_SET_NO_NEW_PRIVS`` call, and with it= the + * %CAP_SYS_ADMIN requirement. This flag requires a ruleset. When + * combined with %LANDLOCK_RESTRICT_SELF_TSYNC, no_new_privs is set on= the + * sibling threads as well. */ /* clang-format off */ #define LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF (1U << 0) #define LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON (1U << 1) #define LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF (1U << 2) #define LANDLOCK_RESTRICT_SELF_TSYNC (1U << 3) +#define LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS (1U << 4) /* clang-format on */ =20 /** diff --git a/security/landlock/limits.h b/security/landlock/limits.h index 08d5f2f6d321..1a7c5fb8f6fd 100644 --- a/security/landlock/limits.h +++ b/security/landlock/limits.h @@ -34,7 +34,7 @@ #define LANDLOCK_NUM_ACCESS_MAX \ MAX(MAX(LANDLOCK_NUM_ACCESS_FS, LANDLOCK_NUM_ACCESS_NET), LANDLOCK_NUM_SC= OPE) =20 -#define LANDLOCK_LAST_RESTRICT_SELF LANDLOCK_RESTRICT_SELF_TSYNC +#define LANDLOCK_LAST_RESTRICT_SELF LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS #define LANDLOCK_MASK_RESTRICT_SELF ((LANDLOCK_LAST_RESTRICT_SELF << 1) - = 1) =20 /* clang-format on */ diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c index 36b02892c62f..e97f944109f9 100644 --- a/security/landlock/syscalls.c +++ b/security/landlock/syscalls.c @@ -169,7 +169,7 @@ static const struct file_operations ruleset_fops =3D { * If the change involves a fix that requires userspace awareness, also up= date * the errata documentation in Documentation/userspace-api/landlock.rst . */ -const int landlock_abi_version =3D 10; +const int landlock_abi_version =3D 11; =20 /** * sys_landlock_create_ruleset - Create a new ruleset @@ -502,21 +502,28 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset= _fd, * - %LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON * - %LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF * - %LANDLOCK_RESTRICT_SELF_TSYNC + * - %LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS * * This system call enforces a Landlock ruleset on the current thread. * Enforcing a ruleset requires that the task has %CAP_SYS_ADMIN in its * namespace or is running with no_new_privs. This avoids scenarios where * unprivileged tasks can affect the behavior of privileged children. * + * With %LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS, the no_new_privs attribute o= f the + * calling thread is set only once the enforcement of the ruleset succeede= d, + * which fulfills the above requirement: no_new_privs is set if and only i= f the + * call succeeds. + * * Return: 0 on success, or -errno on failure. Possible returned errors a= re: * * - %EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot= time; * - %EINVAL: @flags contains an unknown bit. * - %EBADF: @ruleset_fd is not a file descriptor for the current thread; * - %EBADFD: @ruleset_fd is not a ruleset file descriptor; - * - %EPERM: @ruleset_fd has no read access to the underlying ruleset, or = the - * current thread is not running with no_new_privs, or it doesn't have - * %CAP_SYS_ADMIN in its namespace. + * - %EPERM: @ruleset_fd has no read access to the underlying ruleset, or + * %LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS is not set while the current thr= ead + * is not running with no_new_privs and doesn't have %CAP_SYS_ADMIN in i= ts + * namespace. * - %E2BIG: The maximum number of stacked rulesets is reached for the cur= rent * thread. * @@ -529,6 +536,8 @@ SYSCALL_DEFINE2(landlock_restrict_self, const int, rule= set_fd, const __u32, struct landlock_ruleset *ruleset __free(landlock_put_ruleset) =3D NULL; struct cred *new_cred; struct landlock_cred_security *new_llcred; + const bool set_no_new_privs =3D + !!(flags & LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS); bool __maybe_unused log_same_exec, log_new_exec, log_subdomains, prev_log_subdomains; =20 @@ -537,9 +546,10 @@ SYSCALL_DEFINE2(landlock_restrict_self, const int, rul= eset_fd, const __u32, =20 /* * Similar checks as for seccomp(2), except that an -EPERM may be - * returned. + * returned. LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS fulfills this + * requirement. */ - if (!task_no_new_privs(current) && + if (!set_no_new_privs && !task_no_new_privs(current) && !ns_capable_noaudit(current_user_ns(), CAP_SYS_ADMIN)) return -EPERM; =20 @@ -620,12 +630,16 @@ SYSCALL_DEFINE2(landlock_restrict_self, const int, ru= leset_fd, const __u32, =20 if (flags & LANDLOCK_RESTRICT_SELF_TSYNC) { const int err =3D landlock_restrict_sibling_threads( - current_cred(), new_cred); + current_cred(), new_cred, flags); if (err) { abort_creds(new_cred); return err; } } =20 + /* Sets no_new_privs past the last point of failure. */ + if (set_no_new_privs) + task_set_no_new_privs(current); + return commit_creds(new_cred); } diff --git a/security/landlock/tsync.c b/security/landlock/tsync.c index c5730bbd9ed3..0b71e158c3f5 100644 --- a/security/landlock/tsync.c +++ b/security/landlock/tsync.c @@ -17,6 +17,7 @@ #include #include #include +#include =20 #include "cred.h" #include "tsync.h" @@ -466,7 +467,8 @@ static void cancel_tsync_works(const struct tsync_works= *works, * restrict_sibling_threads - enables a Landlock policy for all sibling th= reads */ int landlock_restrict_sibling_threads(const struct cred *old_cred, - const struct cred *new_cred) + const struct cred *new_cred, + const u32 restrict_flags) { int err; struct tsync_shared_context shared_ctx; @@ -481,7 +483,9 @@ int landlock_restrict_sibling_threads(const struct cred= *old_cred, init_completion(&shared_ctx.all_finished); shared_ctx.old_cred =3D old_cred; shared_ctx.new_cred =3D new_cred; - shared_ctx.set_no_new_privs =3D task_no_new_privs(current); + shared_ctx.set_no_new_privs =3D + (restrict_flags & LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS) || + task_no_new_privs(current); =20 /* * Serialize concurrent TSYNC operations to prevent deadlocks when diff --git a/security/landlock/tsync.h b/security/landlock/tsync.h index ef86bb61c2f6..2ae4f938ca00 100644 --- a/security/landlock/tsync.h +++ b/security/landlock/tsync.h @@ -9,8 +9,10 @@ #define _SECURITY_LANDLOCK_TSYNC_H =20 #include +#include =20 int landlock_restrict_sibling_threads(const struct cred *old_cred, - const struct cred *new_cred); + const struct cred *new_cred, + u32 restrict_flags); =20 #endif /* _SECURITY_LANDLOCK_TSYNC_H */ --=20 2.54.0 From nobody Fri Oct 2 07:45:28 2026 Received: from mail-yx1-f49.google.com (mail-yx1-f49.google.com [74.125.224.49]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8378234D926 for ; Mon, 3 Aug 2026 22:31:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.224.49 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785796280; cv=none; b=vDteUHg2VRTOyVm7aFiWKZXcfBx/2vmp1Kaiw22UYIcZP+OqXh4xtR/ItKq+Zap+mKhgl/QiVjCE2VY8b6ZGKl7/wUSMbBDZjEt1mQyytAKWiPRVO18g1mldpOn+kPPO999FxKGZyYwurKRtiETbEyA0aIc1BBEkrvmoCBMfIy8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785796280; c=relaxed/simple; bh=UxdpIGw9oC6cf066AIC44wV6d6UfEMHFOIUEqqOu77U=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=GBI/DSnqSLZ+C38QZZHOupwNXhoVc77etu08R8hra1xhBlnczkvQmc/IP7E2ljNnG4XhA5YMQSLRNgUKfoi+GpbJQM3jMC8bfW4XA1JvJRdnJxUhb/B+i8k+1tBAoHsV5tFhzyvcwOyNhMFdU1WErpvncxdq5Ui+Z5hX6DD7qa0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=j7rm7zbb; arc=none smtp.client-ip=74.125.224.49 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="j7rm7zbb" Received: by mail-yx1-f49.google.com with SMTP id 956f58d0204a3-6689f36ae56so6167547d50.3 for ; Mon, 03 Aug 2026 15:31:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785796277; x=1786401077; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=TaCAksqvz2wcTu4zxzoqEsp14HvgIozMCnF+hHCjPoc=; b=j7rm7zbbPxYkyV2xndjM3PWtx59swOjSL8Vj1JUkTbgnFnAEr8ngh2R2a/YgCpqN9e HCJq3Gld21YP9AdUaGazbQWbQWM6Uq7WTdHNNXnli+As6UArGiqg63DYYCLfY3FRgpGT 0/2autm5aL47beOF3hULE56zn5mFmYrDHBL391YCGIq50M9WxMsjCYkf78Y3RKZxjtOo hNT9nYTBqpZIZEJ3+8fTNrLzkhnVIYxvyFE3mja2YxqpdpL0/GgLe9SG5YYA9I+dGFA0 Zw/nCmVBl/NhoJmTcy2ouj6Qy2xbimjUj4qc+Tu2XF9Q9jqYgswsdasrgmCW5TATeQvf MTsg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785796277; x=1786401077; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=TaCAksqvz2wcTu4zxzoqEsp14HvgIozMCnF+hHCjPoc=; b=oJZqqWIakC9uu5L/jVlknfmuXoZuuVN/Son1HgcVET3NE5k4HvGZTzJI73ehI5bSe5 3prTG0L3glLYnEW0pptqWaulSkHKwuCDHOaJYkGI+SAkOrqXRERF8dv0jLWcuBW+TbRX JIJF5DAc6gPycZzh8AAOhbWKdRDDwlCZ/3VHZdFeMUZCGtiH+mcBxsAFCdZ/AB1qO4hk +mzs9NzpwDPJICZcgYeEMSvdZolnKaq103HvtImsM9/NCZWM7Ugk21I6C+BFPvw11g3e D8N5HmiWgdlAODCfxwwsn10Yha18suxKoQxO3IrxdhV3K1wdlbdIuq3oTVwkcaZi71US 1t5w== X-Gm-Message-State: AOJu0YzlWLlFCrq0RaafOGf/oOvF8pgg+HjqrmSfgLbfBQjl8WE6T4ET GJ3bTKMilhF6ZffQJRop41nJ4s3prEqqSoCZbBu+Kbq4YDM0hJeisTpH X-Gm-Gg: AR+sD13acQxUHV1ZaPQGJeLG/33yz5h+Jwpsjrcd4w2lAmqOFEG5TQNOyWoIavkev1X fcfgLgRkml1jwLoy8ebYPdvtQdwNMrlARXmHHutDimVUQvgdP22MTuWD89jJpSPy77dUAGKYbaz tNt/i9O7sG3wS2ILssA8HBXFperT68wFRWS8455h7i6dPwEdeA0WutibH8nu9AZyETSK80hWSzI 2ygcSOqp1pwI2bVyLxRgGauF3HNtJX+CeEBwf99QFvrYMWT97AWz2rELLCcQjM/gsZxLuQ1N5Um SWa66pGljyT/4B90I8fu3WqucJ1sUVOBdjG3lXxr2AX0hE5EkAx6ajTmu2Ws6sIO3DHuJWPjtaE hHVicfvKvxnmtifv6oraL5/pN/5xL3y9r23UMWh7vTjeQWeCS2GV1c1vShQBC6ecf1HQC15OeQ9 Pu9I0XHjo+RYCTkXbQOTPLPrheFMWLQaHLxzDLHK4krt5GWfqEGajl6b6O7o3frbCHam27V77Qi VjMOi+v58xGnRnOECEhUf0= X-Received: by 2002:a05:690e:155b:10b0:667:e04a:8a3f with SMTP id 956f58d0204a3-6694efcc215mr11327912d50.5.1785796277266; Mon, 03 Aug 2026 15:31:17 -0700 (PDT) Received: from zenbox ([2600:1700:18fb:6011:6253:b407:801c:a745]) by smtp.gmail.com with ESMTPSA id 956f58d0204a3-6694903782fsm6420774d50.21.2026.08.03.15.31.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 03 Aug 2026 15:31:17 -0700 (PDT) From: Justin Suess To: gnoack3000@gmail.com, mic@digikod.net Cc: linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org, Justin Suess Subject: [PATCH v3 2/4] selftests/landlock: Test LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS Date: Mon, 3 Aug 2026 18:31:06 -0400 Message-ID: <20260803223109.707353-3-utilityemal77@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260803223109.707353-1-utilityemal77@gmail.com> References: <20260803223109.707353-1-utilityemal77@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Check that a successful landlock_restrict_self(2) call with LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS sets no_new_privs without a prior prctl(2) call nor CAP_SYS_ADMIN, that a failed call from both an invalid ruleset and hitting the layer maximum leaves the attribute unchanged, and that LANDLOCK_RESTRICT_SELF_TSYNC extends it to sibling threads. Also check that this flag requires a ruleset, and update the restrict_self_checks_ordering EPERM checks since this flag is now checked before the flags validity. Finally, rename restrict_self_fd_logging_flags to restrict_self_fd_flags, and restrict_self_logging_flags to restrict_self_flags to indicate that non-logging flags are now tested. Update the ABI version and last-flag checks accordingly. Signed-off-by: Justin Suess --- Notes: v2->v3: - Run clang-format - Add max-layers tests (base_test and tsync_test) checking E2BIG and that a failed call leaves no_new_privs unchanged - Mention the test renames in the commit message - Match comment style of surrounding tests tools/testing/selftests/landlock/base_test.c | 99 ++++++++++++++++++- tools/testing/selftests/landlock/tsync_test.c | 72 ++++++++++++++ 2 files changed, 166 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/landlock/base_test.c b/tools/testing/s= elftests/landlock/base_test.c index cbd3c1669951..c8ed165a32ed 100644 --- a/tools/testing/selftests/landlock/base_test.c +++ b/tools/testing/selftests/landlock/base_test.c @@ -76,7 +76,7 @@ TEST(abi_version) const struct landlock_ruleset_attr ruleset_attr =3D { .handled_access_fs =3D LANDLOCK_ACCESS_FS_READ_FILE, }; - ASSERT_EQ(10, landlock_create_ruleset(NULL, 0, + ASSERT_EQ(11, landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION)); =20 ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0, @@ -255,8 +255,15 @@ TEST(restrict_self_checks_ordering) =20 /* Checks unprivileged enforcement without no_new_privs. */ drop_caps(_metadata); - ASSERT_EQ(-1, landlock_restrict_self(-1, -1)); + ASSERT_EQ(-1, landlock_restrict_self( + -1, ~LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS)); ASSERT_EQ(EPERM, errno); + /* + * LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS fulfills the no_new_privs / + * CAP_SYS_ADMIN requirement, so the invalid flags are checked first. + */ + ASSERT_EQ(-1, landlock_restrict_self(-1, -1)); + ASSERT_EQ(EINVAL, errno); ASSERT_EQ(-1, landlock_restrict_self(-1, 0)); ASSERT_EQ(EPERM, errno); ASSERT_EQ(-1, landlock_restrict_self(ruleset_fd, 0)); @@ -277,6 +284,41 @@ TEST(restrict_self_checks_ordering) ASSERT_EQ(0, close(ruleset_fd)); } =20 +TEST(restrict_self_max_layers) +{ + const struct landlock_ruleset_attr ruleset_attr =3D { + .handled_access_fs =3D LANDLOCK_ACCESS_FS_EXECUTE, + }; + struct landlock_path_beneath_attr path_beneath_attr =3D { + .allowed_access =3D LANDLOCK_ACCESS_FS_EXECUTE, + .parent_fd =3D -1, + }; + const int ruleset_fd =3D + landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); + ASSERT_LE(0, ruleset_fd); + + path_beneath_attr.parent_fd =3D + open("/tmp", O_PATH | O_NOFOLLOW | O_DIRECTORY | O_CLOEXEC); + ASSERT_LE(0, path_beneath_attr.parent_fd); + ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, + &path_beneath_attr, 0)); + ASSERT_EQ(0, close(path_beneath_attr.parent_fd)); + + /* Enforces the maximum number of allowed layers. */ + for (int i =3D 0; i < LANDLOCK_MAX_NUM_LAYERS; i++) + ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0)); + + /* Enforces one too many rulesets. */ + drop_caps(_metadata); + ASSERT_EQ(-1, landlock_restrict_self( + ruleset_fd, LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS)); + ASSERT_EQ(E2BIG, errno); + + /* Checks that the failed call did not set no_new_privs. */ + ASSERT_EQ(0, prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0)); + ASSERT_EQ(0, close(ruleset_fd)); +} + TEST(restrict_self_fd) { int fd; @@ -288,7 +330,7 @@ TEST(restrict_self_fd) EXPECT_EQ(EBADFD, errno); } =20 -TEST(restrict_self_fd_logging_flags) +TEST(restrict_self_fd_flags) { int fd; =20 @@ -302,11 +344,16 @@ TEST(restrict_self_fd_logging_flags) EXPECT_EQ(-1, landlock_restrict_self( fd, LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF)); EXPECT_EQ(EBADFD, errno); + + /* LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS requires a ruleset FD. */ + EXPECT_EQ(-1, landlock_restrict_self( + fd, LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS)); + EXPECT_EQ(EBADFD, errno); } =20 -TEST(restrict_self_logging_flags) +TEST(restrict_self_flags) { - const __u32 last_flag =3D LANDLOCK_RESTRICT_SELF_TSYNC; + const __u32 last_flag =3D LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS; =20 /* Tests invalid flag combinations. */ =20 @@ -349,6 +396,17 @@ TEST(restrict_self_logging_flags) LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON)); EXPECT_EQ(EBADF, errno); =20 + /* LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS requires a ruleset FD. */ + + EXPECT_EQ(-1, landlock_restrict_self( + -1, LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS)); + EXPECT_EQ(EBADF, errno); + + EXPECT_EQ(-1, landlock_restrict_self( + -1, LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF | + LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS)); + EXPECT_EQ(EBADF, errno); + /* Tests with an invalid ruleset_fd. */ =20 EXPECT_EQ(-1, landlock_restrict_self( @@ -359,6 +417,37 @@ TEST(restrict_self_logging_flags) -1, LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF)); } =20 +TEST(restrict_self_no_new_privs) +{ + const struct landlock_ruleset_attr ruleset_attr =3D { + .handled_access_fs =3D LANDLOCK_ACCESS_FS_READ_FILE, + }; + const int ruleset_fd =3D + landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); + + ASSERT_LE(0, ruleset_fd); + + /* + * The calling thread does not need CAP_SYS_ADMIN nor an explicit + * prctl(2) PR_SET_NO_NEW_PRIVS call. + */ + drop_caps(_metadata); + ASSERT_EQ(0, prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0)); + + /* Checks that a failed call does not set no_new_privs. */ + EXPECT_EQ(-1, landlock_restrict_self( + -1, LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS)); + EXPECT_EQ(EBADF, errno); + EXPECT_EQ(0, prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0)); + + /* Checks that a successful call sets no_new_privs. */ + ASSERT_EQ(0, landlock_restrict_self( + ruleset_fd, LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS)); + EXPECT_EQ(1, prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0)); + + EXPECT_EQ(0, close(ruleset_fd)); +} + TEST(ruleset_fd_io) { struct landlock_ruleset_attr ruleset_attr =3D { diff --git a/tools/testing/selftests/landlock/tsync_test.c b/tools/testing/= selftests/landlock/tsync_test.c index 9cf1491bbaaf..afa4a8222248 100644 --- a/tools/testing/selftests/landlock/tsync_test.c +++ b/tools/testing/selftests/landlock/tsync_test.c @@ -90,6 +90,78 @@ TEST(multi_threaded_success) EXPECT_EQ(0, close(ruleset_fd)); } =20 +TEST(multi_threaded_no_new_privs) +{ + pthread_t t1, t2; + bool no_new_privs1, no_new_privs2; + const int ruleset_fd =3D create_ruleset(_metadata); + + disable_caps(_metadata); + + ASSERT_EQ(0, pthread_create(&t1, NULL, idle, &no_new_privs1)); + ASSERT_EQ(0, pthread_create(&t2, NULL, idle, &no_new_privs2)); + + /* No prior prctl(2) PR_SET_NO_NEW_PRIVS call. */ + ASSERT_EQ(0, prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0)); + + EXPECT_EQ(0, landlock_restrict_self( + ruleset_fd, + LANDLOCK_RESTRICT_SELF_TSYNC | + LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS)); + + EXPECT_EQ(1, prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0)); + + ASSERT_EQ(0, pthread_cancel(t1)); + ASSERT_EQ(0, pthread_cancel(t2)); + ASSERT_EQ(0, pthread_join(t1, NULL)); + ASSERT_EQ(0, pthread_join(t2, NULL)); + + /* The no_new_privs flag was enabled on all threads. */ + EXPECT_TRUE(no_new_privs1); + EXPECT_TRUE(no_new_privs2); + + EXPECT_EQ(0, close(ruleset_fd)); +} + +TEST(multi_threaded_no_new_privs_max_layers) +{ + pthread_t t1, t2; + bool no_new_privs1, no_new_privs2; + const int ruleset_fd =3D create_ruleset(_metadata); + + /* Enforces the maximum number of allowed layers. */ + for (int i =3D 0; i < LANDLOCK_MAX_NUM_LAYERS; i++) + ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0)); + + ASSERT_EQ(0, pthread_create(&t1, NULL, idle, &no_new_privs1)); + ASSERT_EQ(0, pthread_create(&t2, NULL, idle, &no_new_privs2)); + + disable_caps(_metadata); + + /* No prior prctl(2) PR_SET_NO_NEW_PRIVS call. */ + ASSERT_EQ(0, prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0)); + + ASSERT_EQ(-1, + landlock_restrict_self(ruleset_fd, + LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS | + LANDLOCK_RESTRICT_SELF_TSYNC)); + ASSERT_EQ(E2BIG, errno); + + /* Checks that the failed call did not set no_new_privs. */ + ASSERT_EQ(0, prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0)); + + ASSERT_EQ(0, pthread_cancel(t1)); + ASSERT_EQ(0, pthread_cancel(t2)); + ASSERT_EQ(0, pthread_join(t1, NULL)); + ASSERT_EQ(0, pthread_join(t2, NULL)); + + /* The no_new_privs flag was not enabled on any thread. */ + EXPECT_FALSE(no_new_privs1); + EXPECT_FALSE(no_new_privs2); + + ASSERT_EQ(0, close(ruleset_fd)); +} + TEST(multi_threaded_success_despite_diverging_domains) { pthread_t t1, t2; --=20 2.54.0 From nobody Fri Oct 2 07:45:28 2026 Received: from mail-yx1-f50.google.com (mail-yx1-f50.google.com [74.125.224.50]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CB6FA3515D8 for ; Mon, 3 Aug 2026 22:31:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.224.50 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785796281; cv=none; b=jhYZXoZA5jWhBr3s2nlEjVYmovKqee/qkZe4nLkReDgQlIpIJXWhqr5d2yrpK9FtsYEDECXpEJZrlcWdHLAzF7Wz8cuGQ2J8n3cy9dbQxJA+J4LYazz7TcAlNFBElY5zuxyX033Nv7IKQ8isp9bR1X0wsZVndKgOEkaBgPO08aM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785796281; c=relaxed/simple; bh=KhNmQPKvwOQ+mniwSSLAoC9sjghgdGDHBXnf9xXMVX8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=qKGXtkpHuuOA1SmjYai+X8VRzJSEVDlCwNPVCfzYOz9NoC+Bmh9UxvQ2tKfh2WLvAZUk1pVYLLpi9FQUn3B8uM3imgku22T5tB6NhOlp/xmE2BeWGq0dkj9970crktZoi1VWQT84CI75HpppbZ6Ikz2Z58vr5LxMIA7REBGrdUw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=PB3oDy0Q; arc=none smtp.client-ip=74.125.224.50 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="PB3oDy0Q" Received: by mail-yx1-f50.google.com with SMTP id 956f58d0204a3-667971437d6so5437368d50.2 for ; Mon, 03 Aug 2026 15:31:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785796279; x=1786401079; darn=vger.kernel.org; h=content-transfer-encoding:content-type:mime-version:references :in-reply-to:message-id:date:subject:cc:to:from:from:to:cc:subject :date:message-id:reply-to:content-type; bh=e5s0xFKMZYCXt2wJGBakETq4pnBg/XsA2VPADzOv0sc=; b=PB3oDy0QD988WiidHp9mL8hfvIqXe0bNmwrEGjcX9LjZcnypQzM2wSiBT/XxSBC8OK 2fAEnRAsjS9ddBFIf2MvuixQ9dNZ4iMF+WBCSGkktSIwuzupLy4ohH/CN3lfA3RwK+Cn u3Ofmoaonix7+RZkvEHFG/YU02C6LBBDXQqKp5TuCEBG4cpU/lKfHXg9kQ9rQhxeW/PB DRf4BLyTY803Hlcr5Veu6rc9eqOibMLIGHl8/UdwhXdkY0GrABPoqyuDrBIO+0zFLWoD z53Wy4n/uBQxGNbBECVmK2zeizenPOhKWdPZ8oO6C2mYnUDUGugQDT6pTU8+1vxhpQSj s5PQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785796279; x=1786401079; h=content-transfer-encoding:content-type:mime-version:references :in-reply-to:message-id:date:subject:cc:to:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=e5s0xFKMZYCXt2wJGBakETq4pnBg/XsA2VPADzOv0sc=; b=UUfKrmfh0e8w5PHwL7/TXxfT51xpUV85bzAR3zmR2PQUHzBWyvf/8gzxVlbqpxhuSq mgFhgiJuduz7VxEjyMaCZ6ENbaxEgXik+bVAOWILIZi1SYSyf83ujsrkci345VoCvzKp Y9gkX3pjocbdF0WiKLXirm7vkufNmFB8zvOam2MgxKLyz7KX04FgbdDzlR4E0C5J/RAZ Mpl2s8essdtgypSESYUSdcBxB98q1Zb9NHBMQxQq1DL0/iEKp5ls2fizGOKYyollRolp OwWqK/BP1rN2w2sS9JZZ//7il2x7+oS7WcmaxD8hTLSlxADmnss4LiP/1MUg+2C/HYKW 36TA== X-Gm-Message-State: AOJu0Yx4U+xE11iJmGxuWPa34AmQYZuOvQRRQuz3CocPPRc2V4cO+JfX e+SJ2Sf11m02USq7vWmzagtH8HqG/oKd89GbfCTP5bVYprsIad8NPem3 X-Gm-Gg: AR+sD13Zy12NFBMBNR2zWkTilfBjbnhLRRXRKgvkQ3TKJ/f0TLBCC7E5vtSxxdo/moa sgnFZ71IglwvY6wrPFcK5nS89OKyFx5XhqhzlxZXDTf09QgMN/liMHy4DEkFdGjz1LtmsiHDPnV 1iTNkywe7qhPiVeSTT4RRNBkf/qUqjEbHm17xc1CJp9TAkrtKqSiplejW+WhYRSdymgQc6JVUDj lEcUhkYWwPFPrsMy6DCUuGdcAtVgBoySVy9dXDkRAOuslpB0xcdP1InW30LTCyNMj3Q/V7bmtWp i65Da8IA03EWhuj3+OSHYOT3EtQ2KHbHInFROdOzjjnYSWgQ1nLznB7eKqRxBLhBkcaqD36RG7u PdSoXAV3kqtE+2n6BkmXbqNxoYqghpKIfDSBKnNuYT9i9aN39k2ciyOLjLlzhic2IDfg/SlEQIF x3A1XFPw3ywHvJrzibbViuMBfCXHHo/vp2k6grGu3Mx+pABdQwd/kv2Isi4HrC/APsqjPze4vqg JsrxUGNKY3sh1T20yfVRtM= X-Received: by 2002:a53:b4c9:0:b0:669:483e:c36a with SMTP id 956f58d0204a3-6694f17092amr10482172d50.34.1785796278794; Mon, 03 Aug 2026 15:31:18 -0700 (PDT) Received: from zenbox ([2600:1700:18fb:6011:6253:b407:801c:a745]) by smtp.gmail.com with ESMTPSA id 956f58d0204a3-6694903782fsm6420774d50.21.2026.08.03.15.31.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 03 Aug 2026 15:31:18 -0700 (PDT) From: Justin Suess To: gnoack3000@gmail.com, mic@digikod.net Cc: linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org, Justin Suess Subject: [PATCH v3 3/4] landlock: Document LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS Date: Mon, 3 Aug 2026 18:31:07 -0400 Message-ID: <20260803223109.707353-4-utilityemal77@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260803223109.707353-1-utilityemal77@gmail.com> References: <20260803223109.707353-1-utilityemal77@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Document setting no_new_privs with ruleset enforcement, following the same compatibility section style as previous ABI additions. Include a section explaining the tradeoffs of setting no_new_privs through any means for privileged users of Landlock. Signed-off-by: Justin Suess --- Notes: v2->v3: - Update the tutorial: restrict_flags per ABI version and prctl call skipped when LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS is used - Drop "Atomic" from the section title; describe the ordering instead - Explain that not setting no_new_privs is risky even when not required - Fix ABI 8/9 switch coverage (case 8 ... 10) and indentation Documentation/userspace-api/landlock.rst | 47 +++++++++++++++++++++--- 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/Documentation/userspace-api/landlock.rst b/Documentation/users= pace-api/landlock.rst index 5085822d8930..0e4a73fd5ea4 100644 --- a/Documentation/userspace-api/landlock.rst +++ b/Documentation/userspace-api/landlock.rst @@ -8,7 +8,7 @@ Landlock: unprivileged access control =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 :Author: Micka=C3=ABl Sala=C3=BCn -:Date: July 2026 +:Date: August 2026 =20 The goal of Landlock is to enable restriction of ambient rights (e.g. glob= al filesystem or network access) for a set of processes. Because Landlock @@ -250,7 +250,8 @@ similar backwards compatibility check is needed for the= restrict flags =20 __u32 restrict_flags =3D LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON | - LANDLOCK_RESTRICT_SELF_TSYNC; + LANDLOCK_RESTRICT_SELF_TSYNC | + LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS; switch (abi) { case 1 ... 6: /* Removes logging flags for ABI < 7 */ @@ -269,16 +270,36 @@ similar backwards compatibility check is needed for t= he restrict flags * children (and not for all threads, including parents and siblin= gs). */ restrict_flags &=3D ~LANDLOCK_RESTRICT_SELF_TSYNC; + __attribute__((fallthrough)); + case 8 ... 10: + /* Removes no new privs flag for ABI < 11 */ + restrict_flags &=3D ~LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS; } =20 The next step is to restrict the current thread from gaining more privileg= es -(e.g. through a SUID binary). We now have a ruleset with the first rule -allowing read and execute access to ``/usr`` while denying all other handl= ed -accesses for the filesystem, and two more rules allowing DNS queries. +(e.g. through a SUID binary). For unprivileged processes, setting the +no_new_privs attribute is required by Landlock. + +Processes with ``CAP_SYS_ADMIN`` in their namespace can enforce a ruleset +without it, but not setting no_new_privs is risky even when it is not +required: sandboxed processes could still execute set-user-ID, set-group-ID +or file-capability binaries, which would then run with elevated privileges +while being restricted by a Landlock domain they may not expect, making th= em +potential confused deputies. Setting no_new_privs should only be avoided = if +such a privilege transition is expected. + +We now have a ruleset with the first rule allowing read and execute access= to +``/usr`` while denying all other handled accesses for the filesystem, and = two +more rules allowing DNS queries. =20 .. code-block:: c =20 - if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { + /* + * If the ABI > 10, we can tie setting no_new_privs with successful ru= leset + * enforcement and skip the manual prctl(PR_SET_NO_NEW_PRIVS, ...) cal= l. + */ + if (!(restrict_flags & LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS) && + prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { perror("Failed to restrict privileges"); close(ruleset_fd); return 1; @@ -792,6 +813,20 @@ when at least one sys_landlock_add_rule() call is made= for it with the ``LANDLOCK_ADD_RULE_QUIET`` flag, additional add-rule calls for the same object without this flag do not clear it. =20 +no_new_privs flag (ABI < 11) +---------------------------- + +Starting with the Landlock ABI version 11, sys_landlock_restrict_self() +accepts the ``LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS`` flag, which sets the +no_new_privs attribute of the calling thread only once the enforcement of +the ruleset succeeded: no_new_privs is set if and only if the call +succeeds. This removes the need for a prior :manpage:`prctl(2)` +``PR_SET_NO_NEW_PRIVS`` call, and with it the ``CAP_SYS_ADMIN`` +requirement. When combined with ``LANDLOCK_RESTRICT_SELF_TSYNC``, +no_new_privs is set on all threads of the process. As explained in the +tutorial above, not setting no_new_privs is risky even when it is not +required. + .. _kernel_support: =20 Kernel support --=20 2.54.0 From nobody Fri Oct 2 07:45:28 2026 Received: from mail-yx1-f51.google.com (mail-yx1-f51.google.com [74.125.224.51]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C665C3546E0 for ; Mon, 3 Aug 2026 22:31:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.224.51 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785796283; cv=none; b=HnY1+uTp7hl71gaeySI9OEZZQol75UuZ02uBJdsrHrPFZJeOJHJ7yhTSCgMs27QGlzjRqTeR1uavmWKVrHpZsoeD4QPxf3wuUu0hFJHCSzNnxvzYohrXDfVAldYalYxAanp+vHD9r5gddfIq2Iz267YFbh3z+cakyb4nCtzokAQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785796283; c=relaxed/simple; bh=KP2yf+UiOQ+kZdXdrgDO/YAok+x/uEJ6qSv4gTkthFc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=TUbrmEZsCYslkHWdCx1FbSJXjkBdR8CH6Aicgf4oBLyZL1Ii0+kqrPujrn7EAfbE9ra1qS63DuE++usTRtKu1cPOFgIhIOIxos2CYtjwoVvn/RN5HExFV0ApDMtk5JbWMPhIMqtHDm9lgipTtvsgrPk+XSSJVZXgzS5SAUzIjjk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=sqxocZUI; arc=none smtp.client-ip=74.125.224.51 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="sqxocZUI" Received: by mail-yx1-f51.google.com with SMTP id 956f58d0204a3-66896892b6cso5164929d50.0 for ; Mon, 03 Aug 2026 15:31:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785796281; x=1786401081; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=2syxNBpEmU6+L0HeNqA4IUUoR9lvooZKJyrfeWUIyKY=; b=sqxocZUIjx5MLzD7xcDtIYQUjgoUVoduLgb08NFCleeycdRHX91PdC96RS0VUah9AL TK5XFpwFlCZvwvLJqf6+EeKf5qVlo/anIfLgDUQd/2lJdkMYpKC6fxGPsRhb5/g6uk6l lkEyMT98SzP8c+za+wzp4Bv7mV2UcwIAp9e50QrLF10pvtfh4k0BgzI8iGh7x9sB0LgZ CtEwryEindbTbgDlyuwzjK3et5VUb0/W+qzDLuTzXBiqIVXX51saIdPYlLcBD6JaalRr XoOXMD2XvEafrF9gOhzNplEZXdg3l/VMBVlmSDjAwwVd6ISPDNPdIQWz1IEI2supksqd Szkw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785796281; x=1786401081; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=2syxNBpEmU6+L0HeNqA4IUUoR9lvooZKJyrfeWUIyKY=; b=nnzwAx56kVxXuhtkXp8r+QjtqCV8Qwiai3xL7ZfV0yaBTbK14lD/mwak+s1Z8WNK+C /iL6KoMTm6FsWXNqT+wFCDliH23dXJkNX4nWK7aaPirSmttnDg269R3aX8bIcPR5ol+I mre9dwsPtH7Yqdca40wjg2ot+u6VODQEGSJEBKQ1vp6G0E3gNSKf2K9qVNmTkqFWUy1R X1HN1IwWt0eK3Q682unCzNAMLmgGibi6VLmrOIaCmYjNHRjRgya56dgZzoBd0HdGeKwZ oOkCmGt5POZjTFZOXyjCZdSDkg8POCLnOoP4cxawhSn/zwDvaBs62Cc+y7pQ13D2Cwiu hRSg== X-Gm-Message-State: AOJu0YzN6d2JsUawaZGxQXoFidmw5VSTKM3l7pHr+WgUSehcA1utEV+0 LznJWec6KUV16g/Avg86Qz8sOMTuivPoGu3NhtihO8HZVo9v+7kJte3D9D8ODDBC X-Gm-Gg: AR+sD130STscMBJKELUSOn9d1yXhELCqjbppsC+VbZXeyRPFGSFXZsq85YwJTmxUrIt 7YcGFAT9uUFjmQnAjVgeN9CrBlrwhg0GyRzyrMioqoPRqkEfZWl18KPr9ZX+Mz8i+WPIV+HqT5k Seuaqk+ENti9aM5k4XPm9XpJEqV1h2X39oanjQ4UH2QgKSMw+b44hJwim5ry0OuR48ipKt6bnCX cy3py2u6gIIX1ZQvu7nYdT/h2mJahjdYXTFHtredGwLGu2O0zuFHJtg89whimcl8rC5HE9UIhth 41vtODdHzKBQD17Yq5oJkZDV9oGEjMnOsLqU3VLjWwQsOpgc2L5S6Pzz8vPHpkEKt41O1qzuycK Rjx/8M8LkoGOuXbGvnlEoK+A4rD713FYfEWaRo5rfNYGDC0NcAYFnYMpAgaQAin/B1q9/nPh14D 3UUktQSVmiSgHcYpFNocAvfMLZ0qJp4q8QHio1mN3lpGWqPRbvq11LRRDAiH2cv2/5es+m3C15z qfYMVkIk/kunY2GppQR5lY= X-Received: by 2002:a05:690e:440b:b0:667:d82d:d4f3 with SMTP id 956f58d0204a3-6694efcc207mr10221232d50.11.1785796280625; Mon, 03 Aug 2026 15:31:20 -0700 (PDT) Received: from zenbox ([2600:1700:18fb:6011:6253:b407:801c:a745]) by smtp.gmail.com with ESMTPSA id 956f58d0204a3-6694903782fsm6420774d50.21.2026.08.03.15.31.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 03 Aug 2026 15:31:20 -0700 (PDT) From: Justin Suess To: gnoack3000@gmail.com, mic@digikod.net Cc: linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org, Justin Suess Subject: [PATCH v3 4/4] samples/landlock: Add LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS to sampler Date: Mon, 3 Aug 2026 18:31:08 -0400 Message-ID: <20260803223109.707353-5-utilityemal77@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260803223109.707353-1-utilityemal77@gmail.com> References: <20260803223109.707353-1-utilityemal77@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS to the default flag setting. Gate the flag on the ABI version, but do not expose any userspace control over this flag as it has no practical effect on the resulting sandbox. Signed-off-by: Justin Suess --- Notes: v2->v3: - New patch: use LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS by default in the sandboxer, gated on the ABI version, with a prctl fallback samples/landlock/sandboxer.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/samples/landlock/sandboxer.c b/samples/landlock/sandboxer.c index ac71019e6212..030583273f3f 100644 --- a/samples/landlock/sandboxer.c +++ b/samples/landlock/sandboxer.c @@ -369,7 +369,7 @@ static int add_quiet_access(const char *const env_var, return 0; } =20 -#define LANDLOCK_ABI_LAST 10 +#define LANDLOCK_ABI_LAST 11 =20 #define XSTR(s) #s #define STR(s) XSTR(s) @@ -453,8 +453,9 @@ int main(const int argc, char *const argv[], char *cons= t *const envp) .quiet_scoped =3D 0, }; bool quiet_supported =3D true; - int supported_restrict_flags =3D LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON; - int set_restrict_flags =3D 0; + int supported_restrict_flags =3D LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON | + LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS; + int set_restrict_flags =3D LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS; =20 if (argc < 2) { fprintf(stderr, help, argv[0]); @@ -545,6 +546,12 @@ int main(const int argc, char *const argv[], char *con= st *const envp) LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP); /* Removes quiet flags for ABI < 10 later on. */ quiet_supported =3D false; + __attribute__((fallthrough)); + case 10: + /* Removes LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS for ABI < 11 */ + supported_restrict_flags &=3D + ~LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS; + set_restrict_flags &=3D ~LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS; =20 /* Must be printed for any ABI < LANDLOCK_ABI_LAST. */ fprintf(stderr, @@ -673,7 +680,8 @@ int main(const int argc, char *const argv[], char *cons= t *const envp) } } =20 - if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { + if (!(set_restrict_flags & LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS) && + prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { perror("Failed to restrict privileges"); goto err_close_ruleset; } --=20 2.54.0