xref: /linux/security/landlock/trace.c (revision d41d0021a6ea3e9fcd14126a00fead47f981c46e)
1b4540a72SMickaël Salaün // SPDX-License-Identifier: GPL-2.0-only
2b4540a72SMickaël Salaün /*
3b4540a72SMickaël Salaün  * Landlock - Tracepoint helpers
4b4540a72SMickaël Salaün  *
5b4540a72SMickaël Salaün  * Copyright © 2025 Microsoft Corporation
6b4540a72SMickaël Salaün  * Copyright © 2026 Cloudflare, Inc.
7b4540a72SMickaël Salaün  */
8b4540a72SMickaël Salaün 
9*d41d0021SMickaël Salaün #include <kunit/test.h>
1001ce260fSMickaël Salaün #include <linux/cleanup.h>
1101ce260fSMickaël Salaün #include <linux/dcache.h>
1201ce260fSMickaël Salaün #include <linux/err.h>
1301ce260fSMickaël Salaün #include <linux/fs.h>
1401ce260fSMickaël Salaün #include <linux/lsm_audit.h>
1501ce260fSMickaël Salaün #include <net/sock.h>
1601ce260fSMickaël Salaün 
1701ce260fSMickaël Salaün #include "access.h"
1867567f03SMickaël Salaün #include "domain.h"
1901ce260fSMickaël Salaün #include "fs.h"
2001ce260fSMickaël Salaün #include "log.h"
21b4540a72SMickaël Salaün #include "ruleset.h"
2201ce260fSMickaël Salaün #include "trace.h"
23b4540a72SMickaël Salaün 
24b4540a72SMickaël Salaün /*
25b4540a72SMickaël Salaün  * Generates the tracepoint definitions in this translation unit.  The trace
26b4540a72SMickaël Salaün  * event header dereferences the traced objects in TP_fast_assign, so the full
2767567f03SMickaël Salaün  * struct definitions (e.g. ruleset.h, domain.h) must be included before it.
28b4540a72SMickaël Salaün  */
29b4540a72SMickaël Salaün #define CREATE_TRACE_POINTS
30b4540a72SMickaël Salaün #include <trace/events/landlock.h>
3167567f03SMickaël Salaün 
3267567f03SMickaël Salaün /**
3367567f03SMickaël Salaün  * landlock_trace_free_domain - Emit a tracepoint on domain deallocation
3467567f03SMickaël Salaün  *
3567567f03SMickaël Salaün  * @hierarchy: The domain's hierarchy being deallocated.
3667567f03SMickaël Salaün  *
3767567f03SMickaël Salaün  * Fires only for a hierarchy whose creation event was emitted, i.e. one that
3867567f03SMickaël Salaün  * left LANDLOCK_LOG_UNCOMMITTED in landlock_restrict_self().  This keeps the
3967567f03SMickaël Salaün  * create/free pair balanced: a hierarchy that never became observable is freed
4067567f03SMickaël Salaün  * silently, while a domain that landlock_restrict_self() created and a
4167567f03SMickaël Salaün  * thread-sync failure then aborted still fires free_domain, because its
4267567f03SMickaël Salaün  * creation event already fired.
4367567f03SMickaël Salaün  *
4467567f03SMickaël Salaün  * Called from landlock_log_free_domain().
4567567f03SMickaël Salaün  */
4667567f03SMickaël Salaün void landlock_trace_free_domain(const struct landlock_hierarchy *const hierarchy)
4767567f03SMickaël Salaün {
4867567f03SMickaël Salaün 	/*
4967567f03SMickaël Salaün 	 * The log_status read is a correctness guard (keep the create/free pair
5067567f03SMickaël Salaün 	 * balanced), not a cost guard, so this cold path needs no
5167567f03SMickaël Salaün 	 * trace_..._enabled() check: the tracepoint is a static-branch no-op
5267567f03SMickaël Salaün 	 * when disabled.  The denial path guards trace_..._enabled() instead
5367567f03SMickaël Salaün 	 * because it does expensive __getname()/path work before emitting.
5467567f03SMickaël Salaün 	 */
5567567f03SMickaël Salaün 	if (READ_ONCE(hierarchy->log_status) != LANDLOCK_LOG_UNCOMMITTED)
5667567f03SMickaël Salaün 		trace_landlock_free_domain(hierarchy);
5767567f03SMickaël Salaün }
5801ce260fSMickaël Salaün 
5901ce260fSMickaël Salaün /**
6001ce260fSMickaël Salaün  * landlock_trace_denial - Emit a tracepoint for a denied access request
6101ce260fSMickaël Salaün  *
6201ce260fSMickaël Salaün  * @request: Detail of the user space request.
6301ce260fSMickaël Salaün  * @youngest_denied: The youngest hierarchy node that denied the access.
6401ce260fSMickaël Salaün  * @missing: The set of denied access rights.
6501ce260fSMickaël Salaün  * @same_exec: Whether the current task is the same executable that called
6601ce260fSMickaël Salaün  *             landlock_restrict_self() for the denying domain, as computed
6701ce260fSMickaël Salaün  *             by landlock_log_denial().
6801ce260fSMickaël Salaün  * @logged: Whether the domain's policy selects this denial for logging, as
6901ce260fSMickaël Salaün  *          computed by landlock_log_denial().
7001ce260fSMickaël Salaün  *
7101ce260fSMickaël Salaün  * Emits the tracepoint matching @request->type when its event is enabled.
7201ce260fSMickaël Salaün  * Unlike audit, fires regardless of @logged; the value is recorded in the event
7301ce260fSMickaël Salaün  * so consumers can filter on it.
7401ce260fSMickaël Salaün  *
7501ce260fSMickaël Salaün  * Called from landlock_log_denial().
7601ce260fSMickaël Salaün  */
7701ce260fSMickaël Salaün void landlock_trace_denial(
7801ce260fSMickaël Salaün 	const struct landlock_request *const request,
7901ce260fSMickaël Salaün 	const struct landlock_hierarchy *const youngest_denied,
8001ce260fSMickaël Salaün 	const access_mask_t missing, const bool same_exec, const bool logged)
8101ce260fSMickaël Salaün {
8201ce260fSMickaël Salaün 	switch (request->type) {
8301ce260fSMickaël Salaün 	case LANDLOCK_REQUEST_FS_ACCESS:
8401ce260fSMickaël Salaün 	case LANDLOCK_REQUEST_FS_CHANGE_TOPOLOGY:
8501ce260fSMickaël Salaün 		if (trace_landlock_deny_access_fs_enabled()) {
8601ce260fSMickaël Salaün 			char *buf __free(__putname) = __getname();
8701ce260fSMickaël Salaün 			struct path dentry_path;
8801ce260fSMickaël Salaün 			const char *pathname;
8901ce260fSMickaël Salaün 			const struct path *path = NULL;
9001ce260fSMickaël Salaün 
9101ce260fSMickaël Salaün 			/*
9201ce260fSMickaël Salaün 			 * Selects the path from the audit data type, as
9301ce260fSMickaël Salaün 			 * dump_common_audit_data() does.  A FS_ACCESS denial
9401ce260fSMickaël Salaün 			 * carries a file (hook_file_truncate) or an ioctl op
9501ce260fSMickaël Salaün 			 * (hook_file_ioctl) rather than a path;
9601ce260fSMickaël Salaün 			 * FS_CHANGE_TOPOLOGY carries a path or a bare dentry.
9701ce260fSMickaël Salaün 			 * Reading the wrong union member would dereference
9801ce260fSMickaël Salaün 			 * garbage, so every reachable type is handled here.
9901ce260fSMickaël Salaün 			 */
10001ce260fSMickaël Salaün 			switch (request->audit.type) {
10101ce260fSMickaël Salaün 			case LSM_AUDIT_DATA_FILE:
10201ce260fSMickaël Salaün 				path = &request->audit.u.file->f_path;
10301ce260fSMickaël Salaün 				break;
10401ce260fSMickaël Salaün 			case LSM_AUDIT_DATA_IOCTL_OP:
10501ce260fSMickaël Salaün 				path = &request->audit.u.op->path;
10601ce260fSMickaël Salaün 				break;
10701ce260fSMickaël Salaün 			case LSM_AUDIT_DATA_DENTRY:
10801ce260fSMickaël Salaün 				/*
10901ce260fSMickaël Salaün 				 * Build a path on the stack with the real
11001ce260fSMickaël Salaün 				 * dentry so TP_fast_assign can extract dev and
11101ce260fSMickaël Salaün 				 * ino; the mnt field is unused there.
11201ce260fSMickaël Salaün 				 */
11301ce260fSMickaël Salaün 				dentry_path = (struct path){
11401ce260fSMickaël Salaün 					.dentry = request->audit.u.dentry,
11501ce260fSMickaël Salaün 				};
11601ce260fSMickaël Salaün 				path = &dentry_path;
11701ce260fSMickaël Salaün 				break;
11801ce260fSMickaël Salaün 			case LSM_AUDIT_DATA_PATH:
11901ce260fSMickaël Salaün 				path = &request->audit.u.path;
12001ce260fSMickaël Salaün 				break;
12101ce260fSMickaël Salaün 			default:
12201ce260fSMickaël Salaün 				WARN_ONCE(1,
12301ce260fSMickaël Salaün 					  "Unhandled Landlock FS audit type %d",
12401ce260fSMickaël Salaün 					  request->audit.type);
12501ce260fSMickaël Salaün 				break;
12601ce260fSMickaël Salaün 			}
12701ce260fSMickaël Salaün 
12801ce260fSMickaël Salaün 			if (!path)
12901ce260fSMickaël Salaün 				break;
13001ce260fSMickaël Salaün 
13101ce260fSMickaël Salaün 			if (!buf) {
13201ce260fSMickaël Salaün 				pathname = "<no_mem>";
13301ce260fSMickaël Salaün 			} else if (request->audit.type ==
13401ce260fSMickaël Salaün 				   LSM_AUDIT_DATA_DENTRY) {
13501ce260fSMickaël Salaün 				/* No vfsmount: render the dentry path alone. */
13601ce260fSMickaël Salaün 				pathname = dentry_path_raw(
13701ce260fSMickaël Salaün 					request->audit.u.dentry, buf, PATH_MAX);
13801ce260fSMickaël Salaün 				if (IS_ERR(pathname))
13901ce260fSMickaël Salaün 					pathname =
14001ce260fSMickaël Salaün 						PTR_ERR(pathname) ==
14101ce260fSMickaël Salaün 								-ENAMETOOLONG ?
14201ce260fSMickaël Salaün 							"<too_long>" :
14301ce260fSMickaël Salaün 							"<unreachable>";
14401ce260fSMickaël Salaün 			} else {
14501ce260fSMickaël Salaün 				pathname = resolve_path_for_trace(path, buf);
14601ce260fSMickaël Salaün 			}
14701ce260fSMickaël Salaün 
14801ce260fSMickaël Salaün 			trace_landlock_deny_access_fs(youngest_denied,
14901ce260fSMickaël Salaün 						      same_exec, logged,
15001ce260fSMickaël Salaün 						      missing, path, pathname);
15101ce260fSMickaël Salaün 		}
15201ce260fSMickaël Salaün 		break;
15301ce260fSMickaël Salaün 	case LANDLOCK_REQUEST_NET_ACCESS:
15401ce260fSMickaël Salaün 		if (trace_landlock_deny_access_net_enabled())
15501ce260fSMickaël Salaün 			trace_landlock_deny_access_net(
15601ce260fSMickaël Salaün 				youngest_denied, same_exec, logged, missing,
15701ce260fSMickaël Salaün 				request->audit.u.net->sk,
15801ce260fSMickaël Salaün 				ntohs(request->audit.u.net->sport),
15901ce260fSMickaël Salaün 				ntohs(request->audit.u.net->dport));
16001ce260fSMickaël Salaün 		break;
161bb91730fSMickaël Salaün 	case LANDLOCK_REQUEST_PTRACE:
162bb91730fSMickaël Salaün 		if (trace_landlock_deny_ptrace_enabled())
163bb91730fSMickaël Salaün 			trace_landlock_deny_ptrace(youngest_denied, same_exec,
164bb91730fSMickaël Salaün 						   logged,
165bb91730fSMickaël Salaün 						   request->other_domain_id,
166bb91730fSMickaël Salaün 						   request->audit.u.tsk);
167bb91730fSMickaël Salaün 		break;
168bb91730fSMickaël Salaün 	case LANDLOCK_REQUEST_SCOPE_SIGNAL:
169bb91730fSMickaël Salaün 		if (trace_landlock_deny_scope_signal_enabled())
170bb91730fSMickaël Salaün 			trace_landlock_deny_scope_signal(
171bb91730fSMickaël Salaün 				youngest_denied, same_exec, logged,
172bb91730fSMickaël Salaün 				request->other_domain_id, request->audit.u.tsk);
173bb91730fSMickaël Salaün 		break;
174bb91730fSMickaël Salaün 	case LANDLOCK_REQUEST_SCOPE_ABSTRACT_UNIX_SOCKET:
175bb91730fSMickaël Salaün 		if (trace_landlock_deny_scope_abstract_unix_socket_enabled())
176bb91730fSMickaël Salaün 			trace_landlock_deny_scope_abstract_unix_socket(
177bb91730fSMickaël Salaün 				youngest_denied, same_exec, logged,
178bb91730fSMickaël Salaün 				request->other_domain_id,
179bb91730fSMickaël Salaün 				request->audit.u.net->sk);
180bb91730fSMickaël Salaün 		break;
18101ce260fSMickaël Salaün 	default:
182bb91730fSMickaël Salaün 		WARN_ONCE(1, "Unhandled Landlock request type %d",
183bb91730fSMickaël Salaün 			  request->type);
18401ce260fSMickaël Salaün 		break;
18501ce260fSMickaël Salaün 	}
18601ce260fSMickaël Salaün }
187*d41d0021SMickaël Salaün 
188*d41d0021SMickaël Salaün #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST
189*d41d0021SMickaël Salaün 
190*d41d0021SMickaël Salaün static void test_trace_seq_init(struct trace_seq *const seq, const size_t size)
191*d41d0021SMickaël Salaün {
192*d41d0021SMickaël Salaün 	memset(seq, 0, sizeof(*seq));
193*d41d0021SMickaël Salaün 	seq_buf_init(&seq->seq, seq->buffer, size);
194*d41d0021SMickaël Salaün }
195*d41d0021SMickaël Salaün 
196*d41d0021SMickaël Salaün static void test_untrusted_str_data(struct kunit *const test)
197*d41d0021SMickaël Salaün {
198*d41d0021SMickaël Salaün 	const char binary[] = { 'a', '\0', '<' };
199*d41d0021SMickaël Salaün 	static const char ellipsis[] = "\xe2\x80\xa6";
200*d41d0021SMickaël Salaün 	struct trace_seq *const seq =
201*d41d0021SMickaël Salaün 		kunit_kzalloc(test, sizeof(*seq), GFP_KERNEL);
202*d41d0021SMickaël Salaün 	const char *output;
203*d41d0021SMickaël Salaün 
204*d41d0021SMickaël Salaün 	KUNIT_ASSERT_NOT_NULL(test, seq);
205*d41d0021SMickaël Salaün 	test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE);
206*d41d0021SMickaël Salaün 	output = __trace_print_untrusted_str(seq, "<too_long>", 10);
207*d41d0021SMickaël Salaün 	KUNIT_ASSERT_NOT_NULL(test, output);
208*d41d0021SMickaël Salaün 	KUNIT_EXPECT_STREQ(test, output, "<too_long>");
209*d41d0021SMickaël Salaün 
210*d41d0021SMickaël Salaün 	test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE);
211*d41d0021SMickaël Salaün 	output = __trace_print_untrusted_str(seq, binary, sizeof(binary));
212*d41d0021SMickaël Salaün 	KUNIT_ASSERT_NOT_NULL(test, output);
213*d41d0021SMickaël Salaün 	KUNIT_EXPECT_STREQ(test, output, "a\\000<");
214*d41d0021SMickaël Salaün 
215*d41d0021SMickaël Salaün 	/* Input ellipsis bytes are escaped and cannot mimic the raw marker. */
216*d41d0021SMickaël Salaün 	test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE);
217*d41d0021SMickaël Salaün 	output = __trace_print_untrusted_str(seq, ellipsis,
218*d41d0021SMickaël Salaün 					     sizeof(ellipsis) - 1);
219*d41d0021SMickaël Salaün 	KUNIT_ASSERT_NOT_NULL(test, output);
220*d41d0021SMickaël Salaün 	KUNIT_EXPECT_STREQ(test, output, "\\342\\200\\246");
221*d41d0021SMickaël Salaün }
222*d41d0021SMickaël Salaün 
223*d41d0021SMickaël Salaün static void test_untrusted_str_boundaries(struct kunit *const test)
224*d41d0021SMickaël Salaün {
225*d41d0021SMickaël Salaün 	static const char escaped_space[] = "\\040";
226*d41d0021SMickaël Salaün 	const size_t output_size = TRACE_UNTRUSTED_STR_OUTPUT_SIZE;
227*d41d0021SMickaël Salaün 	const size_t marker_len = sizeof(TRACE_TRUNCATION_MARKER) - 1;
228*d41d0021SMickaël Salaün 	const size_t escape_len = sizeof(escaped_space) - 1;
229*d41d0021SMickaël Salaün 	const size_t exact_prefix_len =
230*d41d0021SMickaël Salaün 		output_size - marker_len - 1 - escape_len;
231*d41d0021SMickaël Salaün 	const size_t short_prefix_len = exact_prefix_len + 1;
232*d41d0021SMickaël Salaün 	struct trace_seq *const seq =
233*d41d0021SMickaël Salaün 		kunit_kzalloc(test, sizeof(*seq), GFP_KERNEL);
234*d41d0021SMickaël Salaün 	char *const input = kunit_kmalloc(test, output_size + 1, GFP_KERNEL);
235*d41d0021SMickaël Salaün 	char *const expected = kunit_kmalloc(test, output_size, GFP_KERNEL);
236*d41d0021SMickaël Salaün 	const char *output;
237*d41d0021SMickaël Salaün 
238*d41d0021SMickaël Salaün 	KUNIT_ASSERT_NOT_NULL(test, seq);
239*d41d0021SMickaël Salaün 	KUNIT_ASSERT_NOT_NULL(test, input);
240*d41d0021SMickaël Salaün 	KUNIT_ASSERT_NOT_NULL(test, expected);
241*d41d0021SMickaël Salaün 
242*d41d0021SMickaël Salaün 	/* The escaped string and its trailing NUL exactly fit the limit. */
243*d41d0021SMickaël Salaün 	memset(input, 'a', output_size - 1);
244*d41d0021SMickaël Salaün 	test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE);
245*d41d0021SMickaël Salaün 	output = __trace_print_untrusted_str(seq, input, output_size - 1);
246*d41d0021SMickaël Salaün 	KUNIT_ASSERT_NOT_NULL(test, output);
247*d41d0021SMickaël Salaün 	KUNIT_EXPECT_EQ(test, seq->seq.len, output_size);
248*d41d0021SMickaël Salaün 	KUNIT_EXPECT_EQ(test, memcmp(output, input, output_size - 1), 0);
249*d41d0021SMickaël Salaün 
250*d41d0021SMickaël Salaün 	/* Stop before a four-byte escape when only three bytes remain. */
251*d41d0021SMickaël Salaün 	memset(input, 'a', short_prefix_len);
252*d41d0021SMickaël Salaün 	input[short_prefix_len] = ' ';
253*d41d0021SMickaël Salaün 	memset(input + short_prefix_len + 1, 'b', 5);
254*d41d0021SMickaël Salaün 	memset(expected, 'a', short_prefix_len);
255*d41d0021SMickaël Salaün 	memcpy(expected + short_prefix_len, TRACE_TRUNCATION_MARKER,
256*d41d0021SMickaël Salaün 	       marker_len + 1);
257*d41d0021SMickaël Salaün 	test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE);
258*d41d0021SMickaël Salaün 	output = __trace_print_untrusted_str(seq, input, short_prefix_len + 6);
259*d41d0021SMickaël Salaün 	KUNIT_ASSERT_NOT_NULL(test, output);
260*d41d0021SMickaël Salaün 	KUNIT_EXPECT_STREQ(test, output, expected);
261*d41d0021SMickaël Salaün 
262*d41d0021SMickaël Salaün 	/* Include a four-byte escape that exactly fills the prefix capacity. */
263*d41d0021SMickaël Salaün 	memset(input, 'a', exact_prefix_len);
264*d41d0021SMickaël Salaün 	input[exact_prefix_len] = ' ';
265*d41d0021SMickaël Salaün 	memset(input + exact_prefix_len + 1, 'b', marker_len + 1);
266*d41d0021SMickaël Salaün 	memset(expected, 'a', exact_prefix_len);
267*d41d0021SMickaël Salaün 	memcpy(expected + exact_prefix_len, escaped_space, escape_len);
268*d41d0021SMickaël Salaün 	memcpy(expected + exact_prefix_len + escape_len,
269*d41d0021SMickaël Salaün 	       TRACE_TRUNCATION_MARKER, marker_len + 1);
270*d41d0021SMickaël Salaün 	test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE);
271*d41d0021SMickaël Salaün 	output = __trace_print_untrusted_str(seq, input,
272*d41d0021SMickaël Salaün 					     exact_prefix_len + marker_len + 2);
273*d41d0021SMickaël Salaün 	KUNIT_ASSERT_NOT_NULL(test, output);
274*d41d0021SMickaël Salaün 	KUNIT_EXPECT_STREQ(test, output, expected);
275*d41d0021SMickaël Salaün 
276*d41d0021SMickaël Salaün 	/* Literal backslashes remain escaped in complete output. */
277*d41d0021SMickaël Salaün 	test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE);
278*d41d0021SMickaël Salaün 	output = __trace_print_untrusted_str(seq, "/\\000", 5);
279*d41d0021SMickaël Salaün 	KUNIT_ASSERT_NOT_NULL(test, output);
280*d41d0021SMickaël Salaün 	KUNIT_EXPECT_STREQ(test, output, "/\\\\000");
281*d41d0021SMickaël Salaün }
282*d41d0021SMickaël Salaün 
283*d41d0021SMickaël Salaün static void test_untrusted_str_cursor(struct kunit *const test)
284*d41d0021SMickaël Salaün {
285*d41d0021SMickaël Salaün 	const size_t padding_len =
286*d41d0021SMickaël Salaün 		TRACE_SEQ_BUFFER_SIZE - TRACE_UNTRUSTED_STR_OUTPUT_SIZE + 1;
287*d41d0021SMickaël Salaün 	struct trace_seq *const seq =
288*d41d0021SMickaël Salaün 		kunit_kzalloc(test, sizeof(*seq), GFP_KERNEL);
289*d41d0021SMickaël Salaün 	char *const padding = kunit_kzalloc(test, padding_len, GFP_KERNEL);
290*d41d0021SMickaël Salaün 	const char *output;
291*d41d0021SMickaël Salaün 
292*d41d0021SMickaël Salaün 	KUNIT_ASSERT_NOT_NULL(test, seq);
293*d41d0021SMickaël Salaün 	KUNIT_ASSERT_NOT_NULL(test, padding);
294*d41d0021SMickaël Salaün 
295*d41d0021SMickaël Salaün 	/* Accept available space exactly equal to the fixed reservation. */
296*d41d0021SMickaël Salaün 	test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE);
297*d41d0021SMickaël Salaün 	trace_seq_putmem(seq, padding, padding_len - 1);
298*d41d0021SMickaël Salaün 	output = __trace_print_untrusted_str(seq, "/a", 2);
299*d41d0021SMickaël Salaün 	KUNIT_ASSERT_NOT_NULL(test, output);
300*d41d0021SMickaël Salaün 	KUNIT_EXPECT_STREQ(test, output, "/a");
301*d41d0021SMickaël Salaün 	KUNIT_EXPECT_EQ(test, seq->seq.len, padding_len - 1 + sizeof("/a"));
302*d41d0021SMickaël Salaün 
303*d41d0021SMickaël Salaün 	/* Reject one byte less without changing the scratch cursor. */
304*d41d0021SMickaël Salaün 	test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE);
305*d41d0021SMickaël Salaün 	trace_seq_putmem(seq, padding, padding_len);
306*d41d0021SMickaël Salaün 	output = __trace_print_untrusted_str(seq, "/a", 2);
307*d41d0021SMickaël Salaün 	KUNIT_EXPECT_NULL(test, output);
308*d41d0021SMickaël Salaün 	KUNIT_EXPECT_EQ(test, seq->seq.len, padding_len);
309*d41d0021SMickaël Salaün }
310*d41d0021SMickaël Salaün 
311*d41d0021SMickaël Salaün static void test_untrusted_str_composition(struct kunit *const test)
312*d41d0021SMickaël Salaün {
313*d41d0021SMickaël Salaün 	static const struct trace_print_flags flags[] = {
314*d41d0021SMickaël Salaün 		{ .mask = 1, .name = "read" },
315*d41d0021SMickaël Salaün 	};
316*d41d0021SMickaël Salaün 	const size_t output_size = TRACE_UNTRUSTED_STR_OUTPUT_SIZE;
317*d41d0021SMickaël Salaün 	const size_t prefix_len = output_size - sizeof(TRACE_TRUNCATION_MARKER);
318*d41d0021SMickaël Salaün 	struct trace_seq *const seq =
319*d41d0021SMickaël Salaün 		kunit_kzalloc(test, sizeof(*seq), GFP_KERNEL);
320*d41d0021SMickaël Salaün 	char *const expected = kunit_kmalloc(test, output_size, GFP_KERNEL);
321*d41d0021SMickaël Salaün 	char *const path = kunit_kmalloc(test, output_size, GFP_KERNEL);
322*d41d0021SMickaël Salaün 	const char *flags_output, *path_output;
323*d41d0021SMickaël Salaün 
324*d41d0021SMickaël Salaün 	KUNIT_ASSERT_NOT_NULL(test, seq);
325*d41d0021SMickaël Salaün 	KUNIT_ASSERT_NOT_NULL(test, expected);
326*d41d0021SMickaël Salaün 	KUNIT_ASSERT_NOT_NULL(test, path);
327*d41d0021SMickaël Salaün 	memset(path, 'a', output_size);
328*d41d0021SMickaël Salaün 	memset(expected, 'a', prefix_len);
329*d41d0021SMickaël Salaün 	memcpy(expected + prefix_len, TRACE_TRUNCATION_MARKER,
330*d41d0021SMickaël Salaün 	       sizeof(TRACE_TRUNCATION_MARKER));
331*d41d0021SMickaël Salaün 
332*d41d0021SMickaël Salaün 	/* Exercise both legal TP_printk() sibling evaluation orders. */
333*d41d0021SMickaël Salaün 	test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE);
334*d41d0021SMickaël Salaün 	path_output = __trace_print_untrusted_str(seq, path, output_size);
335*d41d0021SMickaël Salaün 	flags_output =
336*d41d0021SMickaël Salaün 		trace_print_flags_seq(seq, "|", 1, flags, ARRAY_SIZE(flags));
337*d41d0021SMickaël Salaün 	KUNIT_ASSERT_NOT_NULL(test, path_output);
338*d41d0021SMickaël Salaün 	KUNIT_EXPECT_STREQ(test, path_output, expected);
339*d41d0021SMickaël Salaün 	KUNIT_EXPECT_STREQ(test, flags_output, "read");
340*d41d0021SMickaël Salaün 
341*d41d0021SMickaël Salaün 	test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE);
342*d41d0021SMickaël Salaün 	flags_output =
343*d41d0021SMickaël Salaün 		trace_print_flags_seq(seq, "|", 1, flags, ARRAY_SIZE(flags));
344*d41d0021SMickaël Salaün 	path_output = __trace_print_untrusted_str(seq, path, output_size);
345*d41d0021SMickaël Salaün 	KUNIT_ASSERT_NOT_NULL(test, path_output);
346*d41d0021SMickaël Salaün 	KUNIT_EXPECT_STREQ(test, path_output, expected);
347*d41d0021SMickaël Salaün 	KUNIT_EXPECT_STREQ(test, flags_output, "read");
348*d41d0021SMickaël Salaün }
349*d41d0021SMickaël Salaün 
350*d41d0021SMickaël Salaün static struct kunit_case test_cases[] = {
351*d41d0021SMickaël Salaün 	/* clang-format off */
352*d41d0021SMickaël Salaün 	KUNIT_CASE(test_untrusted_str_data),
353*d41d0021SMickaël Salaün 	KUNIT_CASE(test_untrusted_str_boundaries),
354*d41d0021SMickaël Salaün 	KUNIT_CASE(test_untrusted_str_cursor),
355*d41d0021SMickaël Salaün 	KUNIT_CASE(test_untrusted_str_composition),
356*d41d0021SMickaël Salaün 	{}
357*d41d0021SMickaël Salaün 	/* clang-format on */
358*d41d0021SMickaël Salaün };
359*d41d0021SMickaël Salaün 
360*d41d0021SMickaël Salaün static struct kunit_suite test_suite = {
361*d41d0021SMickaël Salaün 	.name = "landlock_trace",
362*d41d0021SMickaël Salaün 	.test_cases = test_cases,
363*d41d0021SMickaël Salaün };
364*d41d0021SMickaël Salaün 
365*d41d0021SMickaël Salaün kunit_test_suite(test_suite);
366*d41d0021SMickaël Salaün 
367*d41d0021SMickaël Salaün #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */
368