Linux Security Modules development
 help / color / mirror / Atom feed
* [PATCH v2 0/6] landlock: Add scoped access bit for SysV message queues
@ 2026-07-27 23:08 Justin Suess
  2026-07-27 23:08 ` [PATCH v2 1/6] landlock: Add kern_ipc_perm credential blob structs Justin Suess
                   ` (5 more replies)
  0 siblings, 6 replies; 7+ messages in thread
From: Justin Suess @ 2026-07-27 23:08 UTC (permalink / raw)
  To: gnoack3000, mic; +Cc: linux-kernel, linux-security-module, Justin Suess

This series extends Landlock with a new scoped access right,
LANDLOCK_SCOPE_SYSV_MSG_QUEUE, allowing a sandboxed process to be
restricted from interacting with SysV message queues created outside
of its Landlock domain (or a nested domain).

While use of SysV message queues is less common than other IPC types,
they are commonly used in older applications which may be vulnerable
to exploitation, so they are a meaningful attack surface to restrict.

Background
==========
SysV message queues have no FD or process-local handle.  A msqid is
valid IPC-namespace-wide and can be obtained without calling
msgget(), so simply hooking msgget() is insufficient.  Domain
provenance has to be tracked on the queue itself and checked on
every operation against it.

Approach
========
A new credential blob is attached to each kern_ipc_perm at creation
time, recording the creating task's Landlock domain and a @kind
tag identifying the IPC object type.  The @kind tag is required
because the LSM core allocates an IPC blob for every kern_ipc_perm
regardless of kind, and the generic ipc_permission hook fires for
semaphores and shared memory as well as message queues.

The enum also leaves room to extend scoping to sem/shm later
without changing the blob layout.

Enforcement is done from security_ipc_permission(), which is the
single choke point for msgget() on an existing queue, msgsnd(),
msgrcv(), and the msgctl() variants that go through ipcperms()
(IPC_STAT, MSG_STAT, MSG_STAT_ANY).  msgctl_down() (IPC_RMID and
IPC_SET) bypasses ipcperms(), so the per-call msg_queue_msgctl
hook is kept for those cases.  msg_queue_msgctl also covers the
IPC_INFO / MSG_INFO case where no specific queue exists.

Quirks
======
- Denials surface as -EACCES rather than -EPERM because the generic
  ipcperms() path maps every LSM denial to -EACCES before returning
  to userspace.  This is documented and the selftests check for
  -EACCES accordingly.
- Because there is no persistent handle, a msqid already obtained
  by a process before it enforces this scope can become unusable
  once the restriction is in place; this is intentional and
  documented.

Patch layout
============
  1. Add the kern_ipc_perm credential blob and @kind enum.
  2. Implement LANDLOCK_SCOPE_SYSV_MSG_QUEUE, the ipc_permission
     hook, and msg_queue_msgctl coverage for IPC_RMID/IPC_SET and
     IPC_INFO/MSG_INFO.
  3. Bump the Landlock ABI.
  4. Selftests covering msgget plus a separate fixture for msgsnd,
     msgrcv, and msgctl using a pre-created msqid.
  5. sandboxer sample support for the new scope.
  6. Documentation updates covering the new scope, the -EACCES
     return code, and the implications of non-persistent handles.

Test coverage
=============
Selftests exercise denial and allow paths for msgget, msgsnd,
msgrcv, and msgctl(IPC_STAT) across domain boundaries, including
nested-domain inheritance.  All existing and added tests are
passing.

Changes since v1
================
- Rebased on mic/next.
- Fixed the kernel-doc Return descriptions of hook_ipc_permission()
  and hook_msg_queue_msgctl().
- Renamed the internal audit request type to
  LANDLOCK_REQUEST_SCOPE_SYSV_MSG_QUEUE for consistency with the
  UAPI macro and the "scope.sysv_msg_queue" audit blocker string.
- Integrated the new scope with the sandboxer's quiet access
  support added in ABI 10 (new "sysv_msg_queue" LL_QUIET_ACCESS
  token).
- Selftests: track the created msqid in the fixture and remove it in
  FIXTURE_TEARDOWN_PARENT() so queues are reclaimed even when a failed
  assertion aborts a test (and never subject to the scoping under
  test); use IPC_PRIVATE where the key is not needed.
- Added CONFIG_SYSVIPC=y to the selftest config fragment.
- Fixed the patch 6 subject typo (LANDLOCK_SCOPE_SYSV_MESSAGE_QUEUE)
  and replaced an incorrect ipcperms(3) manpage reference with the
  kernel helper ipcperms().
- Reworded the LANDLOCK_SCOPE_SYSV_MSG_QUEUE UAPI comment and the
  in-code comment explaining the -EACCES mapping.

v1: https://lore.kernel.org/all/20260521160640.1716746-1-utilityemal77@gmail.com/

Kind Regards,
Justin Suess

Justin Suess (6):
  landlock: Add kern_ipc_perm credential blob structs
  landlock: Add LANDLOCK_SCOPE_SYSV_MSG_QUEUE
  landlock: Bump ABI for LANDLOCK_SCOPE_SYSV_MSG_QUEUE
  selftests/landlock: Test LANDLOCK_SCOPE_SYSV_MSG_QUEUE
  samples/landlock: Support LANDLOCK_SCOPE_SYSV_MSG_QUEUE in sandboxer
  landlock: Document LANDLOCK_SCOPE_SYSV_MSG_QUEUE

 Documentation/admin-guide/LSM/landlock.rst    |   1 +
 Documentation/userspace-api/landlock.rst      |  30 +-
 include/uapi/linux/landlock.h                 |   4 +
 samples/landlock/sandboxer.c                  |  24 +-
 security/landlock/audit.c                     |   4 +
 security/landlock/audit.h                     |   1 +
 security/landlock/limits.h                    |   2 +-
 security/landlock/setup.c                     |   1 +
 security/landlock/syscalls.c                  |   2 +-
 security/landlock/task.c                      | 137 +++++++++
 security/landlock/task.h                      |  50 ++++
 tools/testing/selftests/landlock/base_test.c  |   2 +-
 tools/testing/selftests/landlock/config       |   1 +
 .../landlock/scoped_sysv_msg_queue_test.c     | 265 ++++++++++++++++++
 .../testing/selftests/landlock/scoped_test.c  |   2 +-
 15 files changed, 517 insertions(+), 9 deletions(-)
 create mode 100644 tools/testing/selftests/landlock/scoped_sysv_msg_queue_test.c


