From: Oxana Kharitonova <oxana@cloudflare.com>
To: mic@digikod.net, gnoack@google.com
Cc: paul@paul-moore.com, jmorris@namei.or, serge@hallyn.com,
wangyan01@kylinos.cn, linux-security-module@vger.kernel.org,
linux-kernel@vger.kernel.org, landlock@lists.linux.dev,
oxana@cloudflare.com, webprosto@gmail.com
Subject: [PATCH 2/6] landlock: Scope POSIX message queue opens
Date: Wed, 22 Jul 2026 13:29:38 +0100 [thread overview]
Message-ID: <20260722122952.42149-3-oxana@cloudflare.com> (raw)
In-Reply-To: <20260722122952.42149-1-oxana@cloudflare.com>
Add support for enforcing LANDLOCK_SCOPE_POSIX_MSG_QUEUE when opening
POSIX message queues.
Tag mqueuefs inodes at instantiation time with the landlock domain of
the task that created the queue. This domain is stored in the landlock
inode security blob and kept alive until the inode security blob is
released.
On file open, detect mqueuefs regular files and compare the queue
creator's domain with the opener's scoped domain. Deny the open when
the opener is restricted by LANDLOCK_SCOPE_POSIX_MSG_QUEUE and the queue
was created outside of an allowed parent domain.
This makes POSIX message queues follow the same scoped-domain model as
the existing landlock IPC restrictions, while keeping the queue creator
domain tied to the lifetime of the queue inode.
Signed-off-by: Oxana Kharitonova <oxana@cloudflare.com>
---
include/uapi/linux/landlock.h | 1 +
security/landlock/audit.c | 9 ++++++++
security/landlock/audit.h | 1 +
security/landlock/fs.c | 35 ++++++++++++++++++++++++++++
security/landlock/fs.h | 15 ++++++++++++
security/landlock/limits.h | 2 +-
security/landlock/ruleset.c | 1 -
security/landlock/task.c | 43 +++++++++++++++++++++++++++++++++++
security/landlock/task.h | 4 ++++
9 files changed, 109 insertions(+), 2 deletions(-)
diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h
index 272f047df438..96d0c3b423ac 100644
--- a/include/uapi/linux/landlock.h
+++ b/include/uapi/linux/landlock.h
@@ -491,6 +491,7 @@ struct landlock_net_port_attr {
/* clang-format off */
#define LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET (1ULL << 0)
#define LANDLOCK_SCOPE_SIGNAL (1ULL << 1)
+#define LANDLOCK_SCOPE_POSIX_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..895397b5cbca 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_POSIX_MSG_QUEUE:
+ WARN_ON_ONCE(access_bit != -1);
+ return "scope.posix_msg_queue";
}
WARN_ON_ONCE(1);
@@ -646,6 +650,11 @@ void landlock_log_denial(const struct landlock_cred_security *const subject,
!!(quiet_mask &
LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET);
break;
+ case LANDLOCK_REQUEST_SCOPE_POSIX_MSG_QUEUE:
+ quiet_applicable_to_access =
+ !!(quiet_mask &
+ LANDLOCK_SCOPE_POSIX_MSG_QUEUE);
+ break;
/*
* Leave LANDLOCK_REQUEST_PTRACE and
* LANDLOCK_REQUEST_FS_CHANGE_TOPOLOGY unhandled for now - they
diff --git a/security/landlock/audit.h b/security/landlock/audit.h
index 620f8a24291d..ce85417548fa 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_POSIX_MSG_QUEUE,
};
/*
diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index f7e5e4ef9eac..c6558b448a06 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -51,6 +51,7 @@
#include "object.h"
#include "ruleset.h"
#include "setup.h"
+#include "task.h"
/* Underlying object management */
@@ -1268,6 +1269,33 @@ static void hook_inode_free_security_rcu(void *inode_security)
*/
inode_sec = inode_security + landlock_blob_sizes.lbs_inode;
WARN_ON_ONCE(inode_sec->object);
+
+ landlock_put_ruleset_deferred(inode_sec->mq_domain);
+}
+
+/*
+ * Tag a newly created POSIX message queue with its creator's domain.
+ *
+ * This is the earliest reachable point with both the creator's context and a
+ * fully initialized inode.
+ */
+static void hook_d_instantiate(struct dentry *const dentry,
+ struct inode *const inode)
+{
+ struct landlock_ruleset *dom;
+
+ if (!landlock_is_posix_mqueue_inode(inode))
+ return;
+
+ dom = landlock_get_current_domain();
+ if (!dom)
+ return;
+
+ if (WARN_ON_ONCE(landlock_inode(inode)->mq_domain))
+ return;
+
+ landlock_get_ruleset(dom);
+ landlock_inode(inode)->mq_domain = dom;
}
/* Super-block hooks */
@@ -1756,6 +1784,12 @@ static int hook_file_open(struct file *const file)
const struct landlock_cred_security *const subject =
landlock_get_applicable_subject(file->f_cred, any_fs, NULL);
struct landlock_request request = {};
+ int err;
+
+ /* POSIX message queue scoping is independent of FS access rights. */
+ err = landlock_check_posix_mqueue_open(file);
+ if (err)
+ return err;
if (!subject)
return 0;
@@ -1979,6 +2013,7 @@ static void hook_file_free_security(struct file *file)
static struct security_hook_list landlock_hooks[] __ro_after_init = {
LSM_HOOK_INIT(inode_free_security_rcu, hook_inode_free_security_rcu),
+ LSM_HOOK_INIT(d_instantiate, hook_d_instantiate),
LSM_HOOK_INIT(sb_delete, hook_sb_delete),
LSM_HOOK_INIT(sb_mount, hook_sb_mount),
diff --git a/security/landlock/fs.h b/security/landlock/fs.h
index b4421d9df68f..aefe078845fa 100644
--- a/security/landlock/fs.h
+++ b/security/landlock/fs.h
@@ -14,6 +14,7 @@
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/rcupdate.h>
+#include <uapi/linux/magic.h>
#include "access.h"
#include "cred.h"
@@ -38,6 +39,14 @@ struct landlock_inode_security {
* performed by get_inode_object().
*/
struct landlock_object __rcu *object;
+ /**
+ * @mq_domain: Domain of the task that created POSIX message queue.
+ * Only set for mqueuefs inodes (i.e. s_magic == MQUEUE_MAGIC) at
+ * creation time by hook_d_instantiate(), never modified afterwards.
+ * Used to check LANDLOCK_SCOPE_POSIX_MSG_QUEUE against the
+ * accessing task's domain.
+ */
+ struct landlock_ruleset *mq_domain;
};
/**
@@ -141,6 +150,12 @@ landlock_inode(const struct inode *const inode)
return inode->i_security + landlock_blob_sizes.lbs_inode;
}
+static inline bool
+landlock_is_posix_mqueue_inode(const struct inode *const inode)
+{
+ return S_ISREG(inode->i_mode) && inode->i_sb->s_magic == MQUEUE_MAGIC;
+}
+
static inline struct landlock_superblock_security *
landlock_superblock(const struct super_block *const superblock)
{
diff --git a/security/landlock/limits.h b/security/landlock/limits.h
index 08d5f2f6d321..70a7c5c7af85 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_POSIX_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/ruleset.c b/security/landlock/ruleset.c
index 4dd09ea22c84..aa37d50ead87 100644
--- a/security/landlock/ruleset.c
+++ b/security/landlock/ruleset.c
@@ -520,7 +520,6 @@ static void free_ruleset_work(struct work_struct *const work)
free_ruleset(ruleset);
}
-/* Only called by hook_cred_free(). */
void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset)
{
if (ruleset && refcount_dec_and_test(&ruleset->usage)) {
diff --git a/security/landlock/task.c b/security/landlock/task.c
index 55522a601367..d65e43550b26 100644
--- a/security/landlock/task.c
+++ b/security/landlock/task.c
@@ -453,6 +453,49 @@ static int hook_file_send_sigiotask(struct task_struct *tsk,
return -EPERM;
}
+static const struct access_masks posix_mqueue_scope = {
+ .scope = LANDLOCK_SCOPE_POSIX_MSG_QUEUE,
+};
+
+/**
+ * landlock_check_posix_mqueue_open - Deny opening a POSIX message queue
+ * created by a task from a different (non-ancestor) domain
+ *
+ * @file: The mqueuefs file being opened.
+ *
+ * Return: -EPERM if the open must be denied, 0 otherwise.
+ */
+int landlock_check_posix_mqueue_open(struct file *const file)
+{
+ const struct inode *const inode = file_inode(file);
+ const struct landlock_cred_security *subject;
+ size_t handle_layer;
+
+ if (!landlock_is_posix_mqueue_inode(inode))
+ return 0;
+
+ subject = landlock_get_applicable_subject(file->f_cred,
+ posix_mqueue_scope,
+ &handle_layer);
+ if (!subject)
+ return 0;
+
+ if (!domain_is_scoped(subject->domain,
+ landlock_inode(inode)->mq_domain,
+ LANDLOCK_SCOPE_POSIX_MSG_QUEUE))
+ return 0;
+
+ landlock_log_denial(subject, &(struct landlock_request) {
+ .type = LANDLOCK_REQUEST_SCOPE_POSIX_MSG_QUEUE,
+ .audit = {
+ .type = LSM_AUDIT_DATA_FILE,
+ .u.file = file,
+ },
+ .layer_plus_one = handle_layer + 1,
+ });
+ return -EPERM;
+}
+
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),
diff --git a/security/landlock/task.h b/security/landlock/task.h
index 7c00360219a2..0b031dc17bbf 100644
--- a/security/landlock/task.h
+++ b/security/landlock/task.h
@@ -9,6 +9,10 @@
#ifndef _SECURITY_LANDLOCK_TASK_H
#define _SECURITY_LANDLOCK_TASK_H
+#include <linux/fs.h>
+
__init void landlock_add_task_hooks(void);
+int landlock_check_posix_mqueue_open(struct file *const file);
+
#endif /* _SECURITY_LANDLOCK_TASK_H */
--
2.50.1 (Apple Git-155)
next prev parent reply other threads:[~2026-07-22 12:30 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-22 12:29 [PATCH 0/6] landlock: Add POSIX message queue scoping Oxana Kharitonova
2026-07-22 12:29 ` [PATCH 1/6] ipc: Move mqueue fs magic to uapi magic header Oxana Kharitonova
2026-07-22 12:43 ` Günther Noack
2026-07-22 15:14 ` Oxana Kharitonova
2026-07-22 12:29 ` Oxana Kharitonova [this message]
2026-07-22 12:29 ` [PATCH 3/6] landlock: Bump ABI for LANDLOCK_SCOPE_POSIX_MSG_QUEUE Oxana Kharitonova
2026-07-22 12:29 ` [PATCH 4/6] selftests/landlock: Test POSIX message queue scoping Oxana Kharitonova
2026-07-22 12:29 ` [PATCH 5/6] samples/landlock: Support " Oxana Kharitonova
2026-07-22 12:29 ` [PATCH 6/6] landlock: Document " Oxana Kharitonova
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260722122952.42149-3-oxana@cloudflare.com \
--to=oxana@cloudflare.com \
--cc=gnoack@google.com \
--cc=jmorris@namei.or \
--cc=landlock@lists.linux.dev \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
--cc=mic@digikod.net \
--cc=paul@paul-moore.com \
--cc=serge@hallyn.com \
--cc=wangyan01@kylinos.cn \
--cc=webprosto@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox