1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Landlock - Tracepoint helpers 4 * 5 * Copyright © 2025 Microsoft Corporation 6 * Copyright © 2026 Cloudflare, Inc. 7 */ 8 9 #include <kunit/test.h> 10 #include <linux/cleanup.h> 11 #include <linux/dcache.h> 12 #include <linux/err.h> 13 #include <linux/fs.h> 14 #include <linux/lsm_audit.h> 15 #include <net/sock.h> 16 17 #include "access.h" 18 #include "domain.h" 19 #include "fs.h" 20 #include "log.h" 21 #include "ruleset.h" 22 #include "trace.h" 23 24 /* 25 * Generates the tracepoint definitions in this translation unit. The trace 26 * event header dereferences the traced objects in TP_fast_assign, so the full 27 * struct definitions (e.g. ruleset.h, domain.h) must be included before it. 28 */ 29 #define CREATE_TRACE_POINTS 30 #include <trace/events/landlock.h> 31 32 /** 33 * landlock_trace_free_domain - Emit a tracepoint on domain deallocation 34 * 35 * @hierarchy: The domain's hierarchy being deallocated. 36 * 37 * Fires only for a hierarchy whose creation event was emitted, i.e. one that 38 * left LANDLOCK_LOG_UNCOMMITTED in landlock_restrict_self(). This keeps the 39 * create/free pair balanced: a hierarchy that never became observable is freed 40 * silently, while a domain that landlock_restrict_self() created and a 41 * thread-sync failure then aborted still fires free_domain, because its 42 * creation event already fired. 43 * 44 * Called from landlock_log_free_domain(). 45 */ 46 void landlock_trace_free_domain(const struct landlock_hierarchy *const hierarchy) 47 { 48 /* 49 * The log_status read is a correctness guard (keep the create/free pair 50 * balanced), not a cost guard, so this cold path needs no 51 * trace_..._enabled() check: the tracepoint is a static-branch no-op 52 * when disabled. The denial path guards trace_..._enabled() instead 53 * because it does expensive __getname()/path work before emitting. 54 */ 55 if (READ_ONCE(hierarchy->log_status) != LANDLOCK_LOG_UNCOMMITTED) 56 trace_landlock_free_domain(hierarchy); 57 } 58 59 /** 60 * landlock_trace_denial - Emit a tracepoint for a denied access request 61 * 62 * @request: Detail of the user space request. 63 * @youngest_denied: The youngest hierarchy node that denied the access. 64 * @missing: The set of denied access rights. 65 * @same_exec: Whether the current task is the same executable that called 66 * landlock_restrict_self() for the denying domain, as computed 67 * by landlock_log_denial(). 68 * @logged: Whether the domain's policy selects this denial for logging, as 69 * computed by landlock_log_denial(). 70 * 71 * Emits the tracepoint matching @request->type when its event is enabled. 72 * Unlike audit, fires regardless of @logged; the value is recorded in the event 73 * so consumers can filter on it. 74 * 75 * Called from landlock_log_denial(). 76 */ 77 void landlock_trace_denial( 78 const struct landlock_request *const request, 79 const struct landlock_hierarchy *const youngest_denied, 80 const access_mask_t missing, const bool same_exec, const bool logged) 81 { 82 switch (request->type) { 83 case LANDLOCK_REQUEST_FS_ACCESS: 84 case LANDLOCK_REQUEST_FS_CHANGE_TOPOLOGY: 85 if (trace_landlock_deny_access_fs_enabled()) { 86 char *buf __free(__putname) = __getname(); 87 struct path dentry_path; 88 const char *pathname; 89 const struct path *path = NULL; 90 91 /* 92 * Selects the path from the audit data type, as 93 * dump_common_audit_data() does. A FS_ACCESS denial 94 * carries a file (hook_file_truncate) or an ioctl op 95 * (hook_file_ioctl) rather than a path; 96 * FS_CHANGE_TOPOLOGY carries a path or a bare dentry. 97 * Reading the wrong union member would dereference 98 * garbage, so every reachable type is handled here. 99 */ 100 switch (request->audit.type) { 101 case LSM_AUDIT_DATA_FILE: 102 path = &request->audit.u.file->f_path; 103 break; 104 case LSM_AUDIT_DATA_IOCTL_OP: 105 path = &request->audit.u.op->path; 106 break; 107 case LSM_AUDIT_DATA_DENTRY: 108 /* 109 * Build a path on the stack with the real 110 * dentry so TP_fast_assign can extract dev and 111 * ino; the mnt field is unused there. 112 */ 113 dentry_path = (struct path){ 114 .dentry = request->audit.u.dentry, 115 }; 116 path = &dentry_path; 117 break; 118 case LSM_AUDIT_DATA_PATH: 119 path = &request->audit.u.path; 120 break; 121 default: 122 WARN_ONCE(1, 123 "Unhandled Landlock FS audit type %d", 124 request->audit.type); 125 break; 126 } 127 128 if (!path) 129 break; 130 131 if (!buf) { 132 pathname = "<no_mem>"; 133 } else if (request->audit.type == 134 LSM_AUDIT_DATA_DENTRY) { 135 /* No vfsmount: render the dentry path alone. */ 136 pathname = dentry_path_raw( 137 request->audit.u.dentry, buf, PATH_MAX); 138 if (IS_ERR(pathname)) 139 pathname = 140 PTR_ERR(pathname) == 141 -ENAMETOOLONG ? 142 "<too_long>" : 143 "<unreachable>"; 144 } else { 145 pathname = resolve_path_for_trace(path, buf); 146 } 147 148 trace_landlock_deny_access_fs(youngest_denied, 149 same_exec, logged, 150 missing, path, pathname); 151 } 152 break; 153 case LANDLOCK_REQUEST_NET_ACCESS: 154 if (trace_landlock_deny_access_net_enabled()) 155 trace_landlock_deny_access_net( 156 youngest_denied, same_exec, logged, missing, 157 request->audit.u.net->sk, 158 ntohs(request->audit.u.net->sport), 159 ntohs(request->audit.u.net->dport)); 160 break; 161 case LANDLOCK_REQUEST_PTRACE: 162 if (trace_landlock_deny_ptrace_enabled()) 163 trace_landlock_deny_ptrace(youngest_denied, same_exec, 164 logged, 165 request->other_domain_id, 166 request->audit.u.tsk); 167 break; 168 case LANDLOCK_REQUEST_SCOPE_SIGNAL: 169 if (trace_landlock_deny_scope_signal_enabled()) 170 trace_landlock_deny_scope_signal( 171 youngest_denied, same_exec, logged, 172 request->other_domain_id, request->audit.u.tsk); 173 break; 174 case LANDLOCK_REQUEST_SCOPE_ABSTRACT_UNIX_SOCKET: 175 if (trace_landlock_deny_scope_abstract_unix_socket_enabled()) 176 trace_landlock_deny_scope_abstract_unix_socket( 177 youngest_denied, same_exec, logged, 178 request->other_domain_id, 179 request->audit.u.net->sk); 180 break; 181 default: 182 WARN_ONCE(1, "Unhandled Landlock request type %d", 183 request->type); 184 break; 185 } 186 } 187 188 #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST 189 190 static void test_trace_seq_init(struct trace_seq *const seq, const size_t size) 191 { 192 memset(seq, 0, sizeof(*seq)); 193 seq_buf_init(&seq->seq, seq->buffer, size); 194 } 195 196 static void test_untrusted_str_data(struct kunit *const test) 197 { 198 const char binary[] = { 'a', '\0', '<' }; 199 static const char ellipsis[] = "\xe2\x80\xa6"; 200 struct trace_seq *const seq = 201 kunit_kzalloc(test, sizeof(*seq), GFP_KERNEL); 202 const char *output; 203 204 KUNIT_ASSERT_NOT_NULL(test, seq); 205 test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE); 206 output = __trace_print_untrusted_str(seq, "<too_long>", 10); 207 KUNIT_ASSERT_NOT_NULL(test, output); 208 KUNIT_EXPECT_STREQ(test, output, "<too_long>"); 209 210 test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE); 211 output = __trace_print_untrusted_str(seq, binary, sizeof(binary)); 212 KUNIT_ASSERT_NOT_NULL(test, output); 213 KUNIT_EXPECT_STREQ(test, output, "a\\000<"); 214 215 /* Input ellipsis bytes are escaped and cannot mimic the raw marker. */ 216 test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE); 217 output = __trace_print_untrusted_str(seq, ellipsis, 218 sizeof(ellipsis) - 1); 219 KUNIT_ASSERT_NOT_NULL(test, output); 220 KUNIT_EXPECT_STREQ(test, output, "\\342\\200\\246"); 221 } 222 223 static void test_untrusted_str_boundaries(struct kunit *const test) 224 { 225 static const char escaped_space[] = "\\040"; 226 const size_t output_size = TRACE_UNTRUSTED_STR_OUTPUT_SIZE; 227 const size_t marker_len = sizeof(TRACE_TRUNCATION_MARKER) - 1; 228 const size_t escape_len = sizeof(escaped_space) - 1; 229 const size_t exact_prefix_len = 230 output_size - marker_len - 1 - escape_len; 231 const size_t short_prefix_len = exact_prefix_len + 1; 232 struct trace_seq *const seq = 233 kunit_kzalloc(test, sizeof(*seq), GFP_KERNEL); 234 char *const input = kunit_kmalloc(test, output_size + 1, GFP_KERNEL); 235 char *const expected = kunit_kmalloc(test, output_size, GFP_KERNEL); 236 const char *output; 237 238 KUNIT_ASSERT_NOT_NULL(test, seq); 239 KUNIT_ASSERT_NOT_NULL(test, input); 240 KUNIT_ASSERT_NOT_NULL(test, expected); 241 242 /* The escaped string and its trailing NUL exactly fit the limit. */ 243 memset(input, 'a', output_size - 1); 244 test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE); 245 output = __trace_print_untrusted_str(seq, input, output_size - 1); 246 KUNIT_ASSERT_NOT_NULL(test, output); 247 KUNIT_EXPECT_EQ(test, seq->seq.len, output_size); 248 KUNIT_EXPECT_EQ(test, memcmp(output, input, output_size - 1), 0); 249 250 /* Stop before a four-byte escape when only three bytes remain. */ 251 memset(input, 'a', short_prefix_len); 252 input[short_prefix_len] = ' '; 253 memset(input + short_prefix_len + 1, 'b', 5); 254 memset(expected, 'a', short_prefix_len); 255 memcpy(expected + short_prefix_len, TRACE_TRUNCATION_MARKER, 256 marker_len + 1); 257 test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE); 258 output = __trace_print_untrusted_str(seq, input, short_prefix_len + 6); 259 KUNIT_ASSERT_NOT_NULL(test, output); 260 KUNIT_EXPECT_STREQ(test, output, expected); 261 262 /* Include a four-byte escape that exactly fills the prefix capacity. */ 263 memset(input, 'a', exact_prefix_len); 264 input[exact_prefix_len] = ' '; 265 memset(input + exact_prefix_len + 1, 'b', marker_len + 1); 266 memset(expected, 'a', exact_prefix_len); 267 memcpy(expected + exact_prefix_len, escaped_space, escape_len); 268 memcpy(expected + exact_prefix_len + escape_len, 269 TRACE_TRUNCATION_MARKER, marker_len + 1); 270 test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE); 271 output = __trace_print_untrusted_str(seq, input, 272 exact_prefix_len + marker_len + 2); 273 KUNIT_ASSERT_NOT_NULL(test, output); 274 KUNIT_EXPECT_STREQ(test, output, expected); 275 276 /* Literal backslashes remain escaped in complete output. */ 277 test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE); 278 output = __trace_print_untrusted_str(seq, "/\\000", 5); 279 KUNIT_ASSERT_NOT_NULL(test, output); 280 KUNIT_EXPECT_STREQ(test, output, "/\\\\000"); 281 } 282 283 static void test_untrusted_str_cursor(struct kunit *const test) 284 { 285 const size_t padding_len = 286 TRACE_SEQ_BUFFER_SIZE - TRACE_UNTRUSTED_STR_OUTPUT_SIZE + 1; 287 struct trace_seq *const seq = 288 kunit_kzalloc(test, sizeof(*seq), GFP_KERNEL); 289 char *const padding = kunit_kzalloc(test, padding_len, GFP_KERNEL); 290 const char *output; 291 292 KUNIT_ASSERT_NOT_NULL(test, seq); 293 KUNIT_ASSERT_NOT_NULL(test, padding); 294 295 /* Accept available space exactly equal to the fixed reservation. */ 296 test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE); 297 trace_seq_putmem(seq, padding, padding_len - 1); 298 output = __trace_print_untrusted_str(seq, "/a", 2); 299 KUNIT_ASSERT_NOT_NULL(test, output); 300 KUNIT_EXPECT_STREQ(test, output, "/a"); 301 KUNIT_EXPECT_EQ(test, seq->seq.len, padding_len - 1 + sizeof("/a")); 302 303 /* Reject one byte less without changing the scratch cursor. */ 304 test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE); 305 trace_seq_putmem(seq, padding, padding_len); 306 output = __trace_print_untrusted_str(seq, "/a", 2); 307 KUNIT_EXPECT_NULL(test, output); 308 KUNIT_EXPECT_EQ(test, seq->seq.len, padding_len); 309 } 310 311 static void test_untrusted_str_composition(struct kunit *const test) 312 { 313 static const struct trace_print_flags flags[] = { 314 { .mask = 1, .name = "read" }, 315 }; 316 const size_t output_size = TRACE_UNTRUSTED_STR_OUTPUT_SIZE; 317 const size_t prefix_len = output_size - sizeof(TRACE_TRUNCATION_MARKER); 318 struct trace_seq *const seq = 319 kunit_kzalloc(test, sizeof(*seq), GFP_KERNEL); 320 char *const expected = kunit_kmalloc(test, output_size, GFP_KERNEL); 321 char *const path = kunit_kmalloc(test, output_size, GFP_KERNEL); 322 const char *flags_output, *path_output; 323 324 KUNIT_ASSERT_NOT_NULL(test, seq); 325 KUNIT_ASSERT_NOT_NULL(test, expected); 326 KUNIT_ASSERT_NOT_NULL(test, path); 327 memset(path, 'a', output_size); 328 memset(expected, 'a', prefix_len); 329 memcpy(expected + prefix_len, TRACE_TRUNCATION_MARKER, 330 sizeof(TRACE_TRUNCATION_MARKER)); 331 332 /* Exercise both legal TP_printk() sibling evaluation orders. */ 333 test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE); 334 path_output = __trace_print_untrusted_str(seq, path, output_size); 335 flags_output = 336 trace_print_flags_seq(seq, "|", 1, flags, ARRAY_SIZE(flags)); 337 KUNIT_ASSERT_NOT_NULL(test, path_output); 338 KUNIT_EXPECT_STREQ(test, path_output, expected); 339 KUNIT_EXPECT_STREQ(test, flags_output, "read"); 340 341 test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE); 342 flags_output = 343 trace_print_flags_seq(seq, "|", 1, flags, ARRAY_SIZE(flags)); 344 path_output = __trace_print_untrusted_str(seq, path, output_size); 345 KUNIT_ASSERT_NOT_NULL(test, path_output); 346 KUNIT_EXPECT_STREQ(test, path_output, expected); 347 KUNIT_EXPECT_STREQ(test, flags_output, "read"); 348 } 349 350 static struct kunit_case test_cases[] = { 351 /* clang-format off */ 352 KUNIT_CASE(test_untrusted_str_data), 353 KUNIT_CASE(test_untrusted_str_boundaries), 354 KUNIT_CASE(test_untrusted_str_cursor), 355 KUNIT_CASE(test_untrusted_str_composition), 356 {} 357 /* clang-format on */ 358 }; 359 360 static struct kunit_suite test_suite = { 361 .name = "landlock_trace", 362 .test_cases = test_cases, 363 }; 364 365 kunit_test_suite(test_suite); 366 367 #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ 368