base-commit: 28ca6f6f271d47253c240e64cc88a72c89456d74
-- 
2.54.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v2 1/6] landlock: Add kern_ipc_perm credential blob structs
  2026-07-27 23:08 [PATCH v2 0/6] landlock: Add scoped access bit for SysV message queues Justin Suess
@ 2026-07-27 23:08 ` Justin Suess
  2026-07-27 23:08 ` [PATCH v2 2/6] landlock: Add LANDLOCK_SCOPE_SYSV_MSG_QUEUE Justin Suess
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Justin Suess @ 2026-07-27 23:08 UTC (permalink / raw)
  To: gnoack3000, mic; +Cc: linux-kernel, linux-security-module, Justin Suess

Add landlock_kern_ipc_perm_security, tracking ownership of SysV IPC
objects.

The struct contains the creating task's Landlock credential
(@owner_subject) and a @kind enum identifying which SysV IPC object
this blob describes.  The LSM core allocates the IPC blob for every
kern_ipc_perm regardless of object kind, so the generic
ipc_permission hook needs to be able to tell which objects it should
enforce a given scope on.  An enum makes it straightforward to extend
Landlock to sem and shm scoping later without revisiting the blob
layout.

Define the size of this struct in the lbs_ipc field for the Landlock
blob sizes.

Signed-off-by: Justin Suess <utilityemal77@gmail.com>
---
 security/landlock/setup.c |  1 +
 security/landlock/task.h  | 50 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 51 insertions(+)

diff --git a/security/landlock/setup.c b/security/landlock/setup.c
index 47dac1736f10..44aff2d734e9 100644
--- a/security/landlock/setup.c
+++ b/security/landlock/setup.c
@@ -32,6 +32,7 @@ struct lsm_blob_sizes landlock_blob_sizes __ro_after_init = {
 	.lbs_file = sizeof(struct landlock_file_security),
 	.lbs_inode = sizeof(struct landlock_inode_security),
 	.lbs_superblock = sizeof(struct landlock_superblock_security),
+	.lbs_ipc = sizeof(struct landlock_kern_ipc_perm_security),
 };
 
 int landlock_errata __ro_after_init;
diff --git a/security/landlock/task.h b/security/landlock/task.h
index 7c00360219a2..0fb82e5e347c 100644
--- a/security/landlock/task.h
+++ b/security/landlock/task.h
@@ -9,6 +9,56 @@
 #ifndef _SECURITY_LANDLOCK_TASK_H
 #define _SECURITY_LANDLOCK_TASK_H
 
