* [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
` (5 subsequent siblings)
6 siblings, 0 replies; 18+ 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] 18+ 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
` (4 subsequent siblings)
6 siblings, 0 replies; 18+ 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] 18+ 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-08-21 12:38 ` Günther Noack
2026-07-27 23:08 ` [PATCH v2 4/6] selftests/landlock: Test LANDLOCK_SCOPE_SYSV_MSG_QUEUE Justin Suess
` (3 subsequent siblings)
6 siblings, 1 reply; 18+ 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] 18+ messages in thread* Re: [PATCH v2 3/6] landlock: Bump ABI for LANDLOCK_SCOPE_SYSV_MSG_QUEUE
2026-07-27 23:08 ` [PATCH v2 3/6] landlock: Bump ABI for LANDLOCK_SCOPE_SYSV_MSG_QUEUE Justin Suess
@ 2026-08-21 12:38 ` Günther Noack
2026-08-21 13:15 ` Justin Suess
0 siblings, 1 reply; 18+ messages in thread
From: Günther Noack @ 2026-08-21 12:38 UTC (permalink / raw)
To: Justin Suess; +Cc: mic, linux-kernel, linux-security-module
On Mon, Jul 27, 2026 at 07:08:30PM -0400, Justin Suess wrote:
> Bump the ABI version for Landlock SysV message queue scoping.
>
> Signed-off-by: Justin Suess <utilityemal77@gmail.com>
The ABI bump is normally put into the same commit as the
implementation for easier backporting. Otherwise, looks good.
–Günther
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 3/6] landlock: Bump ABI for LANDLOCK_SCOPE_SYSV_MSG_QUEUE
2026-08-21 12:38 ` Günther Noack
@ 2026-08-21 13:15 ` Justin Suess
2026-08-21 14:31 ` Justin Suess
0 siblings, 1 reply; 18+ messages in thread
From: Justin Suess @ 2026-08-21 13:15 UTC (permalink / raw)
To: Günther Noack; +Cc: mic, linux-kernel, linux-security-module
On Fri, Aug 21, 2026 at 02:38:15PM +0200, Günther Noack wrote:
> On Mon, Jul 27, 2026 at 07:08:30PM -0400, Justin Suess wrote:
> > Bump the ABI version for Landlock SysV message queue scoping.
> >
> > Signed-off-by: Justin Suess <utilityemal77@gmail.com>
>
> The ABI bump is normally put into the same commit as the
> implementation for easier backporting. Otherwise, looks good.
I'll squash them.
>
Thanks,
I did wonder about if we need to use the landlock_object here?
I'm pretty sure SysV message queues stay open after process exit,
which could cause the domain to be pinned by landlock_cred_security,
if programs are lazy and don't close them.
So it probably needs to be a weak reference.
But it's unclear what should be the behavior there when the domain
is dropped:
1. Should it become inaccessible and belong to *nobody's* domain?
(i.e when owning domain is dropped, the queue belongs to no domain and is inaccessible to
all LANDLOCK_SCOPE_SYSV_MSG_QUEUE scoped domains)
2. Should it be moved to the parent's domain?
(i.e when owning domain is dropped, the parent domain is the new scope,
and then it's parent, so on and so forth, more complicated, but more correct)
3. Or be kept as is.
(i.e Allow an open sysv message queue to pin a domain
for it's lifetime)
Either way this almost certainly needs to be rebased since it's been
a little bit and there were significant refactorings of the domain and
ruleset structures since the tracepoints series.
Justin
> –Günther
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 3/6] landlock: Bump ABI for LANDLOCK_SCOPE_SYSV_MSG_QUEUE
2026-08-21 13:15 ` Justin Suess
@ 2026-08-21 14:31 ` Justin Suess
0 siblings, 0 replies; 18+ messages in thread
From: Justin Suess @ 2026-08-21 14:31 UTC (permalink / raw)
To: Günther Noack; +Cc: mic, linux-kernel, linux-security-module
On Fri, Aug 21, 2026 at 09:15:31AM -0400, Justin Suess wrote:
> On Fri, Aug 21, 2026 at 02:38:15PM +0200, Günther Noack wrote:
> > On Mon, Jul 27, 2026 at 07:08:30PM -0400, Justin Suess wrote:
> > > Bump the ABI version for Landlock SysV message queue scoping.
> > >
> > > Signed-off-by: Justin Suess <utilityemal77@gmail.com>
> >
> > The ABI bump is normally put into the same commit as the
> > implementation for easier backporting. Otherwise, looks good.
> I'll squash them.
>
> >
> Thanks,
>
> I did wonder about if we need to use the landlock_object here?
>
> I'm pretty sure SysV message queues stay open after process exit,
> which could cause the domain to be pinned by landlock_cred_security,
> if programs are lazy and don't close them.
>
> So it probably needs to be a weak reference.
>
> But it's unclear what should be the behavior there when the domain
> is dropped:
>
> 1. Should it become inaccessible and belong to *nobody's* domain?
>
> (i.e when owning domain is dropped, the queue belongs to no domain and is inaccessible to
> all LANDLOCK_SCOPE_SYSV_MSG_QUEUE scoped domains)
>
> 2. Should it be moved to the parent's domain?
>
> (i.e when owning domain is dropped, the parent domain is the new scope,
> and then it's parent, so on and so forth, more complicated, but more correct)
>
> 3. Or be kept as is.
>
> (i.e Allow an open sysv message queue to pin a domain
> for it's lifetime)
>
Scratch all of this, I think it's best if the kern_ipc_blob takes a reference to
the landlock_hierarchy plus a depth u16.
I think this will require introducing a new
landlock_put_hierarchy_deferred method because of the runtime context of
the ipc hooks, but should be pretty easy.
Also refactor domain_is_scoped() to take the server side as (hierarchy, depth).
Justin
> Either way this almost certainly needs to be rebased since it's been
> a little bit and there were significant refactorings of the domain and
> ruleset structures since the tracepoints series.
>
> Justin
>
> > –Günther
^ permalink raw reply [flat|nested] 18+ 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-08-22 9:44 ` Günther Noack
2026-07-27 23:08 ` [PATCH v2 5/6] samples/landlock: Support LANDLOCK_SCOPE_SYSV_MSG_QUEUE in sandboxer Justin Suess
` (2 subsequent siblings)
6 siblings, 1 reply; 18+ 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] 18+ messages in thread* Re: [PATCH v2 4/6] selftests/landlock: Test LANDLOCK_SCOPE_SYSV_MSG_QUEUE
2026-07-27 23:08 ` [PATCH v2 4/6] selftests/landlock: Test LANDLOCK_SCOPE_SYSV_MSG_QUEUE Justin Suess
@ 2026-08-22 9:44 ` Günther Noack
0 siblings, 0 replies; 18+ messages in thread
From: Günther Noack @ 2026-08-22 9:44 UTC (permalink / raw)
To: Justin Suess; +Cc: mic, linux-kernel, linux-security-module
Hello!
On Mon, Jul 27, 2026 at 07:08:31PM -0400, Justin Suess wrote:
> 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
> + *
Extraneous blank comment line here; did you mean to add a Copyright
line here?
> + */
> +
> +#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);
Is that supported? :)
Michael Kerrisk's Linux Programming Interface book only covered ftok()
and IPC_PRIVATE. In my understanding, if you pass IPC_PRIVATE to
msgget(), you are guaranteed to get a new queue? You are using that
in the other test below as well. Isn't that what you want?
> + 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);
> + }
Testing nit: The checks on the msgget() results should probably be an
EXPECT_*() variant. ASSERT_*() should only be used when continuing to
run the test doesn't otherwise make sense.
Compare https://google.github.io/googletest/reference/assertions.html:
The majority of the macros listed below come as a pair with an
EXPECT_ variant and an ASSERT_ variant. Upon failure, EXPECT_
macros generate nonfatal failures and allow the current function
to continue running, while ASSERT_ macros generate fatal failures
and abort the current function.
> +
> + _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();
I do not understand why this test needs to fork() at all, tbh.
If you were to
* create the queue with msgget(),
* add a message with msgsnd(),
* then enter the Landlock domain (still in the same process),
* and then check for the operation failure
Wouldn't that also fail? After all, the message queue was created at
a point in time when the process was still unrestricted, so we should
be comparing to the Landlock state at that earlier point in time, no?
> + 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);
If these operations are all supposed to fail (and presumably be
no-ops), is it necessary to still use a fixture with multiple cases
for that? (The alternative would be to make this a regular TEST()
without fixtures and do the three failing operations one after
another. It would save you a lot of macro boilerplate and the special
enum, and would make the test more direct IMHO.)
> + /*
> + * 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
>
–Günther
^ permalink raw reply [flat|nested] 18+ 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
2026-08-22 17:26 ` [PATCH v2 0/6] landlock: Add scoped access bit for SysV message queues Günther Noack
6 siblings, 0 replies; 18+ 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] 18+ 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
2026-08-22 17:26 ` [PATCH v2 0/6] landlock: Add scoped access bit for SysV message queues Günther Noack
6 siblings, 0 replies; 18+ 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] 18+ messages in thread* Re: [PATCH v2 0/6] landlock: Add scoped access bit for SysV message queues
2026-07-27 23:08 [PATCH v2 0/6] landlock: Add scoped access bit for SysV message queues Justin Suess
` (5 preceding siblings ...)
2026-07-27 23:08 ` [PATCH v2 6/6] landlock: Document LANDLOCK_SCOPE_SYSV_MSG_QUEUE Justin Suess
@ 2026-08-22 17:26 ` Günther Noack
2026-08-22 21:13 ` Günther Noack
6 siblings, 1 reply; 18+ messages in thread
From: Günther Noack @ 2026-08-22 17:26 UTC (permalink / raw)
To: Justin Suess; +Cc: mic, linux-kernel, linux-security-module
Hello Justin!
Thanks for the patch set and apologies for review delays; I had to
read up on SysV message queues first. :-] It's possible that I still
have misunderstandings and I'm happy to be corrected.
On Mon, Jul 27, 2026 at 07:08:27PM -0400, Justin Suess wrote:
> 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.
I get the impression that with this scheme it would be possible for a
landlocked process to guess the key of a set of programs which have
not created their message queue yet, so that these would then start
communicating on that message queue which the sandboxed process has
access to.
(Other processes can in principle protect against that by using
IPC_CREAT only with IPC_EXCL, but if I understand correctly, it is
also a common pattern that communicating processes all simply use
msgget() with IPC_CREAT but *without* IPC_EXCL, so that the message
queue for them gets created on the fly when first used?)
To get a feeling for the number of invocations without IPC_EXCL,
compare the number of search results on Debian Code Search for:
https://codesearch.debian.net/search?q=msgget%5C%28.*IPC_CREAT&literal=0 (90 results)
https://codesearch.debian.net/search?q=msgget%5C%28.*IPC_EXCL&literal=0 (26 results))
The construction of the keys is often simple and not built to protect
against guessability. ftok() is already somewhat guessable. Some
programs even use hardcoded key numbers or invent their own
ftok()-like derivation scheme.
I do not see how we can prevent the message-queue-squatting situation
with the current patch set; It feels like a mistake that we need to
analyze what other programs outside the sandbox do, in order to
enforce that the sandboxed program can't talk to them.
Do you have thoughts on this?
> 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.
An audit test would be nice as well; we have one for each possible
denial, I think.
>
> 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
>
–Günther
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH v2 0/6] landlock: Add scoped access bit for SysV message queues
2026-08-22 17:26 ` [PATCH v2 0/6] landlock: Add scoped access bit for SysV message queues Günther Noack
@ 2026-08-22 21:13 ` Günther Noack
2026-08-22 21:51 ` Justin Suess
0 siblings, 1 reply; 18+ messages in thread
From: Günther Noack @ 2026-08-22 21:13 UTC (permalink / raw)
To: Justin Suess; +Cc: mic, linux-kernel, linux-security-module
On Sat, Aug 22, 2026 at 07:26:33PM +0200, Günther Noack wrote:
> I get the impression that with this scheme it would be possible for a
> landlocked process to guess the key of a set of programs which have
> not created their message queue yet, so that these would then start
> communicating on that message queue which the sandboxed process has
> access to.
I realized I did maybe not express that clearly enough: Not only would
the landlocked process guess the right key, but it would then also
*create* the queue msgget(key, IPC_CREAT|mode).
There apparently is a pattern in real-world software where the program
creates the message queue on the fly if it doesn't exist yet, but uses
the existing queue if it does. Such software is then prone to reuse
the message queue that was created by the landlocked process. (You
can find such programs using the Debian code search query from the
parent mail.)
Step 1: Landlocked program creates message queue.
Because it creates the queue, it has access to it.
Step 2: Program outside of that domain runs, trying to use the message
queue. It discovers that the queue already exists and starts
using it.
Step 3: Landlocked program can read and write the queue and manipulate
it.
–Günther
> (Other processes can in principle protect against that by using
> IPC_CREAT only with IPC_EXCL, but if I understand correctly, it is
> also a common pattern that communicating processes all simply use
> msgget() with IPC_CREAT but *without* IPC_EXCL, so that the message
> queue for them gets created on the fly when first used?)
>
> To get a feeling for the number of invocations without IPC_EXCL,
> compare the number of search results on Debian Code Search for:
> https://codesearch.debian.net/search?q=msgget%5C%28.*IPC_CREAT&literal=0 (90 results)
> https://codesearch.debian.net/search?q=msgget%5C%28.*IPC_EXCL&literal=0 (26 results))
>
> The construction of the keys is often simple and not built to protect
> against guessability. ftok() is already somewhat guessable. Some
> programs even use hardcoded key numbers or invent their own
> ftok()-like derivation scheme.
>
> I do not see how we can prevent the message-queue-squatting situation
> with the current patch set; It feels like a mistake that we need to
> analyze what other programs outside the sandbox do, in order to
> enforce that the sandboxed program can't talk to them.
>
> Do you have thoughts on this?
>
> > 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.
>
> An audit test would be nice as well; we have one for each possible
> denial, I think.
>
> >
> > 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
> >
>
> –Günther
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH v2 0/6] landlock: Add scoped access bit for SysV message queues
2026-08-22 21:13 ` Günther Noack
@ 2026-08-22 21:51 ` Justin Suess
2026-08-23 22:21 ` Günther Noack
2026-08-24 6:36 ` Günther Noack
0 siblings, 2 replies; 18+ messages in thread
From: Justin Suess @ 2026-08-22 21:51 UTC (permalink / raw)
To: Günther Noack; +Cc: mic, linux-kernel, linux-security-module
On Sat, Aug 22, 2026 at 11:13:44PM +0200, Günther Noack wrote:
> On Sat, Aug 22, 2026 at 07:26:33PM +0200, Günther Noack wrote:
> > I get the impression that with this scheme it would be possible for a
> > landlocked process to guess the key of a set of programs which have
> > not created their message queue yet, so that these would then start
> > communicating on that message queue which the sandboxed process has
> > access to.
>
> I realized I did maybe not express that clearly enough: Not only would
> the landlocked process guess the right key, but it would then also
> *create* the queue msgget(key, IPC_CREAT|mode).
>
> There apparently is a pattern in real-world software where the program
> creates the message queue on the fly if it doesn't exist yet, but uses
> the existing queue if it does. Such software is then prone to reuse
> the message queue that was created by the landlocked process. (You
> can find such programs using the Debian code search query from the
> parent mail.)
>
> Step 1: Landlocked program creates message queue.
> Because it creates the queue, it has access to it.
>
> Step 2: Program outside of that domain runs, trying to use the message
> queue. It discovers that the queue already exists and starts
> using it.
>
> Step 3: Landlocked program can read and write the queue and manipulate
> it.
>
I do agree. It's a little harder than unix sockets where we can control
things at the client/server level (no such construct exists for sysv)
It's a little bit tricky. I suppose the easiest way to handle it would
be to deny the ability to squat in a key in the first place. (deny msgget
except with IPC_PRIVATE).
This comes with a cost in functionality, but it is easy to implement and
closes the gap.
Justin
> –Günther
>
> > (Other processes can in principle protect against that by using
> > IPC_CREAT only with IPC_EXCL, but if I understand correctly, it is
> > also a common pattern that communicating processes all simply use
> > msgget() with IPC_CREAT but *without* IPC_EXCL, so that the message
> > queue for them gets created on the fly when first used?)
> >
> > To get a feeling for the number of invocations without IPC_EXCL,
> > compare the number of search results on Debian Code Search for:
> > https://codesearch.debian.net/search?q=msgget%5C%28.*IPC_CREAT&literal=0 (90 results)
> > https://codesearch.debian.net/search?q=msgget%5C%28.*IPC_EXCL&literal=0 (26 results))
> >
> > The construction of the keys is often simple and not built to protect
> > against guessability. ftok() is already somewhat guessable. Some
> > programs even use hardcoded key numbers or invent their own
> > ftok()-like derivation scheme.
> >
> > I do not see how we can prevent the message-queue-squatting situation
> > with the current patch set; It feels like a mistake that we need to
> > analyze what other programs outside the sandbox do, in order to
> > enforce that the sandboxed program can't talk to them.
> >
> > Do you have thoughts on this?
> >
> > > 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.
> >
> > An audit test would be nice as well; we have one for each possible
> > denial, I think.
> >
> > >
> > > 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
> > >
> >
> > –Günther
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 0/6] landlock: Add scoped access bit for SysV message queues
2026-08-22 21:51 ` Justin Suess
@ 2026-08-23 22:21 ` Günther Noack
2026-08-24 6:36 ` Günther Noack
1 sibling, 0 replies; 18+ messages in thread
From: Günther Noack @ 2026-08-23 22:21 UTC (permalink / raw)
To: Justin Suess; +Cc: mic, linux-kernel, linux-security-module
On Sat, Aug 22, 2026 at 05:51:33PM -0400, Justin Suess wrote:
> On Sat, Aug 22, 2026 at 11:13:44PM +0200, Günther Noack wrote:
> > On Sat, Aug 22, 2026 at 07:26:33PM +0200, Günther Noack wrote:
> > > I get the impression that with this scheme it would be possible for a
> > > landlocked process to guess the key of a set of programs which have
> > > not created their message queue yet, so that these would then start
> > > communicating on that message queue which the sandboxed process has
> > > access to.
> >
> > I realized I did maybe not express that clearly enough: Not only would
> > the landlocked process guess the right key, but it would then also
> > *create* the queue msgget(key, IPC_CREAT|mode).
> >
> > There apparently is a pattern in real-world software where the program
> > creates the message queue on the fly if it doesn't exist yet, but uses
> > the existing queue if it does. Such software is then prone to reuse
> > the message queue that was created by the landlocked process. (You
> > can find such programs using the Debian code search query from the
> > parent mail.)
> >
> > Step 1: Landlocked program creates message queue.
> > Because it creates the queue, it has access to it.
> >
> > Step 2: Program outside of that domain runs, trying to use the message
> > queue. It discovers that the queue already exists and starts
> > using it.
> >
> > Step 3: Landlocked program can read and write the queue and manipulate
> > it.
> >
> I do agree. It's a little harder than unix sockets where we can control
> things at the client/server level (no such construct exists for sysv)
>
> It's a little bit tricky. I suppose the easiest way to handle it would
> be to deny the ability to squat in a key in the first place. (deny msgget
> except with IPC_PRIVATE).
>
> This comes with a cost in functionality, but it is easy to implement and
> closes the gap.
Hm, I have to ponder this; to paraphrase and collect some thoughts:
1. that would make all named (keyed) message queues unreachable,
because they can only be created outside of a Landlock domain.
2. the ones created with IPC_PRIVATE within the same domain are
still usable.
I find it hard to construct a realistic scenario in which any
communication would still happen across scope boundaries in that case.
It comes at the expense of not being able to create named (keyed)
message queues within a Landlock domain, which would be potentially
surprising, but I also don't currently see a better way.
(With low confidence): I do wonder whether a landlocked process should
be able to lift that queue-creation restriction if it does it within
an IPC namespace that was created within that Landlock domain? (It
would complicate the implementation further, and the namespace
interaction would be unusual for a "scoped" access right. Not sure
whether it's worth it.)
It is still possible to guess the msqids within the same IPC
namespace, but the system calls that take a msqid argument will only
work with the ones that were created in the same domain.
Passing a reference to a IPC_PRIVATE queue outwards of a Landlock
domain is possible (e.g. by telling the msgqid number to that process
somehow), and that can establish a communication channel. This seems
like an unusual way to set that up, but it would be acceptable,
because the outside process would need to collaborate to set it up.
This is in my understanding the only way to establish a cross-scope
communication using a message queue, in your proposal?
Apologies for the brain dump here; I have not fully convinced myself,
but would be interested to hear what you think or whether that seems
correct.
Thanks,
–Günther
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 0/6] landlock: Add scoped access bit for SysV message queues
2026-08-22 21:51 ` Justin Suess
2026-08-23 22:21 ` Günther Noack
@ 2026-08-24 6:36 ` Günther Noack
2026-08-24 12:47 ` Justin Suess
1 sibling, 1 reply; 18+ messages in thread
From: Günther Noack @ 2026-08-24 6:36 UTC (permalink / raw)
To: Justin Suess; +Cc: mic, linux-kernel, linux-security-module
On Sat, Aug 22, 2026 at 05:51:33PM -0400, Justin Suess wrote:
> On Sat, Aug 22, 2026 at 11:13:44PM +0200, Günther Noack wrote:
> > On Sat, Aug 22, 2026 at 07:26:33PM +0200, Günther Noack wrote:
> > > I get the impression that with this scheme it would be possible for a
> > > landlocked process to guess the key of a set of programs which have
> > > not created their message queue yet, so that these would then start
> > > communicating on that message queue which the sandboxed process has
> > > access to.
> >
> > I realized I did maybe not express that clearly enough: Not only would
> > the landlocked process guess the right key, but it would then also
> > *create* the queue msgget(key, IPC_CREAT|mode).
> >
> > There apparently is a pattern in real-world software where the program
> > creates the message queue on the fly if it doesn't exist yet, but uses
> > the existing queue if it does. Such software is then prone to reuse
> > the message queue that was created by the landlocked process. (You
> > can find such programs using the Debian code search query from the
> > parent mail.)
> >
> > Step 1: Landlocked program creates message queue.
> > Because it creates the queue, it has access to it.
> >
> > Step 2: Program outside of that domain runs, trying to use the message
> > queue. It discovers that the queue already exists and starts
> > using it.
> >
> > Step 3: Landlocked program can read and write the queue and manipulate
> > it.
> >
> I do agree. It's a little harder than unix sockets where we can control
> things at the client/server level (no such construct exists for sysv)
>
> It's a little bit tricky. I suppose the easiest way to handle it would
> be to deny the ability to squat in a key in the first place. (deny msgget
> except with IPC_PRIVATE).
>
> This comes with a cost in functionality, but it is easy to implement and
> closes the gap.
Agreed. I am personally leaning on the side of closing the gap as
well, even if it reduces functionality somewhat. SystemV Message
Queues are used very seldomly (as can be seen in Debian Code Search)
and it would affect few programs.
Possible Allow-listing variant
------------------------------
Another possibility that occurred to me after writing the last mail:
One option we could also take here might be to *combine* the scoped
approach and a allow-listing approach, as we have also done it for
named Unix sockets.
In this case that could mean that you would be able to allow-list a
*key*, and then for a msgsnd/msgrcv/msgctl queue operation to be
allowed, it would require that either of these two conditions is
fulfilled:
1. The queue was created in the same Landlock scope (only for IPC_PRIVATE), OR
2. The queue is associated with an allow-listed key
Condition 2 is easy to check because the kernel already tracks the
queue<->key association in struct kern_ipc_perm.
Advantages and Disadvantages:
* It would permit to connect outwards of the scope for allow-listed keys,
but only within the limits of what the sandbox policy permits.
* For keys generated on the fly as with ftok(), these keys are hard to
predict up front. (Would have to enforce the policy *after*
calculating ftok().)
---
Given the limited number of programs that use SystemV message queues
at all, I am unsure whether it is needed to implement that. But maybe
it would be an interesting thing to keep in mind so that we keep such
an option open in the implementation? It would at least not rule out
the possibility of restricting SysV message queues in a finer-grained
way.
Let me know what you think.
–Günther
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 0/6] landlock: Add scoped access bit for SysV message queues
2026-08-24 6:36 ` Günther Noack
@ 2026-08-24 12:47 ` Justin Suess
2026-08-24 16:03 ` Günther Noack
0 siblings, 1 reply; 18+ messages in thread
From: Justin Suess @ 2026-08-24 12:47 UTC (permalink / raw)
To: Günther Noack; +Cc: mic, linux-kernel, linux-security-module
On Mon, Aug 24, 2026 at 08:36:10AM +0200, Günther Noack wrote:
> On Sat, Aug 22, 2026 at 05:51:33PM -0400, Justin Suess wrote:
> > On Sat, Aug 22, 2026 at 11:13:44PM +0200, Günther Noack wrote:
> > > On Sat, Aug 22, 2026 at 07:26:33PM +0200, Günther Noack wrote:
> > > > I get the impression that with this scheme it would be possible for a
> > > > landlocked process to guess the key of a set of programs which have
> > > > not created their message queue yet, so that these would then start
> > > > communicating on that message queue which the sandboxed process has
> > > > access to.
> > >
> > > I realized I did maybe not express that clearly enough: Not only would
> > > the landlocked process guess the right key, but it would then also
> > > *create* the queue msgget(key, IPC_CREAT|mode).
> > >
> > > There apparently is a pattern in real-world software where the program
> > > creates the message queue on the fly if it doesn't exist yet, but uses
> > > the existing queue if it does. Such software is then prone to reuse
> > > the message queue that was created by the landlocked process. (You
> > > can find such programs using the Debian code search query from the
> > > parent mail.)
> > >
> > > Step 1: Landlocked program creates message queue.
> > > Because it creates the queue, it has access to it.
> > >
> > > Step 2: Program outside of that domain runs, trying to use the message
> > > queue. It discovers that the queue already exists and starts
> > > using it.
> > >
> > > Step 3: Landlocked program can read and write the queue and manipulate
> > > it.
> > >
> > I do agree. It's a little harder than unix sockets where we can control
> > things at the client/server level (no such construct exists for sysv)
> >
> > It's a little bit tricky. I suppose the easiest way to handle it would
> > be to deny the ability to squat in a key in the first place. (deny msgget
> > except with IPC_PRIVATE).
> >
> > This comes with a cost in functionality, but it is easy to implement and
> > closes the gap.
>
> Agreed. I am personally leaning on the side of closing the gap as
> well, even if it reduces functionality somewhat. SystemV Message
> Queues are used very seldomly (as can be seen in Debian Code Search)
> and it would affect few programs.
>
From msgget(2):
A new message queue is created if key has the value IPC_PRIVATE or
key isn't IPC_PRIVATE, no message queue with the given key key
exists, and IPC_CREAT is specified in msgflg.
So more specifically, we will disallow if all three of these are true:
- IPC_CREAT is specified
- IPC_PRIVATE is not specified.
- The queue referenced by key does not already exist
Or if these two are true:
- The queue referenced by key already exists.
- The queue referenced by the key is not part of the scope.
Effectively, this allows as much as possible, including msgget(2) on an
existing queue within a scope.
> Possible Allow-listing variant
> ------------------------------
>
> Another possibility that occurred to me after writing the last mail:
> One option we could also take here might be to *combine* the scoped
> approach and a allow-listing approach, as we have also done it for
> named Unix sockets.
>
> In this case that could mean that you would be able to allow-list a
> *key*, and then for a msgsnd/msgrcv/msgctl queue operation to be
> allowed, it would require that either of these two conditions is
> fulfilled:
>
> 1. The queue was created in the same Landlock scope (only for IPC_PRIVATE), OR
> 2. The queue is associated with an allow-listed key
>
> Condition 2 is easy to check because the kernel already tracks the
> queue<->key association in struct kern_ipc_perm.
>
> Advantages and Disadvantages:
>
> * It would permit to connect outwards of the scope for allow-listed keys,
> but only within the limits of what the sandbox policy permits.
>
> * For keys generated on the fly as with ftok(), these keys are hard to
> predict up front. (Would have to enforce the policy *after*
> calculating ftok().)
>
I'll keep this out of the scope for now, but it may be a possible
extension in the future, if a convinving usecase exists.
Each access grant could be an (SYSV_IPC_TYPE, key) tuple that implies
the relevant scope byte.
> ---
>
> Given the limited number of programs that use SystemV message queues
> at all, I am unsure whether it is needed to implement that. But maybe
I think that once there are scoped rights for all of the SysV IPC, that
can be examined.
SysV MQ is the oddball one nobody uses. SysV semaphores and shm are much
more popular. But it's important we make all of the SysV IPC rights
consistent with both eachother and the other scoped access rights.
Justin
> it would be an interesting thing to keep in mind so that we keep such
> an option open in the implementation? It would at least not rule out
> the possibility of restricting SysV message queues in a finer-grained
> way.
>
> Let me know what you think.
>
> –Günther
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH v2 0/6] landlock: Add scoped access bit for SysV message queues
2026-08-24 12:47 ` Justin Suess
@ 2026-08-24 16:03 ` Günther Noack
0 siblings, 0 replies; 18+ messages in thread
From: Günther Noack @ 2026-08-24 16:03 UTC (permalink / raw)
To: Justin Suess; +Cc: Günther Noack, mic, linux-kernel, linux-security-module
On Mon, Aug 24, 2026 at 08:47:28AM -0400, Justin Suess wrote:
> On Mon, Aug 24, 2026 at 08:36:10AM +0200, Günther Noack wrote:
> > Agreed. I am personally leaning on the side of closing the gap as
> > well, even if it reduces functionality somewhat. SystemV Message
> > Queues are used very seldomly (as can be seen in Debian Code Search)
> > and it would affect few programs.
> >
> From msgget(2):
>
> A new message queue is created if key has the value IPC_PRIVATE or
> key isn't IPC_PRIVATE, no message queue with the given key key
> exists, and IPC_CREAT is specified in msgflg.
>
> So more specifically, we will disallow if all three of these are true:
>
> - IPC_CREAT is specified
> - IPC_PRIVATE is not specified.
> - The queue referenced by key does not already exist
>
> Or if these two are true:
>
> - The queue referenced by key already exists.
> - The queue referenced by the key is not part of the scope.
>
> Effectively, this allows as much as possible, including msgget(2) on an
> existing queue within a scope.
We technically allow msgget(2) on an existing queue within a scope,
but if I understand this correctly, there is now no way to *create* such a
queue within the scope. So effectively, that msgget(2) can not
succeed, no?
I agree with your sentiment that we don't need to implement the
allow-listing approach right now. It seems like significant effort for
a very small number of use cases.
—Günther
^ permalink raw reply [flat|nested] 18+ messages in thread