Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag 'landlock-6.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux

Pull landlock updates from Mickaël Salaün:
"This mostly factors out some Landlock code and prepares for upcoming
audit support.

Because files with invalid modes might be visible after filesystem
corruption, Landlock now handles those weird files too.

A few sample and test issues are also fixed"

* tag 'landlock-6.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux:
selftests/landlock: Add layout1.umount_sandboxer tests
selftests/landlock: Add wrappers.h
selftests/landlock: Fix error message
landlock: Optimize file path walks and prepare for audit support
selftests/landlock: Add test to check partial access in a mount tree
landlock: Align partial refer access checks with final ones
landlock: Simplify initially denied access rights
landlock: Move access types
landlock: Factor out check_access_path()
selftests/landlock: Fix build with non-default pthread linking
landlock: Use scoped guards for ruleset in landlock_add_rule()
landlock: Use scoped guards for ruleset
landlock: Constify get_mode_access()
landlock: Handle weird files
samples/landlock: Fix possible NULL dereference in parse_path()
selftests/landlock: Remove unused macros in ptrace_test.c

+489 -195
+7
samples/landlock/sandboxer.c
··· 91 91 } 92 92 } 93 93 *path_list = malloc(num_paths * sizeof(**path_list)); 94 + if (!*path_list) 95 + return -1; 96 + 94 97 for (i = 0; i < num_paths; i++) 95 98 (*path_list)[i] = strsep(&env_path, ENV_DELIMITER); 96 99 ··· 130 127 env_path_name = strdup(env_path_name); 131 128 unsetenv(env_var); 132 129 num_paths = parse_path(env_path_name, &path_list); 130 + if (num_paths < 0) { 131 + fprintf(stderr, "Failed to allocate memory\n"); 132 + goto out_free_name; 133 + } 133 134 if (num_paths == 1 && path_list[0][0] == '\0') { 134 135 /* 135 136 * Allows to not use all possible restrictions (e.g. use
+77
security/landlock/access.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Landlock LSM - Access types and helpers 4 + * 5 + * Copyright © 2016-2020 Mickaël Salaün <mic@digikod.net> 6 + * Copyright © 2018-2020 ANSSI 7 + * Copyright © 2024-2025 Microsoft Corporation 8 + */ 9 + 10 + #ifndef _SECURITY_LANDLOCK_ACCESS_H 11 + #define _SECURITY_LANDLOCK_ACCESS_H 12 + 13 + #include <linux/bitops.h> 14 + #include <linux/build_bug.h> 15 + #include <linux/kernel.h> 16 + #include <uapi/linux/landlock.h> 17 + 18 + #include "limits.h" 19 + 20 + /* 21 + * All access rights that are denied by default whether they are handled or not 22 + * by a ruleset/layer. This must be ORed with all ruleset->access_masks[] 23 + * entries when we need to get the absolute handled access masks, see 24 + * landlock_upgrade_handled_access_masks(). 25 + */ 26 + /* clang-format off */ 27 + #define _LANDLOCK_ACCESS_FS_INITIALLY_DENIED ( \ 28 + LANDLOCK_ACCESS_FS_REFER) 29 + /* clang-format on */ 30 + 31 + typedef u16 access_mask_t; 32 + 33 + /* Makes sure all filesystem access rights can be stored. */ 34 + static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_FS); 35 + /* Makes sure all network access rights can be stored. */ 36 + static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_NET); 37 + /* Makes sure all scoped rights can be stored. */ 38 + static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_SCOPE); 39 + /* Makes sure for_each_set_bit() and for_each_clear_bit() calls are OK. */ 40 + static_assert(sizeof(unsigned long) >= sizeof(access_mask_t)); 41 + 42 + /* Ruleset access masks. */ 43 + struct access_masks { 44 + access_mask_t fs : LANDLOCK_NUM_ACCESS_FS; 45 + access_mask_t net : LANDLOCK_NUM_ACCESS_NET; 46 + access_mask_t scope : LANDLOCK_NUM_SCOPE; 47 + }; 48 + 49 + union access_masks_all { 50 + struct access_masks masks; 51 + u32 all; 52 + }; 53 + 54 + /* Makes sure all fields are covered. */ 55 + static_assert(sizeof(typeof_member(union access_masks_all, masks)) == 56 + sizeof(typeof_member(union access_masks_all, all))); 57 + 58 + typedef u16 layer_mask_t; 59 + 60 + /* Makes sure all layers can be checked. */ 61 + static_assert(BITS_PER_TYPE(layer_mask_t) >= LANDLOCK_MAX_NUM_LAYERS); 62 + 63 + /* Upgrades with all initially denied by default access rights. */ 64 + static inline struct access_masks 65 + landlock_upgrade_handled_access_masks(struct access_masks access_masks) 66 + { 67 + /* 68 + * All access rights that are denied by default whether they are 69 + * explicitly handled or not. 70 + */ 71 + if (access_masks.fs) 72 + access_masks.fs |= _LANDLOCK_ACCESS_FS_INITIALLY_DENIED; 73 + 74 + return access_masks; 75 + } 76 + 77 + #endif /* _SECURITY_LANDLOCK_ACCESS_H */
+59 -55
security/landlock/fs.c
··· 36 36 #include <uapi/linux/fiemap.h> 37 37 #include <uapi/linux/landlock.h> 38 38 39 + #include "access.h" 39 40 #include "common.h" 40 41 #include "cred.h" 41 42 #include "fs.h" ··· 389 388 unlikely(IS_PRIVATE(d_backing_inode(dentry)))); 390 389 } 391 390 392 - static access_mask_t 393 - get_handled_fs_accesses(const struct landlock_ruleset *const domain) 394 - { 395 - /* Handles all initially denied by default access rights. */ 396 - return landlock_union_access_masks(domain).fs | 397 - LANDLOCK_ACCESS_FS_INITIALLY_DENIED; 398 - } 399 - 400 391 static const struct access_masks any_fs = { 401 392 .fs = ~0, 402 393 }; ··· 565 572 #undef NMA_TRUE 566 573 #undef NMA_FALSE 567 574 575 + static bool is_layer_masks_allowed( 576 + layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS]) 577 + { 578 + return !memchr_inv(layer_masks, 0, sizeof(*layer_masks)); 579 + } 580 + 568 581 /* 569 582 * Removes @layer_masks accesses that are not requested. 570 583 * ··· 588 589 589 590 for_each_clear_bit(access_bit, &access_req, ARRAY_SIZE(*layer_masks)) 590 591 (*layer_masks)[access_bit] = 0; 591 - return !memchr_inv(layer_masks, 0, sizeof(*layer_masks)); 592 + 593 + return is_layer_masks_allowed(layer_masks); 592 594 } 593 595 594 596 #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST ··· 778 778 if (WARN_ON_ONCE(domain->num_layers < 1 || !layer_masks_parent1)) 779 779 return false; 780 780 781 + allowed_parent1 = is_layer_masks_allowed(layer_masks_parent1); 782 + 781 783 if (unlikely(layer_masks_parent2)) { 782 784 if (WARN_ON_ONCE(!dentry_child1)) 783 785 return false; 786 + 787 + allowed_parent2 = is_layer_masks_allowed(layer_masks_parent2); 788 + 784 789 /* 785 790 * For a double request, first check for potential privilege 786 791 * escalation by looking at domain handled accesses (which are 787 792 * a superset of the meaningful requested accesses). 788 793 */ 789 794 access_masked_parent1 = access_masked_parent2 = 790 - get_handled_fs_accesses(domain); 795 + landlock_union_access_masks(domain).fs; 791 796 is_dom_check = true; 792 797 } else { 793 798 if (WARN_ON_ONCE(dentry_child1 || dentry_child2)) ··· 852 847 child1_is_directory, layer_masks_parent2, 853 848 layer_masks_child2, 854 849 child2_is_directory))) { 855 - allowed_parent1 = scope_to_request( 856 - access_request_parent1, layer_masks_parent1); 857 - allowed_parent2 = scope_to_request( 858 - access_request_parent2, layer_masks_parent2); 859 - 860 - /* Stops when all accesses are granted. */ 861 - if (allowed_parent1 && allowed_parent2) 862 - break; 863 - 864 850 /* 865 851 * Now, downgrades the remaining checks from domain 866 852 * handled accesses to requested accesses. ··· 859 863 is_dom_check = false; 860 864 access_masked_parent1 = access_request_parent1; 861 865 access_masked_parent2 = access_request_parent2; 866 + 867 + allowed_parent1 = 868 + allowed_parent1 || 869 + scope_to_request(access_masked_parent1, 870 + layer_masks_parent1); 871 + allowed_parent2 = 872 + allowed_parent2 || 873 + scope_to_request(access_masked_parent2, 874 + layer_masks_parent2); 875 + 876 + /* Stops when all accesses are granted. */ 877 + if (allowed_parent1 && allowed_parent2) 878 + break; 862 879 } 863 880 864 881 rule = find_rule(domain, walker_path.dentry); 865 - allowed_parent1 = landlock_unmask_layers( 866 - rule, access_masked_parent1, layer_masks_parent1, 867 - ARRAY_SIZE(*layer_masks_parent1)); 868 - allowed_parent2 = landlock_unmask_layers( 869 - rule, access_masked_parent2, layer_masks_parent2, 870 - ARRAY_SIZE(*layer_masks_parent2)); 882 + allowed_parent1 = allowed_parent1 || 883 + landlock_unmask_layers( 884 + rule, access_masked_parent1, 885 + layer_masks_parent1, 886 + ARRAY_SIZE(*layer_masks_parent1)); 887 + allowed_parent2 = allowed_parent2 || 888 + landlock_unmask_layers( 889 + rule, access_masked_parent2, 890 + layer_masks_parent2, 891 + ARRAY_SIZE(*layer_masks_parent2)); 871 892 872 893 /* Stops when a rule from each layer grants access. */ 873 894 if (allowed_parent1 && allowed_parent2) ··· 908 895 * access to internal filesystems (e.g. nsfs, which is 909 896 * reachable through /proc/<pid>/ns/<namespace>). 910 897 */ 911 - allowed_parent1 = allowed_parent2 = 912 - !!(walker_path.mnt->mnt_flags & MNT_INTERNAL); 898 + if (walker_path.mnt->mnt_flags & MNT_INTERNAL) { 899 + allowed_parent1 = true; 900 + allowed_parent2 = true; 901 + } 913 902 break; 914 903 } 915 904 parent_dentry = dget_parent(walker_path.dentry); ··· 923 908 return allowed_parent1 && allowed_parent2; 924 909 } 925 910 926 - static int check_access_path(const struct landlock_ruleset *const domain, 927 - const struct path *const path, 928 - access_mask_t access_request) 929 - { 930 - layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {}; 931 - 932 - access_request = landlock_init_layer_masks( 933 - domain, access_request, &layer_masks, LANDLOCK_KEY_INODE); 934 - if (is_access_to_paths_allowed(domain, path, access_request, 935 - &layer_masks, NULL, 0, NULL, NULL)) 936 - return 0; 937 - return -EACCES; 938 - } 939 - 940 911 static int current_check_access_path(const struct path *const path, 941 - const access_mask_t access_request) 912 + access_mask_t access_request) 942 913 { 943 914 const struct landlock_ruleset *const dom = get_current_fs_domain(); 915 + layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {}; 944 916 945 917 if (!dom) 946 918 return 0; 947 - return check_access_path(dom, path, access_request); 919 + 920 + access_request = landlock_init_layer_masks( 921 + dom, access_request, &layer_masks, LANDLOCK_KEY_INODE); 922 + if (is_access_to_paths_allowed(dom, path, access_request, &layer_masks, 923 + NULL, 0, NULL, NULL)) 924 + return 0; 925 + 926 + return -EACCES; 948 927 } 949 928 950 - static access_mask_t get_mode_access(const umode_t mode) 929 + static __attribute_const__ access_mask_t get_mode_access(const umode_t mode) 951 930 { 952 931 switch (mode & S_IFMT) { 953 932 case S_IFLNK: 954 933 return LANDLOCK_ACCESS_FS_MAKE_SYM; 955 - case 0: 956 - /* A zero mode translates to S_IFREG. */ 957 - case S_IFREG: 958 - return LANDLOCK_ACCESS_FS_MAKE_REG; 959 934 case S_IFDIR: 960 935 return LANDLOCK_ACCESS_FS_MAKE_DIR; 961 936 case S_IFCHR: ··· 956 951 return LANDLOCK_ACCESS_FS_MAKE_FIFO; 957 952 case S_IFSOCK: 958 953 return LANDLOCK_ACCESS_FS_MAKE_SOCK; 954 + case S_IFREG: 955 + case 0: 956 + /* A zero mode translates to S_IFREG. */ 959 957 default: 960 - WARN_ON_ONCE(1); 961 - return 0; 958 + /* Treats weird files as regular files. */ 959 + return LANDLOCK_ACCESS_FS_MAKE_REG; 962 960 } 963 961 } 964 962 ··· 1422 1414 struct dentry *const dentry, const umode_t mode, 1423 1415 const unsigned int dev) 1424 1416 { 1425 - const struct landlock_ruleset *const dom = get_current_fs_domain(); 1426 - 1427 - if (!dom) 1428 - return 0; 1429 - return check_access_path(dom, dir, get_mode_access(mode)); 1417 + return current_check_access_path(dir, get_mode_access(mode)); 1430 1418 } 1431 1419 1432 1420 static int hook_path_symlink(const struct path *const dir,
+1
security/landlock/fs.h
··· 13 13 #include <linux/init.h> 14 14 #include <linux/rcupdate.h> 15 15 16 + #include "access.h" 16 17 #include "ruleset.h" 17 18 #include "setup.h" 18 19
+13 -13
security/landlock/ruleset.c
··· 8 8 9 9 #include <linux/bits.h> 10 10 #include <linux/bug.h> 11 + #include <linux/cleanup.h> 11 12 #include <linux/compiler_types.h> 12 13 #include <linux/err.h> 13 14 #include <linux/errno.h> 14 15 #include <linux/kernel.h> 15 16 #include <linux/lockdep.h> 17 + #include <linux/mutex.h> 16 18 #include <linux/overflow.h> 17 19 #include <linux/rbtree.h> 18 20 #include <linux/refcount.h> ··· 22 20 #include <linux/spinlock.h> 23 21 #include <linux/workqueue.h> 24 22 23 + #include "access.h" 25 24 #include "limits.h" 26 25 #include "object.h" 27 26 #include "ruleset.h" ··· 387 384 err = -EINVAL; 388 385 goto out_unlock; 389 386 } 390 - dst->access_masks[dst->num_layers - 1] = src->access_masks[0]; 387 + dst->access_masks[dst->num_layers - 1] = 388 + landlock_upgrade_handled_access_masks(src->access_masks[0]); 391 389 392 390 /* Merges the @src inode tree. */ 393 391 err = merge_tree(dst, src, LANDLOCK_KEY_INODE); ··· 541 537 landlock_merge_ruleset(struct landlock_ruleset *const parent, 542 538 struct landlock_ruleset *const ruleset) 543 539 { 544 - struct landlock_ruleset *new_dom; 540 + struct landlock_ruleset *new_dom __free(landlock_put_ruleset) = NULL; 545 541 u32 num_layers; 546 542 int err; 547 543 ··· 561 557 new_dom = create_ruleset(num_layers); 562 558 if (IS_ERR(new_dom)) 563 559 return new_dom; 560 + 564 561 new_dom->hierarchy = 565 562 kzalloc(sizeof(*new_dom->hierarchy), GFP_KERNEL_ACCOUNT); 566 - if (!new_dom->hierarchy) { 567 - err = -ENOMEM; 568 - goto out_put_dom; 569 - } 563 + if (!new_dom->hierarchy) 564 + return ERR_PTR(-ENOMEM); 565 + 570 566 refcount_set(&new_dom->hierarchy->usage, 1); 571 567 572 568 /* ...as a child of @parent... */ 573 569 err = inherit_ruleset(parent, new_dom); 574 570 if (err) 575 - goto out_put_dom; 571 + return ERR_PTR(err); 576 572 577 573 /* ...and including @ruleset. */ 578 574 err = merge_ruleset(new_dom, ruleset); 579 575 if (err) 580 - goto out_put_dom; 576 + return ERR_PTR(err); 581 577 582 - return new_dom; 583 - 584 - out_put_dom: 585 - landlock_put_ruleset(new_dom); 586 - return ERR_PTR(err); 578 + return no_free_ptr(new_dom); 587 579 } 588 580 589 581 /*
+7 -45
security/landlock/ruleset.h
··· 9 9 #ifndef _SECURITY_LANDLOCK_RULESET_H 10 10 #define _SECURITY_LANDLOCK_RULESET_H 11 11 12 - #include <linux/bitops.h> 13 - #include <linux/build_bug.h> 14 - #include <linux/kernel.h> 12 + #include <linux/cleanup.h> 13 + #include <linux/err.h> 15 14 #include <linux/mutex.h> 16 15 #include <linux/rbtree.h> 17 16 #include <linux/refcount.h> 18 17 #include <linux/workqueue.h> 19 - #include <uapi/linux/landlock.h> 20 18 19 + #include "access.h" 21 20 #include "limits.h" 22 21 #include "object.h" 23 - 24 - /* 25 - * All access rights that are denied by default whether they are handled or not 26 - * by a ruleset/layer. This must be ORed with all ruleset->access_masks[] 27 - * entries when we need to get the absolute handled access masks. 28 - */ 29 - /* clang-format off */ 30 - #define LANDLOCK_ACCESS_FS_INITIALLY_DENIED ( \ 31 - LANDLOCK_ACCESS_FS_REFER) 32 - /* clang-format on */ 33 - 34 - typedef u16 access_mask_t; 35 - /* Makes sure all filesystem access rights can be stored. */ 36 - static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_FS); 37 - /* Makes sure all network access rights can be stored. */ 38 - static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_NET); 39 - /* Makes sure all scoped rights can be stored. */ 40 - static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_SCOPE); 41 - /* Makes sure for_each_set_bit() and for_each_clear_bit() calls are OK. */ 42 - static_assert(sizeof(unsigned long) >= sizeof(access_mask_t)); 43 - 44 - /* Ruleset access masks. */ 45 - struct access_masks { 46 - access_mask_t fs : LANDLOCK_NUM_ACCESS_FS; 47 - access_mask_t net : LANDLOCK_NUM_ACCESS_NET; 48 - access_mask_t scope : LANDLOCK_NUM_SCOPE; 49 - }; 50 - 51 - union access_masks_all { 52 - struct access_masks masks; 53 - u32 all; 54 - }; 55 - 56 - /* Makes sure all fields are covered. */ 57 - static_assert(sizeof(typeof_member(union access_masks_all, masks)) == 58 - sizeof(typeof_member(union access_masks_all, all))); 59 - 60 - typedef u16 layer_mask_t; 61 - /* Makes sure all layers can be checked. */ 62 - static_assert(BITS_PER_TYPE(layer_mask_t) >= LANDLOCK_MAX_NUM_LAYERS); 63 22 64 23 /** 65 24 * struct landlock_layer - Access rights for a given layer ··· 211 252 void landlock_put_ruleset(struct landlock_ruleset *const ruleset); 212 253 void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset); 213 254 255 + DEFINE_FREE(landlock_put_ruleset, struct landlock_ruleset *, 256 + if (!IS_ERR_OR_NULL(_T)) landlock_put_ruleset(_T)) 257 + 214 258 int landlock_insert_rule(struct landlock_ruleset *const ruleset, 215 259 const struct landlock_id id, 216 260 const access_mask_t access); ··· 328 366 { 329 367 /* Handles all initially denied by default access rights. */ 330 368 return ruleset->access_masks[layer_level].fs | 331 - LANDLOCK_ACCESS_FS_INITIALLY_DENIED; 369 + _LANDLOCK_ACCESS_FS_INITIALLY_DENIED; 332 370 } 333 371 334 372 static inline access_mask_t
+12 -27
security/landlock/syscalls.c
··· 10 10 #include <linux/anon_inodes.h> 11 11 #include <linux/build_bug.h> 12 12 #include <linux/capability.h> 13 + #include <linux/cleanup.h> 13 14 #include <linux/compiler_types.h> 14 15 #include <linux/dcache.h> 15 16 #include <linux/err.h> ··· 399 398 const enum landlock_rule_type, rule_type, 400 399 const void __user *const, rule_attr, const __u32, flags) 401 400 { 402 - struct landlock_ruleset *ruleset; 403 - int err; 401 + struct landlock_ruleset *ruleset __free(landlock_put_ruleset) = NULL; 404 402 405 403 if (!is_initialized()) 406 404 return -EOPNOTSUPP; ··· 415 415 416 416 switch (rule_type) { 417 417 case LANDLOCK_RULE_PATH_BENEATH: 418 - err = add_rule_path_beneath(ruleset, rule_attr); 419 - break; 418 + return add_rule_path_beneath(ruleset, rule_attr); 420 419 case LANDLOCK_RULE_NET_PORT: 421 - err = add_rule_net_port(ruleset, rule_attr); 422 - break; 420 + return add_rule_net_port(ruleset, rule_attr); 423 421 default: 424 - err = -EINVAL; 425 - break; 422 + return -EINVAL; 426 423 } 427 - landlock_put_ruleset(ruleset); 428 - return err; 429 424 } 430 425 431 426 /* Enforcement */ ··· 451 456 SYSCALL_DEFINE2(landlock_restrict_self, const int, ruleset_fd, const __u32, 452 457 flags) 453 458 { 454 - struct landlock_ruleset *new_dom, *ruleset; 459 + struct landlock_ruleset *new_dom, 460 + *ruleset __free(landlock_put_ruleset) = NULL; 455 461 struct cred *new_cred; 456 462 struct landlock_cred_security *new_llcred; 457 - int err; 458 463 459 464 if (!is_initialized()) 460 465 return -EOPNOTSUPP; ··· 478 483 479 484 /* Prepares new credentials. */ 480 485 new_cred = prepare_creds(); 481 - if (!new_cred) { 482 - err = -ENOMEM; 483 - goto out_put_ruleset; 484 - } 486 + if (!new_cred) 487 + return -ENOMEM; 488 + 485 489 new_llcred = landlock_cred(new_cred); 486 490 487 491 /* ··· 489 495 */ 490 496 new_dom = landlock_merge_ruleset(new_llcred->domain, ruleset); 491 497 if (IS_ERR(new_dom)) { 492 - err = PTR_ERR(new_dom); 493 - goto out_put_creds; 498 + abort_creds(new_cred); 499 + return PTR_ERR(new_dom); 494 500 } 495 501 496 502 /* Replaces the old (prepared) domain. */ 497 503 landlock_put_ruleset(new_llcred->domain); 498 504 new_llcred->domain = new_dom; 499 - 500 - landlock_put_ruleset(ruleset); 501 505 return commit_creds(new_cred); 502 - 503 - out_put_creds: 504 - abort_creds(new_cred); 505 - 506 - out_put_ruleset: 507 - landlock_put_ruleset(ruleset); 508 - return err; 509 506 }
+3 -3
tools/testing/selftests/landlock/Makefile
··· 10 10 11 11 TEST_GEN_PROGS := $(src_test:.c=) 12 12 13 - TEST_GEN_PROGS_EXTENDED := true 13 + TEST_GEN_PROGS_EXTENDED := true sandbox-and-launch wait-pipe 14 14 15 15 # Short targets: 16 - $(TEST_GEN_PROGS): LDLIBS += -lcap 16 + $(TEST_GEN_PROGS): LDLIBS += -lcap -lpthread 17 17 $(TEST_GEN_PROGS_EXTENDED): LDFLAGS += -static 18 18 19 19 include ../lib.mk 20 20 21 21 # Targets with $(OUTPUT)/ prefix: 22 - $(TEST_GEN_PROGS): LDLIBS += -lcap 22 + $(TEST_GEN_PROGS): LDLIBS += -lcap -lpthread 23 23 $(TEST_GEN_PROGS_EXTENDED): LDFLAGS += -static
+3 -35
tools/testing/selftests/landlock/common.h
··· 9 9 10 10 #include <arpa/inet.h> 11 11 #include <errno.h> 12 - #include <linux/landlock.h> 13 12 #include <linux/securebits.h> 14 13 #include <sys/capability.h> 15 14 #include <sys/socket.h> 16 - #include <sys/syscall.h> 17 - #include <sys/types.h> 18 15 #include <sys/un.h> 19 16 #include <sys/wait.h> 20 17 #include <unistd.h> 21 18 22 19 #include "../kselftest_harness.h" 20 + #include "wrappers.h" 23 21 24 22 #define TMP_DIR "tmp" 25 23 ··· 28 30 /* TEST_F_FORK() should not be used for new tests. */ 29 31 #define TEST_F_FORK(fixture_name, test_name) TEST_F(fixture_name, test_name) 30 32 31 - #ifndef landlock_create_ruleset 32 - static inline int 33 - landlock_create_ruleset(const struct landlock_ruleset_attr *const attr, 34 - const size_t size, const __u32 flags) 35 - { 36 - return syscall(__NR_landlock_create_ruleset, attr, size, flags); 37 - } 38 - #endif 39 - 40 - #ifndef landlock_add_rule 41 - static inline int landlock_add_rule(const int ruleset_fd, 42 - const enum landlock_rule_type rule_type, 43 - const void *const rule_attr, 44 - const __u32 flags) 45 - { 46 - return syscall(__NR_landlock_add_rule, ruleset_fd, rule_type, rule_attr, 47 - flags); 48 - } 49 - #endif 50 - 51 - #ifndef landlock_restrict_self 52 - static inline int landlock_restrict_self(const int ruleset_fd, 53 - const __u32 flags) 54 - { 55 - return syscall(__NR_landlock_restrict_self, ruleset_fd, flags); 56 - } 57 - #endif 33 + static const char bin_sandbox_and_launch[] = "./sandbox-and-launch"; 34 + static const char bin_wait_pipe[] = "./wait-pipe"; 58 35 59 36 static void _init_caps(struct __test_metadata *const _metadata, bool drop_all) 60 37 { ··· 222 249 }; 223 250 }; 224 251 }; 225 - 226 - static pid_t __maybe_unused sys_gettid(void) 227 - { 228 - return syscall(__NR_gettid); 229 - } 230 252 231 253 static void __maybe_unused set_unix_address(struct service_fixture *const srv, 232 254 const unsigned short index)
+136 -15
tools/testing/selftests/landlock/fs_test.c
··· 59 59 #define RENAME_EXCHANGE (1 << 1) 60 60 #endif 61 61 62 - #define BINARY_PATH "./true" 62 + static const char bin_true[] = "./true"; 63 63 64 64 /* Paths (sibling number and depth) */ 65 65 static const char dir_s1d1[] = TMP_DIR "/s1d1"; ··· 85 85 /* dir_s3d2 is a mount point. */ 86 86 static const char dir_s3d2[] = TMP_DIR "/s3d1/s3d2"; 87 87 static const char dir_s3d3[] = TMP_DIR "/s3d1/s3d2/s3d3"; 88 + static const char file1_s3d3[] = TMP_DIR "/s3d1/s3d2/s3d3/f1"; 89 + static const char dir_s3d4[] = TMP_DIR "/s3d1/s3d2/s3d4"; 90 + static const char file1_s3d4[] = TMP_DIR "/s3d1/s3d2/s3d4/f1"; 88 91 89 92 /* 90 93 * layout1 hierarchy: ··· 111 108 * │   └── f2 112 109 * └── s3d1 113 110 *    ├── f1 114 - * └── s3d2 115 - * └── s3d3 111 + * └── s3d2 [mount point] 112 + *    ├── s3d3 113 + *    │ └── f1 114 + *    └── s3d4 115 + *    └── f1 116 116 */ 117 117 118 118 static bool fgrep(FILE *const inf, const char *const str) ··· 364 358 ASSERT_EQ(0, mount_opt(&mnt_tmp, dir_s3d2)); 365 359 clear_cap(_metadata, CAP_SYS_ADMIN); 366 360 367 - ASSERT_EQ(0, mkdir(dir_s3d3, 0700)); 361 + create_file(_metadata, file1_s3d3); 362 + create_file(_metadata, file1_s3d4); 368 363 } 369 364 370 365 static void remove_layout1(struct __test_metadata *const _metadata) ··· 385 378 EXPECT_EQ(0, remove_path(dir_s2d2)); 386 379 387 380 EXPECT_EQ(0, remove_path(file1_s3d1)); 388 - EXPECT_EQ(0, remove_path(dir_s3d3)); 381 + EXPECT_EQ(0, remove_path(file1_s3d3)); 382 + EXPECT_EQ(0, remove_path(file1_s3d4)); 389 383 set_cap(_metadata, CAP_SYS_ADMIN); 390 384 umount(dir_s3d2); 391 385 clear_cap(_metadata, CAP_SYS_ADMIN); ··· 1965 1957 test_relative_path(_metadata, REL_CHROOT_CHDIR); 1966 1958 } 1967 1959 1968 - static void copy_binary(struct __test_metadata *const _metadata, 1969 - const char *const dst_path) 1960 + static void copy_file(struct __test_metadata *const _metadata, 1961 + const char *const src_path, const char *const dst_path) 1970 1962 { 1971 1963 int dst_fd, src_fd; 1972 1964 struct stat statbuf; ··· 1976 1968 { 1977 1969 TH_LOG("Failed to open \"%s\": %s", dst_path, strerror(errno)); 1978 1970 } 1979 - src_fd = open(BINARY_PATH, O_RDONLY | O_CLOEXEC); 1971 + src_fd = open(src_path, O_RDONLY | O_CLOEXEC); 1980 1972 ASSERT_LE(0, src_fd) 1981 1973 { 1982 - TH_LOG("Failed to open \"" BINARY_PATH "\": %s", 1983 - strerror(errno)); 1974 + TH_LOG("Failed to open \"%s\": %s", src_path, strerror(errno)); 1984 1975 } 1985 1976 ASSERT_EQ(0, fstat(src_fd, &statbuf)); 1986 1977 ASSERT_EQ(statbuf.st_size, ··· 2010 2003 ASSERT_EQ(1, WIFEXITED(status)); 2011 2004 ASSERT_EQ(err ? 2 : 0, WEXITSTATUS(status)) 2012 2005 { 2013 - TH_LOG("Unexpected return code for \"%s\": %s", path, 2014 - strerror(errno)); 2006 + TH_LOG("Unexpected return code for \"%s\"", path); 2015 2007 }; 2016 2008 } 2017 2009 ··· 2027 2021 create_ruleset(_metadata, rules[0].access, rules); 2028 2022 2029 2023 ASSERT_LE(0, ruleset_fd); 2030 - copy_binary(_metadata, file1_s1d1); 2031 - copy_binary(_metadata, file1_s1d2); 2032 - copy_binary(_metadata, file1_s1d3); 2024 + copy_file(_metadata, bin_true, file1_s1d1); 2025 + copy_file(_metadata, bin_true, file1_s1d2); 2026 + copy_file(_metadata, bin_true, file1_s1d3); 2033 2027 2034 2028 enforce_ruleset(_metadata, ruleset_fd); 2035 2029 ASSERT_EQ(0, close(ruleset_fd)); ··· 2045 2039 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 2046 2040 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 2047 2041 test_execute(_metadata, 0, file1_s1d3); 2042 + } 2043 + 2044 + TEST_F_FORK(layout1, umount_sandboxer) 2045 + { 2046 + int pipe_child[2], pipe_parent[2]; 2047 + char buf_parent; 2048 + pid_t child; 2049 + int status; 2050 + 2051 + copy_file(_metadata, bin_sandbox_and_launch, file1_s3d3); 2052 + ASSERT_EQ(0, pipe2(pipe_child, 0)); 2053 + ASSERT_EQ(0, pipe2(pipe_parent, 0)); 2054 + 2055 + child = fork(); 2056 + ASSERT_LE(0, child); 2057 + if (child == 0) { 2058 + char pipe_child_str[12], pipe_parent_str[12]; 2059 + char *const argv[] = { (char *)file1_s3d3, 2060 + (char *)bin_wait_pipe, pipe_child_str, 2061 + pipe_parent_str, NULL }; 2062 + 2063 + /* Passes the pipe FDs to the executed binary and its child. */ 2064 + EXPECT_EQ(0, close(pipe_child[0])); 2065 + EXPECT_EQ(0, close(pipe_parent[1])); 2066 + snprintf(pipe_child_str, sizeof(pipe_child_str), "%d", 2067 + pipe_child[1]); 2068 + snprintf(pipe_parent_str, sizeof(pipe_parent_str), "%d", 2069 + pipe_parent[0]); 2070 + 2071 + /* 2072 + * We need bin_sandbox_and_launch (copied inside the mount as 2073 + * file1_s3d3) to execute bin_wait_pipe (outside the mount) to 2074 + * make sure the mount point will not be EBUSY because of 2075 + * file1_s3d3 being in use. This avoids a potential race 2076 + * condition between the following read() and umount() calls. 2077 + */ 2078 + ASSERT_EQ(0, execve(argv[0], argv, NULL)) 2079 + { 2080 + TH_LOG("Failed to execute \"%s\": %s", argv[0], 2081 + strerror(errno)); 2082 + }; 2083 + _exit(1); 2084 + return; 2085 + } 2086 + 2087 + EXPECT_EQ(0, close(pipe_child[1])); 2088 + EXPECT_EQ(0, close(pipe_parent[0])); 2089 + 2090 + /* Waits for the child to sandbox itself. */ 2091 + EXPECT_EQ(1, read(pipe_child[0], &buf_parent, 1)); 2092 + 2093 + /* Tests that the sandboxer is tied to its mount point. */ 2094 + set_cap(_metadata, CAP_SYS_ADMIN); 2095 + EXPECT_EQ(-1, umount(dir_s3d2)); 2096 + EXPECT_EQ(EBUSY, errno); 2097 + clear_cap(_metadata, CAP_SYS_ADMIN); 2098 + 2099 + /* Signals the child to launch a grandchild. */ 2100 + EXPECT_EQ(1, write(pipe_parent[1], ".", 1)); 2101 + 2102 + /* Waits for the grandchild. */ 2103 + EXPECT_EQ(1, read(pipe_child[0], &buf_parent, 1)); 2104 + 2105 + /* Tests that the domain's sandboxer is not tied to its mount point. */ 2106 + set_cap(_metadata, CAP_SYS_ADMIN); 2107 + EXPECT_EQ(0, umount(dir_s3d2)) 2108 + { 2109 + TH_LOG("Failed to umount \"%s\": %s", dir_s3d2, 2110 + strerror(errno)); 2111 + }; 2112 + clear_cap(_metadata, CAP_SYS_ADMIN); 2113 + 2114 + /* Signals the grandchild to terminate. */ 2115 + EXPECT_EQ(1, write(pipe_parent[1], ".", 1)); 2116 + ASSERT_EQ(child, waitpid(child, &status, 0)); 2117 + ASSERT_EQ(1, WIFEXITED(status)); 2118 + ASSERT_EQ(0, WEXITSTATUS(status)); 2048 2119 } 2049 2120 2050 2121 TEST_F_FORK(layout1, link) ··· 2525 2442 EXPECT_EQ(EBUSY, errno); 2526 2443 2527 2444 EXPECT_EQ(0, close(root_fd)); 2445 + } 2446 + 2447 + TEST_F_FORK(layout1, refer_part_mount_tree_is_allowed) 2448 + { 2449 + const struct rule layer1[] = { 2450 + { 2451 + /* Parent mount point. */ 2452 + .path = dir_s3d1, 2453 + .access = LANDLOCK_ACCESS_FS_REFER | 2454 + LANDLOCK_ACCESS_FS_MAKE_REG, 2455 + }, 2456 + { 2457 + /* 2458 + * Removing the source file is allowed because its 2459 + * access rights are already a superset of the 2460 + * destination. 2461 + */ 2462 + .path = dir_s3d4, 2463 + .access = LANDLOCK_ACCESS_FS_REFER | 2464 + LANDLOCK_ACCESS_FS_MAKE_REG | 2465 + LANDLOCK_ACCESS_FS_REMOVE_FILE, 2466 + }, 2467 + {}, 2468 + }; 2469 + int ruleset_fd; 2470 + 2471 + ASSERT_EQ(0, unlink(file1_s3d3)); 2472 + ruleset_fd = create_ruleset(_metadata, 2473 + LANDLOCK_ACCESS_FS_REFER | 2474 + LANDLOCK_ACCESS_FS_MAKE_REG | 2475 + LANDLOCK_ACCESS_FS_REMOVE_FILE, 2476 + layer1); 2477 + 2478 + ASSERT_LE(0, ruleset_fd); 2479 + enforce_ruleset(_metadata, ruleset_fd); 2480 + ASSERT_EQ(0, close(ruleset_fd)); 2481 + 2482 + ASSERT_EQ(0, rename(file1_s3d4, file1_s3d3)); 2528 2483 } 2529 2484 2530 2485 TEST_F_FORK(layout1, reparent_link)
-2
tools/testing/selftests/landlock/ptrace_test.c
··· 22 22 /* Copied from security/yama/yama_lsm.c */ 23 23 #define YAMA_SCOPE_DISABLED 0 24 24 #define YAMA_SCOPE_RELATIONAL 1 25 - #define YAMA_SCOPE_CAPABILITY 2 26 - #define YAMA_SCOPE_NO_ATTACH 3 27 25 28 26 static void create_domain(struct __test_metadata *const _metadata) 29 27 {
+82
tools/testing/selftests/landlock/sandbox-and-launch.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Sandbox itself and execute another program (in a different mount point). 4 + * 5 + * Used by layout1.umount_sandboxer from fs_test.c 6 + * 7 + * Copyright © 2024-2025 Microsoft Corporation 8 + */ 9 + 10 + #define _GNU_SOURCE 11 + #include <errno.h> 12 + #include <stdio.h> 13 + #include <stdlib.h> 14 + #include <sys/prctl.h> 15 + #include <unistd.h> 16 + 17 + #include "wrappers.h" 18 + 19 + int main(int argc, char *argv[]) 20 + { 21 + struct landlock_ruleset_attr ruleset_attr = { 22 + .scoped = LANDLOCK_SCOPE_SIGNAL, 23 + }; 24 + int pipe_child, pipe_parent, ruleset_fd; 25 + char buf; 26 + 27 + /* 28 + * The first argument must be the file descriptor number of a pipe. 29 + * The second argument must be the program to execute. 30 + */ 31 + if (argc != 4) { 32 + fprintf(stderr, "Wrong number of arguments (not three)\n"); 33 + return 1; 34 + } 35 + 36 + pipe_child = atoi(argv[2]); 37 + pipe_parent = atoi(argv[3]); 38 + 39 + ruleset_fd = 40 + landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 41 + if (ruleset_fd < 0) { 42 + perror("Failed to create ruleset"); 43 + return 1; 44 + } 45 + 46 + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { 47 + perror("Failed to call prctl()"); 48 + return 1; 49 + } 50 + 51 + if (landlock_restrict_self(ruleset_fd, 0)) { 52 + perror("Failed to restrict self"); 53 + return 1; 54 + } 55 + 56 + if (close(ruleset_fd)) { 57 + perror("Failed to close ruleset"); 58 + return 1; 59 + } 60 + 61 + /* Signals that we are sandboxed. */ 62 + errno = 0; 63 + if (write(pipe_child, ".", 1) != 1) { 64 + perror("Failed to write to the second argument"); 65 + return 1; 66 + } 67 + 68 + /* Waits for the parent to try to umount. */ 69 + if (read(pipe_parent, &buf, 1) != 1) { 70 + perror("Failed to write to the third argument"); 71 + return 1; 72 + } 73 + 74 + /* Shifts arguments. */ 75 + argv[0] = argv[1]; 76 + argv[1] = argv[2]; 77 + argv[2] = argv[3]; 78 + argv[3] = NULL; 79 + execve(argv[0], argv, NULL); 80 + perror("Failed to execute the provided binary"); 81 + return 1; 82 + }
+42
tools/testing/selftests/landlock/wait-pipe.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Write in a pipe and wait. 4 + * 5 + * Used by layout1.umount_sandboxer from fs_test.c 6 + * 7 + * Copyright © 2024-2025 Microsoft Corporation 8 + */ 9 + 10 + #define _GNU_SOURCE 11 + #include <stdio.h> 12 + #include <stdlib.h> 13 + #include <unistd.h> 14 + 15 + int main(int argc, char *argv[]) 16 + { 17 + int pipe_child, pipe_parent; 18 + char buf; 19 + 20 + /* The first argument must be the file descriptor number of a pipe. */ 21 + if (argc != 3) { 22 + fprintf(stderr, "Wrong number of arguments (not two)\n"); 23 + return 1; 24 + } 25 + 26 + pipe_child = atoi(argv[1]); 27 + pipe_parent = atoi(argv[2]); 28 + 29 + /* Signals that we are waiting. */ 30 + if (write(pipe_child, ".", 1) != 1) { 31 + perror("Failed to write to first argument"); 32 + return 1; 33 + } 34 + 35 + /* Waits for the parent do its test. */ 36 + if (read(pipe_parent, &buf, 1) != 1) { 37 + perror("Failed to write to the second argument"); 38 + return 1; 39 + } 40 + 41 + return 0; 42 + }
+47
tools/testing/selftests/landlock/wrappers.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Syscall wrappers 4 + * 5 + * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net> 6 + * Copyright © 2019-2020 ANSSI 7 + * Copyright © 2021-2025 Microsoft Corporation 8 + */ 9 + 10 + #define _GNU_SOURCE 11 + #include <linux/landlock.h> 12 + #include <sys/syscall.h> 13 + #include <sys/types.h> 14 + #include <unistd.h> 15 + 16 + #ifndef landlock_create_ruleset 17 + static inline int 18 + landlock_create_ruleset(const struct landlock_ruleset_attr *const attr, 19 + const size_t size, const __u32 flags) 20 + { 21 + return syscall(__NR_landlock_create_ruleset, attr, size, flags); 22 + } 23 + #endif 24 + 25 + #ifndef landlock_add_rule 26 + static inline int landlock_add_rule(const int ruleset_fd, 27 + const enum landlock_rule_type rule_type, 28 + const void *const rule_attr, 29 + const __u32 flags) 30 + { 31 + return syscall(__NR_landlock_add_rule, ruleset_fd, rule_type, rule_attr, 32 + flags); 33 + } 34 + #endif 35 + 36 + #ifndef landlock_restrict_self 37 + static inline int landlock_restrict_self(const int ruleset_fd, 38 + const __u32 flags) 39 + { 40 + return syscall(__NR_landlock_restrict_self, ruleset_fd, flags); 41 + } 42 + #endif 43 + 44 + static inline pid_t sys_gettid(void) 45 + { 46 + return syscall(__NR_gettid); 47 + }