* [PATCH 0/2] landlock: Fix landlock_restrict_self() on long executable paths
@ 2026-11-11 4:54 Daejun Park via B4 Relay
2026-11-11 4:54 ` [PATCH 1/2] landlock: Fix landlock_restrict_self() failure " Daejun Park via B4 Relay
2026-11-11 4:54 ` [PATCH 2/2] selftests/landlock: Test a long executable path Daejun Park via B4 Relay
0 siblings, 2 replies; 5+ messages in thread
From: Daejun Park via B4 Relay @ 2026-11-11 4:54 UTC (permalink / raw)
To: Mickaël Salaün, Günther Noack
Cc: Paul Moore, James Morris, Serge E. Hallyn, Shuah Khan,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Dwi Siswanto,
Günther Noack, linux-security-module, linux-kselftest,
linux-doc, linux-kernel, stable, Daejun Park
A task whose executable has an absolute path longer than 4106 bytes
triggers a WARN and cannot sandbox itself. Patch 2 adds selftests that
fail without patch 1.
Tested on v7.4-rc2 with KASAN and lockdep. landlock_restrict_self()
from 4107- and 4350-byte paths fails before the series and works after
it. The domain records then show exe="<too_long>". The other Landlock
selftests pass on both kernels, apart from five skipped hostfs tests.
---
Daejun Park (2):
landlock: Fix landlock_restrict_self() failure on long executable paths
selftests/landlock: Test a long executable path
Documentation/admin-guide/LSM/landlock.rst | 3 +-
security/landlock/domain.c | 15 +-
security/landlock/domain.h | 3 +-
tools/testing/selftests/landlock/fs_test.c | 175 +++++++++++++++++++++
.../testing/selftests/landlock/wait-pipe-sandbox.c | 3 +-
5 files changed, 192 insertions(+), 7 deletions(-)
---
base-commit: 7475ea8b58616b33c04afb976cf0fbaad9dd63fb
change-id: 20261110-landlock-long-exe-61cbbfbc3300
Best regards,
--
Daejun Park <daejun7.park@samsung.com>
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/2] landlock: Fix landlock_restrict_self() failure on long executable paths
2026-11-11 4:54 [PATCH 0/2] landlock: Fix landlock_restrict_self() on long executable paths Daejun Park via B4 Relay
@ 2026-11-11 4:54 ` Daejun Park via B4 Relay
2026-11-11 4:59 ` sashiko-bot
2026-11-11 4:54 ` [PATCH 2/2] selftests/landlock: Test a long executable path Daejun Park via B4 Relay
1 sibling, 1 reply; 5+ messages in thread
From: Daejun Park via B4 Relay @ 2026-11-11 4:54 UTC (permalink / raw)
To: Mickaël Salaün, Günther Noack
Cc: Paul Moore, James Morris, Serge E. Hallyn, Shuah Khan,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Dwi Siswanto,
Günther Noack, linux-security-module, linux-kselftest,
linux-doc, linux-kernel, stable, Daejun Park
From: Daejun Park <daejun7.park@samsung.com>
get_current_exe() treats a d_path() error on the current executable as
a bug. But an absolute path is not bounded by PATH_MAX, and d_path()
returns -ENAMETOOLONG when the path does not fit in the
LANDLOCK_PATH_MAX_SIZE buffer.
An unprivileged task with such an executable triggers the
WARN_ON_ONCE(), and its landlock_restrict_self() call fails with
-ENAMETOOLONG.
The path is not used for enforcement. Record "<too_long>" instead, as
audit_log_d_path() does, and create the domain.
Reported-by: Dwi Siswanto <me@dw1.io>
Closes: https://lore.kernel.org/r/20261102022018.73143-1-me@dw1.io
Suggested-by: Dwi Siswanto <me@dw1.io>
Fixes: 1d636984e088 ("landlock: Add AUDIT_LANDLOCK_DOMAIN and log domain status")
Cc: stable@vger.kernel.org
Signed-off-by: Daejun Park <daejun7.park@samsung.com>
---
Documentation/admin-guide/LSM/landlock.rst | 3 ++-
security/landlock/domain.c | 15 +++++++++++----
security/landlock/domain.h | 3 ++-
3 files changed, 15 insertions(+), 6 deletions(-)
diff --git a/Documentation/admin-guide/LSM/landlock.rst b/Documentation/admin-guide/LSM/landlock.rst
index 718e0d23ffb5..983223ce6818 100644
--- a/Documentation/admin-guide/LSM/landlock.rst
+++ b/Documentation/admin-guide/LSM/landlock.rst
@@ -104,7 +104,8 @@ AUDIT_LANDLOCK_DOMAIN
- the enforcement ``mode``
- the domain creator's ``pid``
- the domain creator's ``uid``
- - the domain creator's executable path (``exe``)
+ - the domain creator's executable path (``exe``), or ``"<too_long>"`` if
+ this path does not fit in the kernel buffer
- the domain creator's command line (``comm``)
Example::
diff --git a/security/landlock/domain.c b/security/landlock/domain.c
index 4c0b1f0e1db5..4c9858c11b3d 100644
--- a/security/landlock/domain.c
+++ b/security/landlock/domain.c
@@ -492,7 +492,8 @@ landlock_merge_ruleset(struct landlock_domain *const parent,
* get_current_exe - Get the current's executable path, if any
*
* @exe_str: Returned pointer to a path string with a lifetime tied to the
- * returned buffer, if any.
+ * returned buffer, if any. It is "<too_long>" if the path does
+ * not fit in this buffer.
* @exe_size: Returned size of @exe_str (including the trailing null
* character), if any.
*
@@ -502,6 +503,8 @@ landlock_merge_ruleset(struct landlock_domain *const parent,
static const void *get_current_exe(const char **const exe_str,
size_t *const exe_size)
{
+ /* Cf. audit_log_d_path() */
+ static const char too_long_path[] = "<too_long>";
const size_t buffer_size = LANDLOCK_PATH_MAX_SIZE;
struct mm_struct *mm = current->mm;
struct file *file __free(fput) = NULL;
@@ -521,9 +524,13 @@ static const void *get_current_exe(const char **const exe_str,
return ERR_PTR(-ENOMEM);
exe = d_path(&file->f_path, buffer, buffer_size);
- if (WARN_ON_ONCE(IS_ERR(exe)))
- /* Should never happen according to LANDLOCK_PATH_MAX_SIZE. */
- return ERR_CAST(exe);
+ if (IS_ERR(exe)) {
+ /* The path does not fit: it is not bounded by PATH_MAX. */
+ memcpy(buffer, too_long_path, sizeof(too_long_path));
+ *exe_size = sizeof(too_long_path);
+ *exe_str = buffer;
+ return no_free_ptr(buffer);
+ }
size = buffer + buffer_size - exe;
if (WARN_ON_ONCE(size <= 0))
diff --git a/security/landlock/domain.h b/security/landlock/domain.h
index 935e18b12b3c..d087882fd428 100644
--- a/security/landlock/domain.h
+++ b/security/landlock/domain.h
@@ -72,7 +72,8 @@ struct landlock_details {
/**
* @exe_path: Executable path of the task that initially restricted
* itself, at creation time. Always NULL terminated, and never greater
- * than LANDLOCK_PATH_MAX_SIZE.
+ * than LANDLOCK_PATH_MAX_SIZE. It is "<too_long>" if the path does not
+ * fit.
*/
char exe_path[];
};
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 2/2] selftests/landlock: Test a long executable path
2026-11-11 4:54 [PATCH 0/2] landlock: Fix landlock_restrict_self() on long executable paths Daejun Park via B4 Relay
2026-11-11 4:54 ` [PATCH 1/2] landlock: Fix landlock_restrict_self() failure " Daejun Park via B4 Relay
@ 2026-11-11 4:54 ` Daejun Park via B4 Relay
2026-11-11 5:00 ` sashiko-bot
1 sibling, 1 reply; 5+ messages in thread
From: Daejun Park via B4 Relay @ 2026-11-11 4:54 UTC (permalink / raw)
To: Mickaël Salaün, Günther Noack
Cc: Paul Moore, James Morris, Serge E. Hallyn, Shuah Khan,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Dwi Siswanto,
Günther Noack, linux-security-module, linux-kselftest,
linux-doc, linux-kernel, Daejun Park
From: Daejun Park <daejun7.park@samsung.com>
Add two tests that execute a copy of wait-pipe-sandbox from a path
longer than LANDLOCK_PATH_MAX_SIZE. layout0.long_exe_path checks,
without audit, that the copy can sandbox itself.
audit_layout0.long_exe_path also checks that the copy's domain records
show exe="<too_long>".
Without the get_current_exe() fix, landlock_restrict_self() fails there
with -ENAMETOOLONG.
Also name the right test in the header of wait-pipe-sandbox.c.
Signed-off-by: Daejun Park <daejun7.park@samsung.com>
---
tools/testing/selftests/landlock/fs_test.c | 175 +++++++++++++++++++++
.../testing/selftests/landlock/wait-pipe-sandbox.c | 3 +-
2 files changed, 177 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c
index f725882fa4a7..738a7175d94c 100644
--- a/tools/testing/selftests/landlock/fs_test.c
+++ b/tools/testing/selftests/landlock/fs_test.c
@@ -8013,6 +8013,181 @@ TEST_F(audit_layout1, mount)
EXPECT_EQ(1, records.domain);
}
+/*
+ * Executes a copy of bin_wait_pipe_sandbox from a path which does not fit in
+ * the kernel buffer for the executable path, and checks that this copy can
+ * sandbox itself. Returns the PID of this copy, once it has exited.
+ */
+static pid_t test_long_exe_path(struct __test_metadata *const _metadata)
+{
+ int pipe_child[2], pipe_parent[2];
+ char name[NAME_MAX + 1];
+ int dir_fd, status, i;
+ char buf_parent;
+ pid_t child;
+
+ /*
+ * Creates nested directories whose path (17 * 256 bytes) is longer
+ * than the kernel buffer for the executable path (PATH_MAX + 11
+ * bytes), whatever the current directory is.
+ */
+ memset(name, 'd', sizeof(name) - 1);
+ name[sizeof(name) - 1] = '\0';
+ dir_fd = open(TMP_DIR, O_PATH | O_DIRECTORY | O_CLOEXEC);
+ ASSERT_LE(0, dir_fd);
+ for (i = 0; i <= PATH_MAX / sizeof(name); i++) {
+ int fd;
+
+ ASSERT_EQ(0, mkdirat(dir_fd, name, 0700));
+ fd = openat(dir_fd, name, O_PATH | O_DIRECTORY | O_CLOEXEC);
+ ASSERT_LE(0, fd);
+ EXPECT_EQ(0, close(dir_fd));
+ dir_fd = fd;
+ }
+
+ /* Copies bin_wait_pipe_sandbox to the last directory. */
+ create_file(_metadata, TMP_DIR "/f");
+ copy_file(_metadata, bin_wait_pipe_sandbox, TMP_DIR "/f");
+ ASSERT_EQ(0, renameat(AT_FDCWD, TMP_DIR "/f", dir_fd, "f"));
+
+ ASSERT_EQ(0, pipe2(pipe_child, 0));
+ ASSERT_EQ(0, pipe2(pipe_parent, 0));
+
+ child = fork();
+ ASSERT_LE(0, child);
+ if (child == 0) {
+ const struct landlock_ruleset_attr layer1 = {
+ .scoped = LANDLOCK_SCOPE_SIGNAL,
+ };
+ char pipe_child_str[12], pipe_parent_str[12];
+ char *const argv[] = { "f", pipe_child_str, pipe_parent_str,
+ NULL };
+
+ /* Passes the pipe FDs to the executed binary. */
+ EXPECT_EQ(0, close(pipe_child[0]));
+ EXPECT_EQ(0, close(pipe_parent[1]));
+ snprintf(pipe_child_str, sizeof(pipe_child_str), "%d",
+ pipe_child[1]);
+ snprintf(pipe_parent_str, sizeof(pipe_parent_str), "%d",
+ pipe_parent[0]);
+
+ /* The executed binary expects that it cannot send signals. */
+ drop_access_rights(_metadata, &layer1);
+
+ ASSERT_EQ(0, sys_execveat(dir_fd, argv[0], argv, NULL, 0))
+ {
+ TH_LOG("Failed to execute \"%s\": %s", argv[0],
+ strerror(errno));
+ };
+ _exit(1);
+ }
+
+ EXPECT_EQ(0, close(pipe_child[1]));
+ EXPECT_EQ(0, close(pipe_parent[0]));
+ EXPECT_EQ(0, close(dir_fd));
+
+ /*
+ * Goes through the three steps of the executed binary: it tries to
+ * send a signal, sandboxes itself and tries to open a directory, then
+ * sandboxes itself again and exits.
+ */
+ for (i = 0; i < 3; i++) {
+ ASSERT_EQ(1, read(pipe_child[0], &buf_parent, 1))
+ {
+ /*
+ * The child exits when a step fails. Reaps it before
+ * the teardown, which may fail to unmount TMP_DIR
+ * while the child still uses it.
+ */
+ close(pipe_parent[1]);
+ waitpid(child, &status, 0);
+ }
+ ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
+ }
+
+ EXPECT_EQ(0, close(pipe_parent[1]));
+ EXPECT_EQ(0, close(pipe_child[0]));
+ ASSERT_EQ(child, waitpid(child, &status, 0));
+ ASSERT_EQ(1, WIFEXITED(status));
+ EXPECT_EQ(0, WEXITSTATUS(status));
+
+ return child;
+}
+
+/*
+ * Checks that a task can sandbox itself when the path of its executable does
+ * not fit in the kernel buffer.
+ */
+TEST_F(layout0, long_exe_path)
+{
+ test_long_exe_path(_metadata);
+}
+
+static int matches_log_domain_too_long(int audit_fd, pid_t pid)
+{
+ static const char log_template[] = REGEX_LANDLOCK_PREFIX
+ " status=allocated mode=enforcing pid=%d uid=[0-9]\\+"
+ " exe=\"<too_long>\" comm=\"f\"$";
+ char log_match[sizeof(log_template) + 10];
+ int log_match_len;
+
+ log_match_len =
+ snprintf(log_match, sizeof(log_match), log_template, pid);
+ if (log_match_len >= sizeof(log_match))
+ return -E2BIG;
+
+ return audit_match_record(audit_fd, AUDIT_LANDLOCK_DOMAIN, log_match,
+ NULL);
+}
+
+FIXTURE(audit_layout0)
+{
+ int audit_fd;
+};
+
+FIXTURE_SETUP(audit_layout0)
+{
+ prepare_layout(_metadata);
+
+ /*
+ * No executable filter: audit_init_with_exe_filter() would only keep
+ * the records of this test binary, and the path of the executed copy
+ * is too long for a rule. The test matches its records by PID.
+ */
+ set_cap(_metadata, CAP_AUDIT_CONTROL);
+ self->audit_fd = audit_init();
+ clear_cap(_metadata, CAP_AUDIT_CONTROL);
+ if (self->audit_fd < 0) {
+ /*
+ * FIXTURE_TEARDOWN_PARENT() is not called when FIXTURE_SETUP()
+ * failed, so we need to remove TMP_DIR to avoid cascading
+ * errors with the next tests.
+ */
+ cleanup_layout(_metadata);
+ }
+ ASSERT_LE(0, self->audit_fd);
+}
+
+FIXTURE_TEARDOWN_PARENT(audit_layout0)
+{
+ cleanup_layout(_metadata);
+
+ EXPECT_EQ(0, audit_cleanup(-1, NULL));
+}
+
+/*
+ * Checks that the records of the domains created by such a task get a
+ * placeholder instead of the path of its executable.
+ */
+TEST_F(audit_layout0, long_exe_path)
+{
+ const pid_t child = test_long_exe_path(_metadata);
+
+ /* The executed binary created two domains. */
+ EXPECT_EQ(0, matches_log_domain_too_long(self->audit_fd, child));
+ EXPECT_EQ(0, matches_log_domain_too_long(self->audit_fd, child));
+}
+
static bool debug_quiet_tests;
FIXTURE(audit_quiet_layout1)
diff --git a/tools/testing/selftests/landlock/wait-pipe-sandbox.c b/tools/testing/selftests/landlock/wait-pipe-sandbox.c
index 87dbc9164430..0dfa8e75fc13 100644
--- a/tools/testing/selftests/landlock/wait-pipe-sandbox.c
+++ b/tools/testing/selftests/landlock/wait-pipe-sandbox.c
@@ -2,7 +2,8 @@
/*
* Write in a pipe, wait, sandbox itself, test sandboxing, and wait again.
*
- * Used by audit_exec.flags from audit_test.c
+ * Used by audit_exec.signal_and_open from audit_test.c, and by
+ * layout0.long_exe_path and audit_layout0.long_exe_path from fs_test.c
*
* Copyright © 2024-2025 Microsoft Corporation
*/
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH 1/2] landlock: Fix landlock_restrict_self() failure on long executable paths
2026-11-11 4:54 ` [PATCH 1/2] landlock: Fix landlock_restrict_self() failure " Daejun Park via B4 Relay
@ 2026-11-11 4:59 ` sashiko-bot
0 siblings, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-11-11 4:59 UTC (permalink / raw)
Cc: linux-security-module
> From: Daejun Park <daejun7.park@samsung.com>
>
> get_current_exe() treats a d_path() error on the current executable as
> a bug. But an absolute path is not bounded by PATH_MAX, and d_path()
> returns -ENAMETOOLONG when the path does not fit in the
> LANDLOCK_PATH_MAX_SIZE buffer.
>
> An unprivileged task with such an executable triggers the
> WARN_ON_ONCE(), and its landlock_restrict_self() call fails with
> -ENAMETOOLONG.
>
> The path is not used for enforcement. Record "<too_long>" instead, as
> audit_log_d_path() does, and create the domain.
>
> Reported-by: Dwi Siswanto <me@dw1.io>
> Closes: https://lore.kernel.org/r/20261102022018.73143-1-me@dw1.io
> Suggested-by: Dwi Siswanto <me@dw1.io>
> Fixes: 1d636984e088 ("landlock: Add AUDIT_LANDLOCK_DOMAIN and log domain status")
> Cc: stable@vger.kernel.org
> Signed-off-by: Daejun Park <daejun7.park@samsung.com>
Sashiko has reviewed this patch and found no issues.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261111-landlock-long-exe-v1-0-588cfd174a10@samsung.com?part=1
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH 2/2] selftests/landlock: Test a long executable path
2026-11-11 4:54 ` [PATCH 2/2] selftests/landlock: Test a long executable path Daejun Park via B4 Relay
@ 2026-11-11 5:00 ` sashiko-bot
0 siblings, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-11-11 5:00 UTC (permalink / raw)
Cc: linux-security-module
Thank you for your contribution! Sashiko AI review found no new issues
in this patch, but noticed 1 related pre-existing issue it does not
introduce:
- [Medium] selftests/landlock: TMP_DIR leak on audit setup failure in
fs_test [1]
--
commit f00c6c9b2b3886a7e8767a4a9aab53cc1603cea4
Author: Daejun Park <daejun7.park@samsung.com>
selftests/landlock: Test a long executable path
This commit adds two selftests, layout0.long_exe_path and
audit_layout0.long_exe_path, to verify that a task executed from a path
longer than LANDLOCK_PATH_MAX_SIZE can sandbox itself and logs
exe="<too_long>" in audit domain records.
> diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c
> index f725882fa4a78..738a7175d94cb 100644
> --- a/tools/testing/selftests/landlock/fs_test.c
> +++ b/tools/testing/selftests/landlock/fs_test.c
[ ... ]
> +FIXTURE_SETUP(audit_layout0)
> +{
> + prepare_layout(_metadata);
> +
> + /*
> + * No executable filter: audit_init_with_exe_filter() would only keep
> + * the records of this test binary, and the path of the executed copy
> + * is too long for a rule. The test matches its records by PID.
> + */
> + set_cap(_metadata, CAP_AUDIT_CONTROL);
> + self->audit_fd = audit_init();
> + clear_cap(_metadata, CAP_AUDIT_CONTROL);
> + if (self->audit_fd < 0) {
> + /*
> + * FIXTURE_TEARDOWN_PARENT() is not called when FIXTURE_SETUP()
> + * failed, so we need to remove TMP_DIR to avoid cascading
> + * errors with the next tests.
> + */
> + cleanup_layout(_metadata);
> + }
> + ASSERT_LE(0, self->audit_fd);
> +}
[Severity: Medium]
This problem wasn't introduced by this patch, but do the existing
FIXTURE_SETUP(audit_layout1), FIXTURE_SETUP(audit_quiet_layout1) and
FIXTURE_SETUP(audit_quiet_rename) fixtures leak TMP_DIR [1] in the
same way when audit_init_with_exe_filter() fails?
In those fixtures, prepare_layout() and create_layout1() create TMP_DIR
before calling audit_init_with_exe_filter(), which has no error-path
cleanup:
tools/testing/selftests/landlock/fs_test.c:FIXTURE_SETUP(audit_layout1) {
prepare_layout(_metadata);
create_layout1(_metadata);
set_cap(_metadata, CAP_AUDIT_CONTROL);
self->audit_fd = audit_init_with_exe_filter(&self->audit_filter);
EXPECT_LE(0, self->audit_fd);
disable_caps(_metadata);
}
When audit_init_with_exe_filter() returns a negative error (for example
-EEXIST when auditd holds the audit netlink socket), EXPECT_LE() marks the
setup as failed and skips FIXTURE_TEARDOWN_PARENT(), leaving TMP_DIR on the
filesystem and causing subsequent tests to fail in create_directory() with
EEXIST.
[1] https://sashiko.dev/#/bug/linux-e22dd50d-22f3-469c-81cf-fd03bb763237
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261111-landlock-long-exe-v1-0-588cfd174a10@samsung.com?part=2
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-11-11 5:00 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-11 4:54 [PATCH 0/2] landlock: Fix landlock_restrict_self() on long executable paths Daejun Park via B4 Relay
2026-11-11 4:54 ` [PATCH 1/2] landlock: Fix landlock_restrict_self() failure " Daejun Park via B4 Relay
2026-11-11 4:59 ` sashiko-bot
2026-11-11 4:54 ` [PATCH 2/2] selftests/landlock: Test a long executable path Daejun Park via B4 Relay
2026-11-11 5:00 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox