Re: [PATCH 0/6] landlock: Add POSIX message queue scoping

From: Günther Noack

Date: Fri Aug 21 2026 - 04:46:48 EST


Hello Oxana!

On Wed, Jul 22, 2026 at 01:29:36PM +0100, Oxana Kharitonova wrote:
> This series adds landlock support for scoping POSIX message queuesi [1].
>
> Landlock already supports scoped IPC restrictions for signals and abstract
> UNIX sockets. These restrictions make it possible to prevent a sandboxed
> task from interacting with IPC objects outside of its Landlock domain,
> while still allowing communication within the same domain or with nested
> domains.
>
> This series extends the same model to POSIX message queues with a new
> LANDLOCK_SCOPE_POSIX_MSG_QUEUE scope. When this scope is enforced, a task
> can only open POSIX message queues that were created by a task in the same
> landlock domain or in a nested domain.
>
> The implementation tags mqueuefs inodes at creation time with the creator's
> landlock domain. This domain is kept alive for the lifetime of the inode
> and is checked when the queue is opened.
>
> The series also exposes the mqueuefs magic number through the shared UAPI
> magic header, bumps the Landlock ABI, updates documentation, adds sandboxer
> support, and adds selftests.
>
> The new behavior is:
>
> - a task restricted with LANDLOCK_SCOPE_POSIX_MSG_QUEUE cannot open a queue
> created outside of its Landlock scope;
> - a task can still open a queue created within its own Landlock domain;
> - queues created outside of any Landlock domain are treated as outside the
> scope for a scoped opener.

Thank you for sending this!

I have a high level question about this patch set:

You are adding a check to hook_file_open(), but the existing
hook_file_open() can already prevent mq_open().

The following experiment illustrates this:
* Restrict LANDLOCK_ACCESS_FS_READ_FILE and LANDLOCK_ACCESS_FS_WRITE_FILE
* Try to run mq_open("/foobar", O_CREAT...)

Depending on the installed PATH_BENEATH rules, you now see differing behaviour:

* If we allow READ_FILE and WRITE_FILE on nothing, mq_open() is *DENIED*.
* If we allow READ_FILE and WRITE_FILE on /, mq_open() is *DENIED*.
* If we allow READ_FILE and WRITE_FILE on a mounted /dev/mqueue, mq_open() is *ALLOWED*.

So it seems that the existing file system restrictions are already
preventing POSIX message queues from being opened? And not only that
-- since such Landlock policies are already quite common, it seems
likely that many existing landlocked programs are already restricting
opening of POSIX message queues today.

So, to clarify:

* What your patch set is adding is only that we are now additionally
taking the Landlock domain scope into account?
* This distinction only makes a difference for landlocked programs
that do not restrict READ_FILE/WRITE_FILE or that do restrict it and
then allow-list READ_FILE/WRITE_FILE on a previously mounted
/dev/mqueue.

Maybe this would be interesting to clarify a bit more prominently in
the cover letter, because it reduces the applicability of this
patchset?

Attached below is a LLM-generated (but double checked) test program
which you can use to try out the creation of mqueues. (As first
argument, use "-", "/" or "/dev/mqueue".)

What *might* still be interesting to restrict though: While mq_open()
checks the READ_FILE and WRITE_FILE rights, it checks none of the
LANDLOCK_ACCESS_FS_MAKE_* rights -- the message queue gets still
created, even when the mq_open() is denied and returns with an error.

To expand on Justin's comment in [1] -- it feels that there are maybe
still some gaps in the "lifecycle management" of these message queues
that might be worth thinking systematically about, because both
mq_unlink() and the creation of the message queue entries are
currently apparently not restrictable yet? It makes me wonder whether
hijacking of message queue names (creating same-named queues in other
Landlock domains) is a problem then? Do you have thoughts on this?

Thanks,
–Günther

[1] https://lore.kernel.org/all/amKDYoOSvHMzVbKX@suesslenovo/


--- llmq.c
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <linux/landlock.h>
#include <mqueue.h>
#include <stdio.h>
#include <string.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <unistd.h>

static int ll_create(const struct landlock_ruleset_attr *a, size_t s, __u32 f)
{ return syscall(__NR_landlock_create_ruleset, a, s, f); }
static int ll_add(int fd, enum landlock_rule_type t, const void *a, __u32 f)
{ return syscall(__NR_landlock_add_rule, fd, t, a, f); }
static int ll_self(int fd, __u32 f)
{ return syscall(__NR_landlock_restrict_self, fd, f); }

#define RW (LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE)

int main(int argc, char **argv)
{
const char *grant = argv[1]; /* path to allow, or "-" for none */
struct landlock_ruleset_attr rsa = { .handled_access_fs = RW };
int rs = ll_create(&rsa, sizeof(rsa), 0);
mqd_t mq;
int f;

if (rs < 0) { perror("create_ruleset"); return 1; }

if (strcmp(grant, "-") != 0) {
struct landlock_path_beneath_attr pb = { .allowed_access = RW };
pb.parent_fd = open(grant, O_PATH | O_CLOEXEC);
if (pb.parent_fd < 0) { perror(grant); return 1; }
if (ll_add(rs, LANDLOCK_RULE_PATH_BENEATH, &pb, 0)) {
perror("add_rule"); return 1;
}
close(pb.parent_fd);
}

if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { perror("nnp"); return 1; }
if (ll_self(rs, 0)) { perror("restrict_self"); return 1; }

printf("granted=%-12s ", grant);

/* sanity: a normal file open, to prove the rule itself works */
f = open("/etc/hostname", O_RDONLY);
printf("open(/etc/hostname)=%-14s ",
f >= 0 ? "OK" : strerror(errno));
if (f >= 0) close(f);

mq = mq_open("/foobar", O_CREAT | O_RDWR, 0600, NULL);
printf("mq_open()=%s\n", mq != (mqd_t)-1 ? "OK" : strerror(errno));
if (mq != (mqd_t)-1) mq_close(mq);
return 0;
}
---