All of lore.kernel.org
 help / color / mirror / Atom feed
From: Mark Hatle <mark.hatle@kernel.crashing.org>
To: yocto-patches@lists.yoctoproject.org
Subject: [pseudo][PATCH 1/6] test-landlock: Add a Landlock sandbox test
Date: Thu, 12 Nov 2026 10:19:53 -0600	[thread overview]
Message-ID: <1794500398-31706-2-git-send-email-mark.hatle@kernel.crashing.org> (raw)
In-Reply-To: <1794500398-31706-1-git-send-email-mark.hatle@kernel.crashing.org>

From: Mark Hatle <mark.hatle@amd.com>

[YOCTO #16493]

AI-Generated: Uses GitHub Copilot

Reported-by: Richard Tollerton <rich.tollerton@ni.com>
Signed-off-by: Mark Hatle <mark.hatle@amd.com>
---
 test/test-landlock.c  | 146 ++++++++++++++++++++++++++++++++++++++++++++++++++
 test/test-landlock.sh |  24 +++++++++
 2 files changed, 170 insertions(+)
 create mode 100644 test/test-landlock.c
 create mode 100755 test/test-landlock.sh

diff --git a/test/test-landlock.c b/test/test-landlock.c
new file mode 100644
index 0000000..e9bce88
--- /dev/null
+++ b/test/test-landlock.c
@@ -0,0 +1,146 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * xz (5.6 and later) sandboxes itself with Landlock when it works on a
+ * pipe, revoking every filesystem access right before it does any work.
+ * libpseudo only connects to the server on the first operation it has to
+ * report, and connecting needs filesystem access (open(".") and fchdir()
+ * to the state directory, the pidfile, spawning a server), so under pseudo
+ * the first operation after the sandbox goes up fails. Do what xz does,
+ * then fstat() a descriptor that was opened beforehand and check that
+ * pseudo still reports the ownership it recorded for it.
+ *
+ * If libpseudo refuses Landlock (as it does seccomp), carry on without the
+ * sandbox the way xz does; only the result of the fstat() matters.
+ */
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include <linux/types.h>
+
+/* matches test-openat2-syscall: the shell wrapper turns 77 into "skipped" */
+#define SKIP 77
+
+/* <linux/landlock.h> and the syscall numbers are missing on hosts with
+ * pre-5.13 kernel headers.  The numbers are the same on every architecture.
+ */
+#ifndef __NR_landlock_create_ruleset
+#define __NR_landlock_create_ruleset 444
+#endif
+#ifndef __NR_landlock_restrict_self
+#define __NR_landlock_restrict_self 446
+#endif
+
+struct test_landlock_ruleset_attr {
+	__u64 handled_access_fs;
+};
+
+#define TEST_LANDLOCK_CREATE_RULESET_VERSION	(1U << 0)
+
+/* ABI 1: 13 rights, then REFER (2), TRUNCATE (3), IOCTL_DEV (5) */
+#define TEST_LANDLOCK_ACCESS_FS_ABI1		((1ULL << 13) - 1)
+#define TEST_LANDLOCK_ACCESS_FS_REFER		(1ULL << 13)
+#define TEST_LANDLOCK_ACCESS_FS_TRUNCATE	(1ULL << 14)
+#define TEST_LANDLOCK_ACCESS_FS_IOCTL_DEV	(1ULL << 15)
+
+static int landlock_abi(void) {
+	return (int) syscall(__NR_landlock_create_ruleset, NULL, 0,
+		TEST_LANDLOCK_CREATE_RULESET_VERSION);
+}
+
+/* Is Landlock there at all? The wrapper runs this with PSEUDO_DISABLED
+ * set, so the answer is the kernel's.
+ */
+static int probe(void) {
+	if (landlock_abi() < 1) {
+		if (errno == ENOSYS || errno == EOPNOTSUPP)
+			return SKIP;
+		perror("landlock_create_ruleset(VERSION)");
+		return 1;
+	}
+	return 0;
+}
+
+/* Revoke every filesystem access right the kernel knows about, as xz's
+ * sandbox_enable_strict_if_allowed() does. Returns 0 if the sandbox is up,
+ * 1 if Landlock is not available, -1 on error.
+ */
+static int sandbox(void) {
+	struct test_landlock_ruleset_attr attr = { 0 };
+	int abi, fd;
+
+	abi = landlock_abi();
+	if (abi < 1) {
+		if (errno == ENOSYS || errno == EOPNOTSUPP)
+			return 1;
+		perror("landlock_create_ruleset(VERSION)");
+		return -1;
+	}
+	attr.handled_access_fs = TEST_LANDLOCK_ACCESS_FS_ABI1;
+	if (abi >= 2)
+		attr.handled_access_fs |= TEST_LANDLOCK_ACCESS_FS_REFER;
+	if (abi >= 3)
+		attr.handled_access_fs |= TEST_LANDLOCK_ACCESS_FS_TRUNCATE;
+	if (abi >= 5)
+		attr.handled_access_fs |= TEST_LANDLOCK_ACCESS_FS_IOCTL_DEV;
+
+	fd = (int) syscall(__NR_landlock_create_ruleset, &attr, sizeof(attr), 0);
+	if (fd == -1) {
+		perror("landlock_create_ruleset");
+		return -1;
+	}
+	if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) {
+		perror("prctl(PR_SET_NO_NEW_PRIVS)");
+		close(fd);
+		return -1;
+	}
+	if (syscall(__NR_landlock_restrict_self, fd, 0) == -1) {
+		perror("landlock_restrict_self");
+		close(fd);
+		return -1;
+	}
+	close(fd);
+	return 0;
+}
+
+int main(int argc, char **argv) {
+	struct stat st;
+	uid_t uid;
+	gid_t gid;
+	int rc;
+
+	if (argc > 1 && !strcmp(argv[1], "--probe"))
+		return probe();
+	if (argc != 3) {
+		fprintf(stderr, "usage: %s --probe | uid gid < file\n", argv[0]);
+		return 1;
+	}
+	uid = (uid_t) strtoul(argv[1], NULL, 10);
+	gid = (gid_t) strtoul(argv[2], NULL, 10);
+
+	/* nothing above this point has needed the pseudo server */
+	rc = sandbox();
+	if (rc < 0)
+		return 1;
+	if (rc > 0)
+		fprintf(stderr, "Landlock unavailable, running unsandboxed\n");
+
+	if (fstat(0, &st) == -1) {
+		perror("fstat(stdin)");
+		return 1;
+	}
+	if (st.st_uid != uid || st.st_gid != gid) {
+		fprintf(stderr, "fstat(stdin): owner %d:%d, expected %d:%d\n",
+			(int) st.st_uid, (int) st.st_gid, (int) uid, (int) gid);
+		return 1;
+	}
+	return 0;
+}
diff --git a/test/test-landlock.sh b/test/test-landlock.sh
new file mode 100755
index 0000000..1514236
--- /dev/null
+++ b/test/test-landlock.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: LGPL-2.1-only
+#
+
+# Landlock needs a 5.13 kernel built with it enabled. Ask with pseudo out of
+# the way, so that pseudo refusing Landlock is not mistaken for the kernel
+# lacking it.
+PSEUDO_DISABLED=1 ./test/test-landlock --probe
+case $? in
+0)  ;;
+77) exit 255 ;;
+*)  exit 1 ;;
+esac
+
+file=test-landlock-file
+trap "rm -f $file" 0
+
+echo foo > $file || exit 1
+chown 1234:5678 $file || exit 1
+
+# A client that cannot reach the server retries for about 90 seconds with
+# SIGTERM blocked by the wrapper, so stop it with SIGKILL instead.
+timeout -s KILL 10s ./test/test-landlock 1234 5678 < $file
-- 
1.8.3.1


  reply	other threads:[~2026-11-12 16:20 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-12 16:19 [pseudo][PATCH 0/6] Add landlock support, fix seccomp Mark Hatle
2026-11-12 16:19 ` Mark Hatle [this message]
2026-11-12 16:19 ` [pseudo][PATCH 2/6] pseudo_client: Connect to the server without changing directory Mark Hatle
2026-11-12 16:19 ` [pseudo][PATCH 3/6] ports/linux/pseudo_wrappers: Report Landlock as unsupported Mark Hatle
2026-11-12 16:19 ` [pseudo][PATCH 4/6] test-seccomp: Check that seccomp is never really enabled Mark Hatle
2026-11-12 16:19 ` [pseudo][PATCH 5/6] ports/linux/pseudo_wrappers: Fake seccomp filters set by prctl() Mark Hatle
2026-11-12 16:19 ` [pseudo][PATCH 6/6] ports/linux/pseudo_wrappers: Fake seccomp strict mode in syscall() Mark Hatle

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=1794500398-31706-2-git-send-email-mark.hatle@kernel.crashing.org \
    --to=mark.hatle@kernel.crashing.org \
    --cc=yocto-patches@lists.yoctoproject.org \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.