+#include <linux/ipc.h>
+#include <linux/types.h>
+
+#include "cred.h"
+#include "setup.h"
+
+/**
+ * enum landlock_sysv_ipc_kind - Kind of SysV IPC object backed by a blob
+ *
+ * @LANDLOCK_SYSV_IPC_UNSET: Blob has not been tagged by a Landlock IPC
+ *	allocation hook.  This is the zero value used for sem and shm
+ *	objects that Landlock does not currently scope, as well as for
+ *	any future kind that has not yet been wired up.
+ * @LANDLOCK_SYSV_IPC_MSG_QUEUE: Blob belongs to a SysV message queue.
+ */
+enum landlock_sysv_ipc_kind {
+	LANDLOCK_SYSV_IPC_UNSET = 0,
+	LANDLOCK_SYSV_IPC_MSG_QUEUE,
+};
+
+/**
+ * struct landlock_kern_ipc_perm_security - IPC object security blob
+ *
+ * Enable provenance tracking of SysV IPC objects to scope IPC accesses.
+ * The LSM core allocates a blob for every kern_ipc_perm regardless of the
+ * underlying object kind (msg queue, semaphore, shared memory), so callers
+ * that act on a subset of object kinds must consult @kind before
+ * interpreting @owner_subject.
+ */
+struct landlock_kern_ipc_perm_security {
+	/**
+	 * @owner_subject: Landlock credential of the task that created the
+	 * kernel IPC object.  Only meaningful when @kind is not
+	 * %LANDLOCK_SYSV_IPC_UNSET.
+	 */
+	struct landlock_cred_security owner_subject;
+	/**
+	 * @kind: Kind of SysV IPC object this blob describes.  Set by the
+	 * matching alloc hook; %LANDLOCK_SYSV_IPC_UNSET for objects whose
+	 * kind Landlock does not currently track.
+	 */
+	enum landlock_sysv_ipc_kind kind;
+};
+
+static inline struct landlock_kern_ipc_perm_security *
+landlock_kern_ipc_perm(const struct kern_ipc_perm *const perm)
+{
+	return perm->security + landlock_blob_sizes.lbs_ipc;
+}
+
 __init void landlock_add_task_hooks(void);
 
 #endif /* _SECURITY_LANDLOCK_TASK_H */
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v2 2/6] landlock: Add LANDLOCK_SCOPE_SYSV_MSG_QUEUE
  2026-07-27 23:08 [PATCH v2 0/6] landlock: Add scoped access bit for SysV message queues Justin Suess
  2026-07-27 23:08 ` [PATCH v2 1/6] landlock: Add kern_ipc_perm credential blob structs Justin Suess
@ 2026-07-27 23:08 ` Justin Suess
  2026-07-27 23:08 ` [PATCH v2 3/6] landlock: Bump ABI for LANDLOCK_SCOPE_SYSV_MSG_QUEUE Justin Suess
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Justin Suess @ 2026-07-27 23:08 UTC (permalink / raw)
  To: gnoack3000, mic; +Cc: linux-kernel, linux-security-module, Justin Suess

Add a new scoped access right LANDLOCK_SCOPE_SYSV_MSG_QUEUE for
controlling operations msgget, msgsnd, msgrcv, and msgctl on SysV
message queues.

Merely handling msgget is insufficient; SysV message queues do not
use FDs or process local handles, and the msqid associated with a
queue is valid within the IPC namespace.  There is no requirement
to perform a msgget to interact with a SysV message queue.

When a process enforces this scoping, access to SysV message queues
by a restricted process is only allowed if the queue was created by
a process in the same or a nested Landlock domain.

When a SysV message queue is allocated by a process in a Landlock
domain, the security blob for the kern_ipc_perm is updated to
reflect domain provenance and the blob is tagged as a message queue
via the new @kind enum.

The scope is enforced from the generic ipc_permission hook rather
than the per-call msg_queue_* hooks.  ipc_permission is the choke
point for msgget on an existing queue and for msgsnd / msgrcv /
msgctl(IPC_STAT, MSG_STAT, MSG_STAT_ANY).

ipc_permission also fires for semaphores and shared memory, so the
hook bails out when the blob's @kind is not LANDLOCK_SYSV_IPC_MSG_QUEUE.

msgctl_down() (IPC_RMID and IPC_SET) does not go through
ipc_permission, so msg_queue_msgctl is kept to cover those.  It
also guards against the IPC_INFO / MSG_INFO case where @msq is
NULL and there is no specific queue to scope.

Also update the scoped_test ACCESS_LAST sentinel to track the new
last scope so the unknown-scope selftest does not falsely accept
LANDLOCK_SCOPE_SYSV_MSG_QUEUE as unknown.

Audit records are generated for this scope on denials.

Signed-off-by: Justin Suess <utilityemal77@gmail.com>
---
 include/uapi/linux/landlock.h                 |   4 +
 security/landlock/audit.c                     |   4 +
 security/landlock/audit.h                     |   1 +
 security/landlock/limits.h                    |   2 +-
 security/landlock/task.c                      | 137 ++++++++++++++++++
 .../testing/selftests/landlock/scoped_test.c  |   2 +-
 6 files changed, 148 insertions(+), 2 deletions(-)

diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h
index 272f047df438..0b25e5b7acc7 100644
--- a/include/uapi/linux/landlock.h
+++ b/include/uapi/linux/landlock.h
@@ -487,10 +487,14 @@ struct landlock_net_port_attr {
  *   related Landlock domain (e.g., a parent domain or a non-sandboxed process).
  * - %LANDLOCK_SCOPE_SIGNAL: Restrict a sandboxed process from sending a signal
  *   to another process outside the domain.
+ * - %LANDLOCK_SCOPE_SYSV_MSG_QUEUE: Restrict a sandboxed process from
+ *   interacting with a System V message queue created by a process outside the
+ *   domain.
  */
 /* clang-format off */
 #define LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET		(1ULL << 0)
 #define LANDLOCK_SCOPE_SIGNAL		                (1ULL << 1)
+#define LANDLOCK_SCOPE_SYSV_MSG_QUEUE			(1ULL << 2)
 /* clang-format on*/
 
 #endif /* _UAPI_LINUX_LANDLOCK_H */
diff --git a/security/landlock/audit.c b/security/landlock/audit.c
index 50536c568526..32a125fdb5a1 100644
--- a/security/landlock/audit.c
+++ b/security/landlock/audit.c
@@ -82,6 +82,10 @@ get_blocker(const enum landlock_request_type type,
 	case LANDLOCK_REQUEST_SCOPE_SIGNAL:
 		WARN_ON_ONCE(access_bit != -1);
 		return "scope.signal";
+
+	case LANDLOCK_REQUEST_SCOPE_SYSV_MSG_QUEUE:
+		WARN_ON_ONCE(access_bit != -1);
+		return "scope.sysv_msg_queue";
 	}
 
 	WARN_ON_ONCE(1);
diff --git a/security/landlock/audit.h b/security/landlock/audit.h
index 620f8a24291d..f089f960ef17 100644
--- a/security/landlock/audit.h
+++ b/security/landlock/audit.h
@@ -21,6 +21,7 @@ enum landlock_request_type {
 	LANDLOCK_REQUEST_NET_ACCESS,
 	LANDLOCK_REQUEST_SCOPE_ABSTRACT_UNIX_SOCKET,
 	LANDLOCK_REQUEST_SCOPE_SIGNAL,
+	LANDLOCK_REQUEST_SCOPE_SYSV_MSG_QUEUE,
 };
 
 /*
diff --git a/security/landlock/limits.h b/security/landlock/limits.h
index 08d5f2f6d321..77b2f3ada1bd 100644
--- a/security/landlock/limits.h
+++ b/security/landlock/limits.h
@@ -27,7 +27,7 @@
 #define LANDLOCK_MASK_ACCESS_NET	((LANDLOCK_LAST_ACCESS_NET << 1) - 1)
 #define LANDLOCK_NUM_ACCESS_NET		__const_hweight64(LANDLOCK_MASK_ACCESS_NET)
 
-#define LANDLOCK_LAST_SCOPE		LANDLOCK_SCOPE_SIGNAL
+#define LANDLOCK_LAST_SCOPE		LANDLOCK_SCOPE_SYSV_MSG_QUEUE
 #define LANDLOCK_MASK_SCOPE		((LANDLOCK_LAST_SCOPE << 1) - 1)
 #define LANDLOCK_NUM_SCOPE		__const_hweight64(LANDLOCK_MASK_SCOPE)
 
diff --git a/security/landlock/task.c b/security/landlock/task.c
index 55522a601367..64c2d5dca2d6 100644
--- a/security/landlock/task.c
+++ b/security/landlock/task.c
@@ -453,6 +453,138 @@ static int hook_file_send_sigiotask(struct task_struct *tsk,
 	return -EPERM;
 }
 
+static const struct access_masks sysv_msg_queue_scope = {
+	.scope = LANDLOCK_SCOPE_SYSV_MSG_QUEUE,
+};
+
+/**
+ * hook_msg_queue_alloc_security - Record the creator's domain on a msg queue
+ *
+ * @perm: IPC permission structure of the newly created message queue.
+ *
+ * Save a reference to the creating task's Landlock domain in the IPC security
+ * blob and tag the blob as belonging to a message queue so that the generic
+ * ipc_permission hook can distinguish msg queues from sem and shm objects.
+ *
+ * Return: 0 (allocation of the blob itself is handled by the LSM core).
+ */
+static int hook_msg_queue_alloc_security(struct kern_ipc_perm *const perm)
+{
+	struct landlock_kern_ipc_perm_security *const ipc_sec =
+		landlock_kern_ipc_perm(perm);
+	const struct landlock_cred_security *const subject =
+		landlock_get_applicable_subject(current_cred(),
+						sysv_msg_queue_scope, NULL);
+
+	ipc_sec->kind = LANDLOCK_SYSV_IPC_MSG_QUEUE;
+
+	/*
+	 * The blob is zero-allocated by the LSM core, so owner_subject.domain
+	 * is already NULL for an unsandboxed creator.
+	 */
+	if (!subject)
+		return 0;
+
+	landlock_get_ruleset(subject->domain);
+	ipc_sec->owner_subject = *subject;
+	return 0;
+}
+
+/**
+ * hook_msg_queue_free_security - Release the creator's domain reference
+ *
+ * @perm: IPC permission structure of the message queue being destroyed.
+ *
+ * The IPC security blob itself is freed by the LSM core.
+ */
+static void hook_msg_queue_free_security(struct kern_ipc_perm *const perm)
+{
+	struct landlock_kern_ipc_perm_security *const ipc_sec =
+		landlock_kern_ipc_perm(perm);
+
+	/* May be called from an RCU callback (msg_rcu_free()). */
+	landlock_put_ruleset_deferred(ipc_sec->owner_subject.domain);
+}
+
+/**
+ * hook_ipc_permission - Enforce SysV msg queue scoping on the current task
+ *
+ * @ipcp: IPC permission structure of the object being accessed.
+ * @flag: Requested mode bits (unused; same value for every msg queue access).
+ *
+ * The ipc_permission hook is the choke point for msgget on an existing queue
+ * and for msgsnd / msgrcv / msgctl(IPC_STAT, MSG_STAT, MSG_STAT_ANY) before
+ * they touch any per-message state. Using the per-message msg_queue_msgrcv hook
+ * instead would not work: find_msg() silently skips messages for which the
+ * hook returns an error and turns the result into -EAGAIN / -ENOMSG.
+ *
+ * The hook fires for sem and shm objects as well; @kind is used to filter
+ * them out.
+ *
+ * Return: 0 if access is allowed, -EACCES if scoped out.
+ */
+static int hook_ipc_permission(struct kern_ipc_perm *const ipcp,
+			       const short flag)
+{
+	const struct landlock_kern_ipc_perm_security *const ipc_sec =
+		landlock_kern_ipc_perm(ipcp);
+	size_t handle_layer;
+	const struct landlock_cred_security *subject;
+
+	/* Don't worry about other IPC objects for now */
+	if (ipc_sec->kind != LANDLOCK_SYSV_IPC_MSG_QUEUE)
+		return 0;
+
+	subject = landlock_get_applicable_subject(current_cred(),
+						  sysv_msg_queue_scope,
+						  &handle_layer);
+	if (!subject)
+		return 0;
+
+	if (!domain_is_scoped(subject->domain, ipc_sec->owner_subject.domain,
+			      sysv_msg_queue_scope.scope))
+		return 0;
+
+	landlock_log_denial(subject, &(struct landlock_request) {
+		.type = LANDLOCK_REQUEST_SCOPE_SYSV_MSG_QUEUE,
+		.audit = {
+			.type = LSM_AUDIT_DATA_IPC,
+			.u.ipc_id = ipcp->key,
+		},
+		.layer_plus_one = handle_layer + 1,
+	});
+	/*
+	 * The precise error value does not reach user space through
+	 * ipcperms(): its callers map any non-zero return to -EACCES.
+	 * Return -EACCES anyway so that the msgctl_down() path (which
+	 * propagates the hook's return value as is) reports the same error.
+	 */
+	return -EACCES;
+}
+
+/**
+ * hook_msg_queue_msgctl - Enforce scoping on msgctl(IPC_RMID, IPC_SET)
+ *
+ * @msq: IPC permission structure of the message queue, or NULL for
+ *	 namespace-wide commands (IPC_INFO, MSG_INFO).
+ * @cmd: msgctl command code (unused).
+ *
+ * msgctl_down() does not go through ipc_permission(), so this hook is
+ * needed to cover IPC_RMID and IPC_SET.  IPC_INFO and MSG_INFO are
+ * namespace-wide queries with no specific queue, so they are not in scope
+ * for SysV msg queue scoping.
+ *
+ * Return: 0 if access is allowed, -EACCES if scoped out.
+ */
+static int hook_msg_queue_msgctl(struct kern_ipc_perm *const msq, const int cmd)
+{
+	/* IPC_INFO and MSG_INFO are queue-less; nothing to scope. */
+	if (!msq)
+		return 0;
+
+	return hook_ipc_permission(msq, 0);
+}
+
 static struct security_hook_list landlock_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(ptrace_access_check, hook_ptrace_access_check),
 	LSM_HOOK_INIT(ptrace_traceme, hook_ptrace_traceme),
@@ -462,6 +594,11 @@ static struct security_hook_list landlock_hooks[] __ro_after_init = {
 
 	LSM_HOOK_INIT(task_kill, hook_task_kill),
 	LSM_HOOK_INIT(file_send_sigiotask, hook_file_send_sigiotask),
+
+	LSM_HOOK_INIT(msg_queue_alloc_security, hook_msg_queue_alloc_security),
+	LSM_HOOK_INIT(msg_queue_free_security, hook_msg_queue_free_security),
+	LSM_HOOK_INIT(msg_queue_msgctl, hook_msg_queue_msgctl),
+	LSM_HOOK_INIT(ipc_permission, hook_ipc_permission),
 };
 
 __init void landlock_add_task_hooks(void)
diff --git a/tools/testing/selftests/landlock/scoped_test.c b/tools/testing/selftests/landlock/scoped_test.c
index b90f76ed0d9c..6692ba0573e6 100644
--- a/tools/testing/selftests/landlock/scoped_test.c
+++ b/tools/testing/selftests/landlock/scoped_test.c
@@ -12,7 +12,7 @@
 
 #include "common.h"
 
-#define ACCESS_LAST LANDLOCK_SCOPE_SIGNAL
+#define ACCESS_LAST LANDLOCK_SCOPE_SYSV_MSG_QUEUE
 
 TEST(ruleset_with_unknown_scope)
 {
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v2 3/6] landlock: Bump ABI for LANDLOCK_SCOPE_SYSV_MSG_QUEUE
  2026-07-27 23:08 [PATCH v2 0/6] landlock: Add scoped access bit for SysV message queues Justin Suess
  2026-07-27 23:08 ` [PATCH v2 1/6] landlock: Add kern_ipc_perm credential blob structs Justin Suess
  2026-07-27 23:08 ` [PATCH v2 2/6] landlock: Add LANDLOCK_SCOPE_SYSV_MSG_QUEUE Justin Suess
@ 2026-07-27 23:08 ` Justin Suess
  2026-07-27 23:08 ` [PATCH v2 4/6] selftests/landlock: Test LANDLOCK_SCOPE_SYSV_MSG_QUEUE Justin Suess
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Justin Suess @ 2026-07-27 23:08 UTC (permalink / raw)
  To: gnoack3000, mic; +Cc: linux-kernel, linux-security-module, Justin Suess

Bump the ABI version for Landlock SysV message queue scoping.

Signed-off-by: Justin Suess <utilityemal77@gmail.com>
---
 security/landlock/syscalls.c                 | 2 +-
 tools/testing/selftests/landlock/base_test.c | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
index 36b02892c62f..84521a31bf75 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c
@@ -169,7 +169,7 @@ static const struct file_operations ruleset_fops = {
  * If the change involves a fix that requires userspace awareness, also update
  * the errata documentation in Documentation/userspace-api/landlock.rst .
  */
-const int landlock_abi_version = 10;
+const int landlock_abi_version = 11;
 
 /**
  * sys_landlock_create_ruleset - Create a new ruleset
diff --git a/tools/testing/selftests/landlock/base_test.c b/tools/testing/selftests/landlock/base_test.c
index cbd3c1669951..b8b5fa1042ba 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 = {
 		.handled_access_fs = 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));
 
 	ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0,
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v2 4/6] selftests/landlock: Test LANDLOCK_SCOPE_SYSV_MSG_QUEUE
  2026-07-27 23:08 [PATCH v2 0/6] landlock: Add scoped access bit for SysV message queues Justin Suess
                   ` (2 preceding siblings ...)
  2026-07-27 23:08 ` [PATCH v2 3/6] landlock: Bump ABI for LANDLOCK_SCOPE_SYSV_MSG_QUEUE Justin Suess
@ 2026-07-27 23:08 ` Justin Suess
  2026-07-27 23:08 ` [PATCH v2 5/6] samples/landlock: Support LANDLOCK_SCOPE_SYSV_MSG_QUEUE in sandboxer Justin Suess
  2026-07-27 23:08 ` [PATCH v2 6/6] landlock: Document LANDLOCK_SCOPE_SYSV_MSG_QUEUE Justin Suess
  5 siblings, 0 replies; 7+ messages in thread
