diff options
Diffstat (limited to 'tools/testing')
| -rw-r--r-- | tools/testing/selftests/landlock/audit.h | 82 | ||||
| -rw-r--r-- | tools/testing/selftests/landlock/audit_test.c | 34 |
2 files changed, 77 insertions, 39 deletions
diff --git a/tools/testing/selftests/landlock/audit.h b/tools/testing/selftests/landlock/audit.h index 74e1c3d763be..834005b2b0f0 100644 --- a/tools/testing/selftests/landlock/audit.h +++ b/tools/testing/selftests/landlock/audit.h @@ -249,9 +249,9 @@ static __maybe_unused char *regex_escape(const char *const src, char *dst, static int audit_match_record(int audit_fd, const __u16 type, const char *const pattern, __u64 *domain_id) { - struct audit_message msg; + struct audit_message msg, last_mismatch = {}; int ret, err = 0; - bool matches_record = !type; + int num_type_match = 0; regmatch_t matches[2]; regex_t regex; @@ -259,21 +259,35 @@ static int audit_match_record(int audit_fd, const __u16 type, if (ret) return -EINVAL; - do { + /* + * Reads records until one matches both the expected type and the + * pattern. Type-matching records with non-matching content are + * silently consumed, which handles stale domain deallocation records + * from a previous test emitted asynchronously by kworker threads. + */ + while (true) { memset(&msg, 0, sizeof(msg)); err = audit_recv(audit_fd, &msg); - if (err) + if (err) { + if (num_type_match) { + printf("DATA: %s\n", last_mismatch.data); + printf("ERROR: %d record(s) matched type %u" + " but not pattern: %s\n", + num_type_match, type, pattern); + } goto out; + } - if (msg.header.nlmsg_type == type) - matches_record = true; - } while (!matches_record); + if (type && msg.header.nlmsg_type != type) + continue; - ret = regexec(®ex, msg.data, ARRAY_SIZE(matches), matches, 0); - if (ret) { - printf("DATA: %s\n", msg.data); - printf("ERROR: no match for pattern: %s\n", pattern); - err = -ENOENT; + ret = regexec(®ex, msg.data, ARRAY_SIZE(matches), matches, + 0); + if (!ret) + break; + + num_type_match++; + last_mismatch = msg; } if (domain_id) { @@ -316,21 +330,49 @@ static int __maybe_unused matches_log_domain_allocated(int audit_fd, pid_t pid, domain_id); } -static int __maybe_unused matches_log_domain_deallocated( - int audit_fd, unsigned int num_denials, __u64 *domain_id) +/* + * Matches a domain deallocation record. When expected_domain_id is non-zero, + * the pattern includes the specific domain ID so that stale deallocation + * records from a previous test (with a different domain ID) are skipped by + * audit_match_record(), and the socket timeout is temporarily increased to + * audit_tv_dom_drop to wait for the asynchronous kworker deallocation. + */ +static int __maybe_unused +matches_log_domain_deallocated(int audit_fd, unsigned int num_denials, + __u64 expected_domain_id, __u64 *domain_id) { static const char log_template[] = REGEX_LANDLOCK_PREFIX " status=deallocated denials=%u$"; - char log_match[sizeof(log_template) + 10]; - int log_match_len; + static const char log_template_with_id[] = + "^audit([0-9.:]\\+): domain=\\(%llx\\)" + " status=deallocated denials=%u$"; + char log_match[sizeof(log_template_with_id) + 32]; + int log_match_len, err; + + if (expected_domain_id) + log_match_len = snprintf(log_match, sizeof(log_match), + log_template_with_id, + (unsigned long long)expected_domain_id, + num_denials); + else + log_match_len = snprintf(log_match, sizeof(log_match), + log_template, num_denials); - log_match_len = snprintf(log_match, sizeof(log_match), log_template, - num_denials); if (log_match_len >= sizeof(log_match)) return -E2BIG; - return audit_match_record(audit_fd, AUDIT_LANDLOCK_DOMAIN, log_match, - domain_id); + if (expected_domain_id) + setsockopt(audit_fd, SOL_SOCKET, SO_RCVTIMEO, + &audit_tv_dom_drop, sizeof(audit_tv_dom_drop)); + + err = audit_match_record(audit_fd, AUDIT_LANDLOCK_DOMAIN, log_match, + domain_id); + + if (expected_domain_id) + setsockopt(audit_fd, SOL_SOCKET, SO_RCVTIMEO, &audit_tv_default, + sizeof(audit_tv_default)); + + return err; } struct audit_records { diff --git a/tools/testing/selftests/landlock/audit_test.c b/tools/testing/selftests/landlock/audit_test.c index c697e22d8f68..93ae5bd0dcce 100644 --- a/tools/testing/selftests/landlock/audit_test.c +++ b/tools/testing/selftests/landlock/audit_test.c @@ -139,23 +139,24 @@ TEST_F(audit, layers) WEXITSTATUS(status) != EXIT_SUCCESS) _metadata->exit_code = KSFT_FAIL; - /* Purges log from deallocated domains. */ - EXPECT_EQ(0, setsockopt(self->audit_fd, SOL_SOCKET, SO_RCVTIMEO, - &audit_tv_dom_drop, sizeof(audit_tv_dom_drop))); + /* + * Purges log from deallocated domains. Records arrive in LIFO order + * (innermost domain first) because landlock_put_hierarchy() walks the + * chain sequentially in a single kworker context. + */ for (i = ARRAY_SIZE(*domain_stack) - 1; i >= 0; i--) { __u64 deallocated_dom = 2; EXPECT_EQ(0, matches_log_domain_deallocated(self->audit_fd, 1, + (*domain_stack)[i], &deallocated_dom)); EXPECT_EQ((*domain_stack)[i], deallocated_dom) { TH_LOG("Failed to match domain %llx (#%d)", - (*domain_stack)[i], i); + (unsigned long long)(*domain_stack)[i], i); } } EXPECT_EQ(0, munmap(domain_stack, sizeof(*domain_stack))); - EXPECT_EQ(0, setsockopt(self->audit_fd, SOL_SOCKET, SO_RCVTIMEO, - &audit_tv_default, sizeof(audit_tv_default))); EXPECT_EQ(0, close(ruleset_fd)); } @@ -271,13 +272,9 @@ TEST_F(audit, thread) EXPECT_EQ(0, close(pipe_parent[1])); ASSERT_EQ(0, pthread_join(thread, NULL)); - EXPECT_EQ(0, setsockopt(self->audit_fd, SOL_SOCKET, SO_RCVTIMEO, - &audit_tv_dom_drop, sizeof(audit_tv_dom_drop))); - EXPECT_EQ(0, matches_log_domain_deallocated(self->audit_fd, 1, - &deallocated_dom)); + EXPECT_EQ(0, matches_log_domain_deallocated( + self->audit_fd, 1, denial_dom, &deallocated_dom)); EXPECT_EQ(denial_dom, deallocated_dom); - EXPECT_EQ(0, setsockopt(self->audit_fd, SOL_SOCKET, SO_RCVTIMEO, - &audit_tv_default, sizeof(audit_tv_default))); } /* @@ -753,22 +750,21 @@ TEST_F(audit_flags, signal) if (variant->restrict_flags & LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF) { + /* + * No deallocation record: denials=0 never matches a real + * record. + */ EXPECT_EQ(-EAGAIN, - matches_log_domain_deallocated(self->audit_fd, 0, + matches_log_domain_deallocated(self->audit_fd, 0, 0, &deallocated_dom)); EXPECT_EQ(deallocated_dom, 2); } else { - EXPECT_EQ(0, setsockopt(self->audit_fd, SOL_SOCKET, SO_RCVTIMEO, - &audit_tv_dom_drop, - sizeof(audit_tv_dom_drop))); EXPECT_EQ(0, matches_log_domain_deallocated(self->audit_fd, 2, + *self->domain_id, &deallocated_dom)); EXPECT_NE(deallocated_dom, 2); EXPECT_NE(deallocated_dom, 0); EXPECT_EQ(deallocated_dom, *self->domain_id); - EXPECT_EQ(0, setsockopt(self->audit_fd, SOL_SOCKET, SO_RCVTIMEO, - &audit_tv_default, - sizeof(audit_tv_default))); } } |
