* [PATCH 0/2] landlock: Fix landlock_restrict_self() on long executable paths
@ 2026-11-11 4:54 ` Daejun Park
0 siblings, 0 replies; 8+ 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] 8+ messages in thread* [PATCH 0/2] landlock: Fix landlock_restrict_self() on long executable paths @ 2026-11-11 4:54 ` Daejun Park 0 siblings, 0 replies; 8+ messages in thread From: Daejun Park @ 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] 8+ messages in thread
* [PATCH 1/2] landlock: Fix landlock_restrict_self() failure on long executable paths 2026-11-11 4:54 ` Daejun Park @ 2026-11-11 4:54 ` Daejun Park -1 siblings, 0 replies; 8+ 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] 8+ messages in thread
* [PATCH 1/2] landlock: Fix landlock_restrict_self() failure on long executable paths @ 2026-11-11 4:54 ` Daejun Park 0 siblings, 0 replies; 8+ messages in thread From: Daejun Park @ 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 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] 8+ messages in thread
* Re: [PATCH 1/2] landlock: Fix landlock_restrict_self() failure on long executable paths 2026-11-11 4:54 ` Daejun Park (?) @ 2026-11-11 4:59 ` sashiko-bot -1 siblings, 0 replies; 8+ 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] 8+ messages in thread
* [PATCH 2/2] selftests/landlock: Test a long executable path 2026-11-11 4:54 ` Daejun Park @ 2026-11-11 4:54 ` Daejun Park -1 siblings, 0 replies; 8+ 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] 8+ messages in thread
* [PATCH 2/2] selftests/landlock: Test a long executable path @ 2026-11-11 4:54 ` Daejun Park 0 siblings, 0 replies; 8+ messages in thread From: Daejun Park @ 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 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] 8+ messages in thread
* Re: [PATCH 2/2] selftests/landlock: Test a long executable path 2026-11-11 4:54 ` Daejun Park (?) @ 2026-11-11 5:00 ` sashiko-bot -1 siblings, 0 replies; 8+ 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] 8+ messages in thread
end of thread, other threads:[~2026-11-11 5:00 UTC | newest] Thread overview: 8+ 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 ` Daejun Park 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 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 4:54 ` Daejun Park 2026-11-11 5:00 ` sashiko-bot
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.