From: Justin Suess @ 2026-07-27 23:08 UTC (permalink / raw)
  To: gnoack3000, mic; +Cc: linux-kernel, linux-security-module, Justin Suess

Add selftests for SysV message queue scoped right.

Use the existing scoped domain harness for msgget, and another fixture
for testing msgsnd, msgrcv and msgctl.

Pass the msqid around for coverage of non-msgget syscalls, since calling
msgget while already restricted would fail and prevent testing the
operation under test.

Denials are checked against -EACCES rather than -EPERM: msgget,
msgsnd, msgrcv and msgctl(IPC_STAT) all reach the Landlock scope
check via ipcperms(), whose callers map every non-zero return into
-EACCES before propagating it to user space.

Track the created msqid in the fixture and remove it from
FIXTURE_TEARDOWN_PARENT() so that queues are reclaimed even when a
failed assertion aborts a test, and so that the removal is never
subject to the scoping under test.

Also add CONFIG_SYSVIPC to the selftest config fragment since the new
test requires SysV IPC support.

Signed-off-by: Justin Suess <utilityemal77@gmail.com>
---
 tools/testing/selftests/landlock/config       |   1 +
 .../landlock/scoped_sysv_msg_queue_test.c     | 265 ++++++++++++++++++
 2 files changed, 266 insertions(+)
 create mode 100644 tools/testing/selftests/landlock/scoped_sysv_msg_queue_test.c

