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