diff --git a/tools/testing/selftests/landlock/config b/tools/testing/selftests/landlock/config
index 8fe9b461b1fd..8acb03464df4 100644
--- a/tools/testing/selftests/landlock/config
+++ b/tools/testing/selftests/landlock/config
@@ -15,5 +15,6 @@ CONFIG_SECURITY=y
 CONFIG_SECURITY_LANDLOCK=y
 CONFIG_SHMEM=y
 CONFIG_SYSFS=y
+CONFIG_SYSVIPC=y
 CONFIG_TMPFS=y
 CONFIG_TMPFS_XATTR=y
diff --git a/tools/testing/selftests/landlock/scoped_sysv_msg_queue_test.c b/tools/testing/selftests/landlock/scoped_sysv_msg_queue_test.c
new file mode 100644
index 000000000000..91a560c957e6
--- /dev/null
+++ b/tools/testing/selftests/landlock/scoped_sysv_msg_queue_test.c
@@ -0,0 +1,265 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Landlock tests - SysV Message Queue Scoping
+ *
+ */
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/landlock.h>
+#include <sys/ipc.h>
+#include <sys/msg.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "scoped_common.h"
+
+/*
+ * Removes the message queue identified by @msqid, ignoring any error since
+ * the caller might no longer have permission to operate on it (for example,
+ * after entering a scoped domain).
+ */
+static void cleanup_msg_queue(int msqid)
+{
+	if (msqid >= 0)
+		msgctl(msqid, IPC_RMID, NULL);
+}
+
+FIXTURE(scoped_domains)
+{
+	int msqid;
+};
+
+#include "scoped_base_variants.h"
+
+FIXTURE_SETUP(scoped_domains)
+{
+	self->msqid = -1;
+	drop_caps(_metadata);
+}
+
+/*
+ * The queue is removed by the (never sandboxed) test harness parent, which
+ * also runs when an assertion aborts the test after the queue got created.
+ */
+FIXTURE_TEARDOWN_PARENT(scoped_domains)
+{
+	cleanup_msg_queue(self->msqid);
+}
+
+/*
+ * Parent creates a SysV message queue, then the child tries to associate
+ * with it via msgget(2).  When the child is in a domain that scopes message
+ * queues and the parent is not in that same scope, the association must be
+ * denied with -EACCES (msgget runs the scope check via ipcperms(), which
+ * masks every denial as -EACCES).
+ */
+TEST_F(scoped_domains, check_access_msg_queue)
+{
+	pid_t child;
+	int status;
+	int pipe_parent[2], pipe_child[2];
+	char buf;
+	key_t key;
+	bool can_associate;
+
+	/*
+	 * The child can associate with the parent's queue unless the child
+	 * is in a scoped domain that does not include the parent (i.e. the
+	 * parent is outside the child's domain).
+	 */
+	can_associate = !variant->domain_child;
+
+	/*
+	 * Picks a per-test key derived from PID to avoid collisions.  Stale
+	 * queues from a previous run are unlikely but handled by removing
+	 * any matching entry before applying any scope.
+	 */
+	key = (key_t)(getpid() & 0x7fffffff);
+	cleanup_msg_queue(msgget(key, 0));
+
+	if (variant->domain_both)
+		create_scoped_domain(_metadata, LANDLOCK_SCOPE_SYSV_MSG_QUEUE);
+
+	ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC));
+	ASSERT_EQ(0, pipe2(pipe_child, O_CLOEXEC));
+
+	child = fork();
+	ASSERT_LE(0, child);
+	if (child == 0) {
+		int ret;
+
+		EXPECT_EQ(0, close(pipe_child[0]));
+		EXPECT_EQ(0, close(pipe_parent[1]));
+
+		if (variant->domain_child)
+			create_scoped_domain(_metadata,
+					     LANDLOCK_SCOPE_SYSV_MSG_QUEUE);
+
+		/* Signals readiness to the parent. */
+		ASSERT_EQ(1, write(pipe_child[1], ".", 1));
+		EXPECT_EQ(0, close(pipe_child[1]));
+
+		/* Waits for the parent to have created the queue. */
+		ASSERT_EQ(1, read(pipe_parent[0], &buf, 1));
+		EXPECT_EQ(0, close(pipe_parent[0]));
+
+		ret = msgget(key, 0);
+		if (can_associate) {
+			ASSERT_LE(0, ret);
+		} else {
+			ASSERT_EQ(-1, ret);
+			/*
+			 * msgget uses ipcperms(), which masks every LSM
+			 * denial as -EACCES regardless of the value the
+			 * LSM hook returns.
+			 */
+			ASSERT_EQ(EACCES, errno);
+		}
+
+		_exit(_metadata->exit_code);
+		return;
+	}
+	EXPECT_EQ(0, close(pipe_child[1]));
+	EXPECT_EQ(0, close(pipe_parent[0]));
+
+	if (variant->domain_parent)
+		create_scoped_domain(_metadata, LANDLOCK_SCOPE_SYSV_MSG_QUEUE);
+
+	/* Waits for the child to be ready. */
+	ASSERT_EQ(1, read(pipe_child[0], &buf, 1));
+	EXPECT_EQ(0, close(pipe_child[0]));
+
+	self->msqid = msgget(key, IPC_CREAT | IPC_EXCL | 0600);
+	ASSERT_LE(0, self->msqid);
+
+	/* Releases the child. */
+	ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
+	EXPECT_EQ(0, close(pipe_parent[1]));
+
+	ASSERT_EQ(child, waitpid(child, &status, 0));
+
+	if (WIFSIGNALED(status) || !WIFEXITED(status) ||
+	    WEXITSTATUS(status) != EXIT_SUCCESS)
+		_metadata->exit_code = KSFT_FAIL;
+}
+
+/*
+ * The msg_queue_associate hook (exercised by msgget(2)) is covered by the
+ * scoped_domains fixture above.  The remaining hooks all funnel through the
+ * same scope check, so it suffices to verify that each operation is denied
+ * when the child is scoped relative to the queue's creator.
+ *
+ * To attribute a denial to the operation under test (and not to a preceding
+ * msgget(2) call), the parent creates the queue and the child inherits the
+ * msqid across fork(2), bypassing msg_queue_associate.
+ */
+enum msg_op {
+	MSG_OP_SND,
+	MSG_OP_RCV,
+	MSG_OP_CTL,
+};
+
+FIXTURE(scoping_msg_ops)
+{
+	int msqid;
+};
+
+FIXTURE_VARIANT(scoping_msg_ops)
+{
+	enum msg_op op;
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(scoping_msg_ops, msgsnd) {
+	/* clang-format on */
+	.op = MSG_OP_SND,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(scoping_msg_ops, msgrcv) {
+	/* clang-format on */
+	.op = MSG_OP_RCV,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(scoping_msg_ops, msgctl) {
+	/* clang-format on */
+	.op = MSG_OP_CTL,
+};
+
+FIXTURE_SETUP(scoping_msg_ops)
+{
+	self->msqid = -1;
+	drop_caps(_metadata);
+}
+
+/* See the scoped_domains teardown comment. */
+FIXTURE_TEARDOWN_PARENT(scoping_msg_ops)
+{
+	cleanup_msg_queue(self->msqid);
+}
+
+TEST_F(scoping_msg_ops, deny_op)
+{
+	struct msgbuf {
+		long mtype;
+		char mtext[1];
+	} msg = { .mtype = 1 };
+	struct msqid_ds ds;
+	pid_t child;
+	int status;
+	int ret = 0;
+
+	/*
+	 * The child inherits the msqid across fork(2), so no key is needed:
+	 * IPC_PRIVATE always creates a new queue and cannot collide with
+	 * queues left over by other processes.
+	 */
+	self->msqid = msgget(IPC_PRIVATE, 0600);
+	ASSERT_LE(0, self->msqid);
+
+	/* Preloads a message so msgrcv(2) would otherwise succeed. */
+	ASSERT_EQ(0, msgsnd(self->msqid, &msg, sizeof(msg.mtext), 0));
+
+	child = fork();
+	ASSERT_LE(0, child);
+	if (child == 0) {
+		create_scoped_domain(_metadata, LANDLOCK_SCOPE_SYSV_MSG_QUEUE);
+
+		switch (variant->op) {
+		case MSG_OP_SND:
+			ret = msgsnd(self->msqid, &msg, sizeof(msg.mtext), 0);
+			break;
+		case MSG_OP_RCV:
+			ret = msgrcv(self->msqid, &msg, sizeof(msg.mtext), 0,
+				     IPC_NOWAIT);
+			break;
+		case MSG_OP_CTL:
+			ret = msgctl(self->msqid, IPC_STAT, &ds);
+			break;
+		}
+		ASSERT_EQ(-1, ret);
+		/*
+		 * msgsnd, msgrcv and msgctl(IPC_STAT) all reach the
+		 * Landlock scope check via ipcperms(), whose callers map
+		 * any non-zero return into -EACCES before propagating it
+		 * to user space.
+		 */
+		ASSERT_EQ(EACCES, errno);
+
+		_exit(_metadata->exit_code);
+		return;
+	}
+
+	ASSERT_EQ(child, waitpid(child, &status, 0));
+
+	if (WIFSIGNALED(status) || !WIFEXITED(status) ||
+	    WEXITSTATUS(status) != EXIT_SUCCESS)
+		_metadata->exit_code = KSFT_FAIL;
+}
+
+TEST_HARNESS_MAIN
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v2 5/6] samples/landlock: Support LANDLOCK_SCOPE_SYSV_MSG_QUEUE in sandboxer
  2026-07-27 23:08 [PATCH v2 0/6] landlock: Add scoped access bit for SysV message queues Justin Suess
                   ` (3 preceding siblings ...)
  2026-07-27 23:08 ` [PATCH v2 4/6] selftests/landlock: Test LANDLOCK_SCOPE_SYSV_MSG_QUEUE Justin Suess
@ 2026-07-27 23:08 ` Justin Suess
  2026-07-27 23:08 ` [PATCH v2 6/6] landlock: Document LANDLOCK_SCOPE_SYSV_MSG_QUEUE Justin Suess
  5 siblings, 0 replies; 7+ messages in thread
From: Justin Suess @ 2026-07-27 23:08 UTC (permalink / raw)
  To: gnoack3000, mic; +Cc: linux-kernel, linux-security-module, Justin Suess

Add sandboxer support for the new LANDLOCK_SCOPE_SYSV_MSG_QUEUE access
right: handle it by default, parse it as the "m" token of LL_SCOPED,
and integrate it with the quiet access rights via the new
"sysv_msg_queue" LL_QUIET_ACCESS token.

Signed-off-by: Justin Suess <utilityemal77@gmail.com>
---
 samples/landlock/sandboxer.c | 24 ++++++++++++++++++++----
 1 file changed, 20 insertions(+), 4 deletions(-)

diff --git a/samples/landlock/sandboxer.c b/samples/landlock/sandboxer.c
index ac71019e6212..88acb47765a2 100644
--- a/samples/landlock/sandboxer.c
+++ b/samples/landlock/sandboxer.c
@@ -240,10 +240,12 @@ static bool check_ruleset_scope(const char *const env_var,
 	bool error = false;
 	bool abstract_scoping = false;
 	bool signal_scoping = false;
+	bool sysv_msg_queue_scoping = false;
 
 	/* Scoping is not supported by Landlock ABI */
 	if (!(ruleset_attr->scoped &
-	      (LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET | LANDLOCK_SCOPE_SIGNAL)))
+	      (LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET | LANDLOCK_SCOPE_SIGNAL |
+	       LANDLOCK_SCOPE_SYSV_MSG_QUEUE)))
 		goto out_unset;
 
 	env_type_scope = getenv(env_var);
@@ -260,6 +262,9 @@ static bool check_ruleset_scope(const char *const env_var,
 		} else if (strcmp("s", ipc_scoping_name) == 0 &&
 			   !signal_scoping) {
 			signal_scoping = true;
+		} else if (strcmp("m", ipc_scoping_name) == 0 &&
+			   !sysv_msg_queue_scoping) {
+			sysv_msg_queue_scoping = true;
 		} else {
 			fprintf(stderr, "Unknown or duplicate scope \"%s\"\n",
 				ipc_scoping_name);
@@ -276,6 +281,8 @@ static bool check_ruleset_scope(const char *const env_var,
 		ruleset_attr->scoped &= ~LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET;
 	if (!signal_scoping)
 		ruleset_attr->scoped &= ~LANDLOCK_SCOPE_SIGNAL;
+	if (!sysv_msg_queue_scoping)
+		ruleset_attr->scoped &= ~LANDLOCK_SCOPE_SYSV_MSG_QUEUE;
 
 	unsetenv(env_var);
 	return error;
@@ -354,6 +361,9 @@ static int add_quiet_access(const char *const env_var,
 				LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET;
 		else if (strcmp(str_access, "signal") == 0)
 			ruleset_attr->quiet_scoped |= LANDLOCK_SCOPE_SIGNAL;
+		else if (strcmp(str_access, "sysv_msg_queue") == 0)
+			ruleset_attr->quiet_scoped |=
+				LANDLOCK_SCOPE_SYSV_MSG_QUEUE;
 		else {
 			fprintf(stderr, "Unknown quiet access \"%s\"\n",
 				str_access);
@@ -369,7 +379,7 @@ static int add_quiet_access(const char *const env_var,
 	return 0;
 }
 
-#define LANDLOCK_ABI_LAST 10
+#define LANDLOCK_ABI_LAST 11
 
 #define XSTR(s) #s
 #define STR(s) XSTR(s)
@@ -400,6 +410,7 @@ static const char help[] =
 	"* " ENV_SCOPED_NAME ": actions denied on the outside of the landlock domain\n"
 	"  - \"a\" to restrict opening abstract unix sockets\n"
 	"  - \"s\" to restrict sending signals\n"
+	"  - \"m\" to restrict associating with message queues\n"
 	"\n"
 	"A sandboxer should not log denied access requests to avoid spamming logs, "
 	"but to test audit we can set " ENV_FORCE_LOG_NAME "=1\n"
@@ -416,6 +427,7 @@ static const char help[] =
 	"  - \"udp_connect\" to quiet udp connect / send denials\n"
 	"  - \"abstract_unix_socket\" to quiet abstract unix socket denials\n"
 	"  - \"signal\" to quiet signal denials\n"
+	"  - \"sysv_msg_queue\" to quiet SysV message queue denials\n"
 	"\n"
 	"Example:\n"
 	ENV_FS_RO_NAME "=\"${PATH}:/lib:/usr:/proc:/etc:/dev/urandom\" "
@@ -423,7 +435,7 @@ static const char help[] =
 	ENV_TCP_BIND_NAME "=\"9418\" "
 	ENV_TCP_CONNECT_NAME "=\"80:443\" "
 	ENV_UDP_CONNECT_SEND_NAME "=\"53\" "
-	ENV_SCOPED_NAME "=\"a:s\" "
+	ENV_SCOPED_NAME "=\"a:s:m\" "
 	"%1$s bash -i\n"
 	"\n"
 	"This sandboxer can use Landlock features up to ABI version "
@@ -447,7 +459,7 @@ int main(const int argc, char *const argv[], char *const *const envp)
 				      LANDLOCK_ACCESS_NET_BIND_UDP |
 				      LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP,
 		.scoped = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET |
-			  LANDLOCK_SCOPE_SIGNAL,
+			  LANDLOCK_SCOPE_SIGNAL | LANDLOCK_SCOPE_SYSV_MSG_QUEUE,
 		.quiet_access_fs = 0,
 		.quiet_access_net = 0,
 		.quiet_scoped = 0,
@@ -546,6 +558,10 @@ int main(const int argc, char *const argv[], char *const *const envp)
 		/* Removes quiet flags for ABI < 10 later on. */
 		quiet_supported = false;
 
+		__attribute__((fallthrough));
+	case 10:
+		/* Removes LANDLOCK_SCOPE_SYSV_MSG_QUEUE for ABI < 11 */
+		ruleset_attr.scoped &= ~LANDLOCK_SCOPE_SYSV_MSG_QUEUE;
 		/* Must be printed for any ABI < LANDLOCK_ABI_LAST. */
 		fprintf(stderr,
 			"Hint: You should update the running kernel "
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v2 6/6] landlock: Document LANDLOCK_SCOPE_SYSV_MSG_QUEUE
  2026-07-27 23:08 [PATCH v2 0/6] landlock: Add scoped access bit for SysV message queues Justin Suess
                   ` (4 preceding siblings ...)
  2026-07-27 23:08 ` [PATCH v2 5/6] samples/landlock: Support LANDLOCK_SCOPE_SYSV_MSG_QUEUE in sandboxer Justin Suess
@ 2026-07-27 23:08 ` Justin Suess
  5 siblings, 0 replies; 7+ messages in thread
From: Justin Suess @ 2026-07-27 23:08 UTC (permalink / raw)
  To: gnoack3000, mic; +Cc: linux-kernel, linux-security-module, Justin Suess

Document the new SysV message queue scope restriction.  Make clear
that because these queues do not use persistent handles, subsequent
operations on a queue already obtained via msgget (or any other
means) may be restricted once this right is enforced.  Also note
that denials surface as -EACCES rather than -EPERM, since the
generic SysV IPC permission path maps every LSM denial to -EACCES.

Signed-off-by: Justin Suess <utilityemal77@gmail.com>
---
 Documentation/admin-guide/LSM/landlock.rst |  1 +
 Documentation/userspace-api/landlock.rst   | 30 +++++++++++++++++++++-
 2 files changed, 30 insertions(+), 1 deletion(-)

diff --git a/Documentation/admin-guide/LSM/landlock.rst b/Documentation/admin-guide/LSM/landlock.rst
index 8eb85c9381ff..ce5f9653b69a 100644
--- a/Documentation/admin-guide/LSM/landlock.rst
+++ b/Documentation/admin-guide/LSM/landlock.rst
@@ -63,6 +63,7 @@ AUDIT_LANDLOCK_ACCESS
     **scope.*** - IPC scoping restrictions (ABI 6+):
         - scope.abstract_unix_socket - Abstract UNIX socket connection denied
         - scope.signal - Signal sending denied
+        - scope.sysv_msg_queue - SysV message queue operation denied (ABI 11+)
 
     Multiple blockers can appear in a single event (comma-separated) when
     multiple access rights are missing. For example, creating a regular file
diff --git a/Documentation/userspace-api/landlock.rst b/Documentation/userspace-api/landlock.rst
index 5a63d4476c1c..7c5453747457 100644
--- a/Documentation/userspace-api/landlock.rst
+++ b/Documentation/userspace-api/landlock.rst
@@ -86,7 +86,8 @@ to be explicit about the denied-by-default access rights.
             LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP,
         .scoped =
             LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET |
-            LANDLOCK_SCOPE_SIGNAL,
+            LANDLOCK_SCOPE_SIGNAL |
+            LANDLOCK_SCOPE_SYSV_MSG_QUEUE,
     };
 
 Because we may not know which kernel version an application will be executed
@@ -140,6 +141,10 @@ version, and only use the available subset of access rights:
         ruleset_attr.handled_access_net &=
             ~(LANDLOCK_ACCESS_NET_BIND_UDP |
               LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP);
+        __attribute__((fallthrough));
+    case 10:
+        /* Removes LANDLOCK_SCOPE_SYSV_MSG_QUEUE for ABI < 11 */
+        ruleset_attr.scoped &= ~LANDLOCK_SCOPE_SYSV_MSG_QUEUE;
     }
 
 This enables the creation of an inclusive ruleset that will contain our rules.
@@ -420,6 +425,22 @@ The operations which can be scoped are:
     A :manpage:`sendto(2)` on a socket which was previously connected will not
     be restricted.  This works for both datagram and stream sockets.
 
+``LANDLOCK_SCOPE_SYSV_MSG_QUEUE``
+    This limits the set of System V message queues to which we can perform
+    :manpage:`msgget(2)`, :manpage:`msgrcv(2)`, :manpage:`msgsnd(2)`, and
+    :manpage:`msgctl(2)` calls to only message queues which were created by a
+    process in the same or a nested Landlock domain.
+
+    Since System V message queues are IPC namespace global constructs and do
+    not use file descriptors, enforcement of a ruleset with this scoping may
+    cause subsequent operations on an msqid that were allowed prior to
+    enforcement to be denied.
+
+    Denials are reported as ``EACCES``.  Unlike other Landlock scopes,
+    the check runs in the generic SysV IPC permission path (the kernel's
+    ``ipcperms()`` helper), whose callers map every denial to ``EACCES``
+    before it reaches user space.
+
 IPC scoping does not support exceptions via :manpage:`landlock_add_rule(2)`.
 If an operation is scoped within a domain, no rules can be added to allow access
 to resources or processes outside of the scope.
@@ -789,6 +810,13 @@ 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.
 
+System V message queue (ABI < 11)
+---------------------------------
+
+Starting with the Landlock ABI version 11, it is possible to restrict
+operations on System V message queues by setting
+``LANDLOCK_SCOPE_SYSV_MSG_QUEUE`` to the ``scoped`` ruleset attribute.
+
 .. _kernel_support:
 
 Kernel support
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-07-27 23:08 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-27 23:08 [PATCH v2 0/6] landlock: Add scoped access bit for SysV message queues Justin Suess
2026-07-27 23:08 ` [PATCH v2 1/6] landlock: Add kern_ipc_perm credential blob structs Justin Suess
2026-07-27 23:08 ` [PATCH v2 2/6] landlock: Add LANDLOCK_SCOPE_SYSV_MSG_QUEUE Justin Suess
2026-07-27 23:08 ` [PATCH v2 3/6] landlock: Bump ABI for LANDLOCK_SCOPE_SYSV_MSG_QUEUE Justin Suess
2026-07-27 23:08 ` [PATCH v2 4/6] selftests/landlock: Test LANDLOCK_SCOPE_SYSV_MSG_QUEUE Justin Suess
2026-07-27 23:08 ` [PATCH v2 5/6] samples/landlock: Support LANDLOCK_SCOPE_SYSV_MSG_QUEUE in sandboxer Justin Suess
2026-07-27 23:08 ` [PATCH v2 6/6] landlock: Document LANDLOCK_SCOPE_SYSV_MSG_QUEUE Justin Suess

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