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 'apparmor-pr-2026-02-18' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor

Pull AppArmor updates from John Johansen:
"Features:
- add .kunitconfig
- audit execpath in userns mediation
- add support loading per permission tagging

Cleanups:
- remove unused percpu critical sections in buffer management
- document the buffer hold, add an overflow guard
- split xxx_in_ns into its two separate semantic use cases
- remove apply_modes_to_perms from label_match
- refactor/cleanup cred helper fns.
- guard against free attachment/data routines being called with NULL
- drop in_atomic flag in common_mmap, common_file_perm, and cleanup
- make str table more generic and be able to have multiple entries
- Replace deprecated strcpy with memcpy in gen_symlink_name
- Replace deprecated strcpy in d_namespace_path
- Replace sprintf/strcpy with scnprintf/strscpy in aa_policy_init
- replace sprintf with snprintf in aa_new_learning_profile

Bug Fixes:
- fix cast in format string DEBUG statement
- fix make aa_labelmatch return consistent
- fix fmt string type error in process_strs_entry
- fix kernel-doc comments for inview
- fix invalid deref of rawdata when export_binary is unset
- avoid per-cpu hold underflow in aa_get_buffer
- fix fast path cache check for unix sockets
- fix rlimit for posix cpu timers
- fix label and profile debug macros
- move check for aa_null file to cover all cases
- return -ENOMEM in unpack_perms_table upon alloc failure
- fix boolean argument in apparmor_mmap_file
- Fix & Optimize table creation from possibly unaligned memory
- Allow apparmor to handle unaligned dfa tables
- fix NULL deref in aa_sock_file_perm
- fix NULL pointer dereference in __unix_needs_revalidation
- fix signedness bug in unpack_tags()"

* tag 'apparmor-pr-2026-02-18' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor: (34 commits)
apparmor: fix signedness bug in unpack_tags()
apparmor: fix cast in format string DEBUG statement
apparmor: fix aa_label to return state from compount and component match
apparmor: fix fmt string type error in process_strs_entry
apparmor: fix kernel-doc comments for inview
apparmor: fix invalid deref of rawdata when export_binary is unset
apparmor: add .kunitconfig
apparmor: cleanup remove unused percpu critical sections in buffer management
apparmor: document the buffer hold, add an overflow guard
apparmor: avoid per-cpu hold underflow in aa_get_buffer
apparmor: split xxx_in_ns into its two separate semantic use cases
apparmor: make label_match return a consistent value
apparmor: remove apply_modes_to_perms from label_match
apparmor: fix fast path cache check for unix sockets
apparmor: fix rlimit for posix cpu timers
apparmor: refactor/cleanup cred helper fns.
apparmor: fix label and profile debug macros
apparmor: move check for aa_null file to cover all cases
apparmor: guard against free routines being called with a NULL
apparmor: return -ENOMEM in unpack_perms_table upon alloc failure
...

+690 -243
+5
security/apparmor/.kunitconfig
··· 1 + CONFIG_KUNIT=y 2 + CONFIG_NET=y 3 + CONFIG_SECURITY=y 4 + CONFIG_SECURITY_APPARMOR=y 5 + CONFIG_SECURITY_APPARMOR_KUNIT_TEST=y
+1 -1
security/apparmor/af_unix.c
··· 416 416 unix_sk(sk), 417 417 peer_addr, peer_addrlen, &p, &ad->info); 418 418 419 - return fn_for_each_in_ns(peer_label, peerp, 419 + return fn_for_each_in_scope(peer_label, peerp, 420 420 match_label(profile, rules, state, request, 421 421 peerp, p, ad)); 422 422 }
+18 -5
security/apparmor/apparmorfs.c
··· 801 801 802 802 perms = allperms; 803 803 if (view_only) { 804 - label_for_each_in_ns(i, labels_ns(label), label, profile) { 804 + label_for_each_in_scope(i, labels_ns(label), label, profile) { 805 805 profile_query_cb(profile, &perms, match_str, match_len); 806 806 } 807 807 } else { ··· 1607 1607 { 1608 1608 char *buffer, *s; 1609 1609 int error; 1610 - int size = depth * 6 + strlen(dirname) + strlen(fname) + 11; 1610 + const char *path = "../../"; 1611 + size_t path_len = strlen(path); 1612 + int size; 1611 1613 1614 + /* Extra 11 bytes: "raw_data" (9) + two slashes "//" (2) */ 1615 + size = depth * path_len + strlen(dirname) + strlen(fname) + 11; 1612 1616 s = buffer = kmalloc(size, GFP_KERNEL); 1613 1617 if (!buffer) 1614 1618 return ERR_PTR(-ENOMEM); 1615 1619 1616 1620 for (; depth > 0; depth--) { 1617 - strcpy(s, "../../"); 1618 - s += 6; 1619 - size -= 6; 1621 + memcpy(s, path, path_len); 1622 + s += path_len; 1623 + size -= path_len; 1620 1624 } 1621 1625 1622 1626 error = snprintf(s, size, "raw_data/%s/%s", dirname, fname); ··· 1648 1644 1649 1645 label = aa_get_label_rcu(&proxy->label); 1650 1646 profile = labels_profile(label); 1647 + 1648 + /* rawdata can be null when aa_g_export_binary is unset during 1649 + * runtime and a profile is replaced 1650 + */ 1651 + if (!profile->rawdata) { 1652 + aa_put_label(label); 1653 + return ERR_PTR(-ENOENT); 1654 + } 1655 + 1651 1656 depth = profile_depth(profile); 1652 1657 target = gen_symlink_name(depth, profile->rawdata->name, name); 1653 1658 aa_put_label(label);
+31 -29
security/apparmor/domain.c
··· 115 115 * @label: label to check access permissions for 116 116 * @stack: whether this is a stacking request 117 117 * @state: state to start match in 118 - * @subns: whether to do permission checks on components in a subns 118 + * @inview: whether to match labels in view or only in scope 119 119 * @request: permissions to request 120 120 * @perms: perms struct to set 121 121 * ··· 127 127 */ 128 128 static int label_compound_match(struct aa_profile *profile, 129 129 struct aa_label *label, bool stack, 130 - aa_state_t state, bool subns, u32 request, 130 + aa_state_t state, bool inview, u32 request, 131 131 struct aa_perms *perms) 132 132 { 133 133 struct aa_ruleset *rules = profile->label.rules[0]; ··· 135 135 struct label_it i; 136 136 struct path_cond cond = { }; 137 137 138 - /* find first subcomponent that is visible */ 138 + /* find first subcomponent that is in view and going to be interated with */ 139 139 label_for_each(i, label, tp) { 140 - if (!aa_ns_visible(profile->ns, tp->ns, subns)) 140 + if (!aa_ns_visible(profile->ns, tp->ns, inview)) 141 141 continue; 142 142 state = match_component(profile, tp, stack, state); 143 143 if (!state) ··· 151 151 152 152 next: 153 153 label_for_each_cont(i, label, tp) { 154 - if (!aa_ns_visible(profile->ns, tp->ns, subns)) 154 + if (!aa_ns_visible(profile->ns, tp->ns, inview)) 155 155 continue; 156 156 state = aa_dfa_match(rules->file->dfa, state, "//&"); 157 157 state = match_component(profile, tp, false, state); ··· 177 177 * @label: label to check access permissions for 178 178 * @stack: whether this is a stacking request 179 179 * @start: state to start match in 180 - * @subns: whether to do permission checks on components in a subns 180 + * @inview: whether to match labels in view or only in scope 181 181 * @request: permissions to request 182 182 * @perms: an initialized perms struct to add accumulation to 183 183 * ··· 189 189 */ 190 190 static int label_components_match(struct aa_profile *profile, 191 191 struct aa_label *label, bool stack, 192 - aa_state_t start, bool subns, u32 request, 192 + aa_state_t start, bool inview, u32 request, 193 193 struct aa_perms *perms) 194 194 { 195 195 struct aa_ruleset *rules = profile->label.rules[0]; ··· 201 201 202 202 /* find first subcomponent to test */ 203 203 label_for_each(i, label, tp) { 204 - if (!aa_ns_visible(profile->ns, tp->ns, subns)) 204 + if (!aa_ns_visible(profile->ns, tp->ns, inview)) 205 205 continue; 206 206 state = match_component(profile, tp, stack, start); 207 207 if (!state) ··· 218 218 aa_apply_modes_to_perms(profile, &tmp); 219 219 aa_perms_accum(perms, &tmp); 220 220 label_for_each_cont(i, label, tp) { 221 - if (!aa_ns_visible(profile->ns, tp->ns, subns)) 221 + if (!aa_ns_visible(profile->ns, tp->ns, inview)) 222 222 continue; 223 223 state = match_component(profile, tp, stack, start); 224 224 if (!state) ··· 245 245 * @label: label to match (NOT NULL) 246 246 * @stack: whether this is a stacking request 247 247 * @state: state to start in 248 - * @subns: whether to match subns components 248 + * @inview: whether to match labels in view or only in scope 249 249 * @request: permission request 250 250 * @perms: Returns computed perms (NOT NULL) 251 251 * 252 252 * Returns: the state the match finished in, may be the none matching state 253 253 */ 254 254 static int label_match(struct aa_profile *profile, struct aa_label *label, 255 - bool stack, aa_state_t state, bool subns, u32 request, 255 + bool stack, aa_state_t state, bool inview, u32 request, 256 256 struct aa_perms *perms) 257 257 { 258 258 int error; 259 259 260 260 *perms = nullperms; 261 - error = label_compound_match(profile, label, stack, state, subns, 261 + error = label_compound_match(profile, label, stack, state, inview, 262 262 request, perms); 263 263 if (!error) 264 264 return error; 265 265 266 266 *perms = allperms; 267 - return label_components_match(profile, label, stack, state, subns, 267 + return label_components_match(profile, label, stack, state, inview, 268 268 request, perms); 269 269 } 270 270 ··· 529 529 /* TODO: move lookup parsing to unpack time so this is a straight 530 530 * index into the resultant label 531 531 */ 532 - for (next = rules->file->trans.table[index]; next; 532 + for (next = rules->file->trans.table[index].strs; next; 533 533 next = next_name(xtype, next)) { 534 534 const char *lookup = (*next == '&') ? next + 1 : next; 535 535 *name = next; ··· 880 880 AA_BUG(!bprm); 881 881 AA_BUG(!buffer); 882 882 883 - /* TODO: determine how much we want to loosen this */ 884 - error = fn_for_each_in_ns(label, profile, 883 + /* TODO: determine how much we want to loosen this 884 + * only check profiles in scope for permission to change at exec 885 + */ 886 + error = fn_for_each_in_scope(label, profile, 885 887 profile_onexec(subj_cred, profile, onexec, stack, 886 888 bprm, buffer, cond, unsafe)); 887 889 if (error) 888 890 return ERR_PTR(error); 889 891 890 - new = fn_label_build_in_ns(label, profile, GFP_KERNEL, 892 + new = fn_label_build_in_scope(label, profile, GFP_KERNEL, 891 893 stack ? aa_label_merge(&profile->label, onexec, 892 894 GFP_KERNEL) 893 895 : aa_get_newest_label(onexec), ··· 899 897 return new; 900 898 901 899 /* TODO: get rid of GLOBAL_ROOT_UID */ 902 - error = fn_for_each_in_ns(label, profile, 900 + error = fn_for_each_in_scope(label, profile, 903 901 aa_audit_file(subj_cred, profile, &nullperms, 904 902 OP_CHANGE_ONEXEC, 905 903 AA_MAY_ONEXEC, bprm->filename, NULL, ··· 1125 1123 /*find first matching hat */ 1126 1124 for (i = 0; i < count && !hat; i++) { 1127 1125 name = hats[i]; 1128 - label_for_each_in_ns(it, labels_ns(label), label, profile) { 1126 + label_for_each_in_scope(it, labels_ns(label), label, profile) { 1129 1127 if (sibling && PROFILE_IS_HAT(profile)) { 1130 1128 root = aa_get_profile_rcu(&profile->parent); 1131 1129 } else if (!sibling && !PROFILE_IS_HAT(profile)) { ··· 1161 1159 * change_hat. 1162 1160 */ 1163 1161 name = NULL; 1164 - label_for_each_in_ns(it, labels_ns(label), label, profile) { 1162 + label_for_each_in_scope(it, labels_ns(label), label, profile) { 1165 1163 if (!list_empty(&profile->base.profiles)) { 1166 1164 info = "hat not found"; 1167 1165 error = -ENOENT; ··· 1172 1170 error = -ECHILD; 1173 1171 1174 1172 fail: 1175 - label_for_each_in_ns(it, labels_ns(label), label, profile) { 1173 + label_for_each_in_scope(it, labels_ns(label), label, profile) { 1176 1174 /* 1177 1175 * no target as it has failed to be found or built 1178 1176 * ··· 1190 1188 return ERR_PTR(error); 1191 1189 1192 1190 build: 1193 - new = fn_label_build_in_ns(label, profile, GFP_KERNEL, 1191 + new = fn_label_build_in_scope(label, profile, GFP_KERNEL, 1194 1192 build_change_hat(subj_cred, profile, name, 1195 1193 sibling), 1196 1194 aa_get_label(&profile->label)); ··· 1253 1251 bool empty = true; 1254 1252 1255 1253 rcu_read_lock(); 1256 - label_for_each_in_ns(i, labels_ns(label), label, profile) { 1254 + label_for_each_in_scope(i, labels_ns(label), label, profile) { 1257 1255 empty &= list_empty(&profile->base.profiles); 1258 1256 } 1259 1257 rcu_read_unlock(); ··· 1340 1338 perms.kill = AA_MAY_CHANGEHAT; 1341 1339 1342 1340 fail: 1343 - fn_for_each_in_ns(label, profile, 1341 + fn_for_each_in_scope(label, profile, 1344 1342 aa_audit_file(subj_cred, profile, &perms, OP_CHANGE_HAT, 1345 1343 AA_MAY_CHANGEHAT, NULL, NULL, target, 1346 1344 GLOBAL_ROOT_UID, info, error)); ··· 1448 1446 */ 1449 1447 stack = true; 1450 1448 perms.audit = request; 1451 - (void) fn_for_each_in_ns(label, profile, 1449 + (void) fn_for_each_in_scope(label, profile, 1452 1450 aa_audit_file(subj_cred, profile, &perms, op, 1453 1451 request, auditname, NULL, target, 1454 1452 GLOBAL_ROOT_UID, stack_msg, 0)); ··· 1494 1492 * 1495 1493 * if (!stack) { 1496 1494 */ 1497 - error = fn_for_each_in_ns(label, profile, 1495 + error = fn_for_each_in_scope(label, profile, 1498 1496 change_profile_perms_wrapper(op, auditname, 1499 1497 subj_cred, 1500 1498 profile, target, stack, ··· 1508 1506 check: 1509 1507 /* check if tracing task is allowed to trace target domain */ 1510 1508 error = may_change_ptraced_domain(subj_cred, target, &info); 1511 - if (error && !fn_for_each_in_ns(label, profile, 1509 + if (error && !fn_for_each_in_scope(label, profile, 1512 1510 COMPLAIN_MODE(profile))) 1513 1511 goto audit; 1514 1512 ··· 1524 1522 1525 1523 /* stacking is always a subset, so only check the nonstack case */ 1526 1524 if (!stack) { 1527 - new = fn_label_build_in_ns(label, profile, GFP_KERNEL, 1525 + new = fn_label_build_in_scope(label, profile, GFP_KERNEL, 1528 1526 aa_get_label(target), 1529 1527 aa_get_label(&profile->label)); 1530 1528 /* ··· 1567 1565 } 1568 1566 1569 1567 audit: 1570 - error = fn_for_each_in_ns(label, profile, 1568 + error = fn_for_each_in_scope(label, profile, 1571 1569 aa_audit_file(subj_cred, 1572 1570 profile, &perms, op, request, auditname, 1573 1571 NULL, new ? new : target,
+34 -15
security/apparmor/file.c
··· 103 103 104 104 ad.subj_cred = subj_cred; 105 105 ad.request = request; 106 + ad.tags = perms->tag; 106 107 ad.name = name; 107 108 ad.fs.target = target; 108 109 ad.peer = tlabel; ··· 155 154 const char *info = NULL; 156 155 int error; 157 156 158 - error = aa_path_name(path, flags, buffer, name, &info, 159 - labels_profile(label)->disconnected); 157 + /* don't reaudit files closed during inheritance */ 158 + if (unlikely(path->dentry == aa_null.dentry)) 159 + error = -EACCES; 160 + else 161 + error = aa_path_name(path, flags, buffer, name, &info, 162 + labels_profile(label)->disconnected); 160 163 if (error) { 161 164 fn_for_each_confined(label, profile, 162 165 aa_audit_file(subj_cred, ··· 572 567 return unconfined(obj_label); 573 568 } 574 569 575 - static bool __unix_needs_revalidation(struct file *file, struct aa_label *label, 576 - u32 request) 570 + static bool __is_unix_file(struct file *file) 577 571 { 578 572 struct socket *sock = (struct socket *) file->private_data; 579 573 ··· 580 576 581 577 if (!S_ISSOCK(file_inode(file)->i_mode)) 582 578 return false; 583 - if (request & NET_PEER_MASK) 579 + /* sock and sock->sk can be NULL for sockets being set up or torn down */ 580 + if (!sock || !sock->sk) 584 581 return false; 585 - if (sock->sk->sk_family == PF_UNIX) { 586 - struct aa_sk_ctx *ctx = aa_sock(sock->sk); 587 - 588 - if (rcu_access_pointer(ctx->peer) != 589 - rcu_access_pointer(ctx->peer_lastupdate)) 590 - return true; 591 - return !__aa_subj_label_is_cached(rcu_dereference(ctx->label), 592 - label); 593 - } 582 + if (sock->sk->sk_family == PF_UNIX) 583 + return true; 594 584 return false; 585 + } 586 + 587 + static bool __unix_needs_revalidation(struct file *file, struct aa_label *label, 588 + u32 request) 589 + { 590 + struct socket *sock = (struct socket *) file->private_data; 591 + 592 + AA_BUG(!__is_unix_file(file)); 593 + lockdep_assert_in_rcu_read_lock(); 594 + 595 + struct aa_sk_ctx *skctx = aa_sock(sock->sk); 596 + 597 + if (rcu_access_pointer(skctx->peer) != 598 + rcu_access_pointer(skctx->peer_lastupdate)) 599 + return true; 600 + 601 + return !__aa_subj_label_is_cached(rcu_dereference(skctx->label), label); 595 602 } 596 603 597 604 /** ··· 628 613 AA_BUG(!label); 629 614 AA_BUG(!file); 630 615 616 + /* don't reaudit files closed during inheritance */ 617 + if (unlikely(file->f_path.dentry == aa_null.dentry)) 618 + return -EACCES; 619 + 631 620 fctx = file_ctx(file); 632 621 633 622 rcu_read_lock(); ··· 647 628 */ 648 629 denied = request & ~fctx->allow; 649 630 if (unconfined(label) || __file_is_delegated(flabel) || 650 - __unix_needs_revalidation(file, label, request) || 631 + (!denied && __is_unix_file(file) && !__unix_needs_revalidation(file, label, request)) || 651 632 (!denied && __aa_subj_label_is_cached(label, flabel))) { 652 633 rcu_read_unlock(); 653 634 goto done;
+2
security/apparmor/include/audit.h
··· 119 119 const char *info; 120 120 u32 request; 121 121 u32 denied; 122 + u32 tags; 123 + 122 124 union { 123 125 /* these entries require a custom callback fn */ 124 126 struct {
+71 -33
security/apparmor/include/cred.h
··· 37 37 } 38 38 39 39 /** 40 - * aa_cred_raw_label - obtain cred's label 41 - * @cred: cred to obtain label from (NOT NULL) 42 - * 43 - * Returns: confining label 44 - * 45 - * does NOT increment reference count 46 - */ 47 - static inline struct aa_label *aa_cred_raw_label(const struct cred *cred) 48 - { 49 - struct aa_label *label = cred_label(cred); 50 - 51 - AA_BUG(!label); 52 - return label; 53 - } 54 - 55 - /** 56 40 * aa_get_newest_cred_label - obtain the newest label on a cred 57 41 * @cred: cred to obtain label from (NOT NULL) 58 42 * ··· 44 60 */ 45 61 static inline struct aa_label *aa_get_newest_cred_label(const struct cred *cred) 46 62 { 47 - return aa_get_newest_label(aa_cred_raw_label(cred)); 63 + return aa_get_newest_label(cred_label(cred)); 48 64 } 49 65 50 66 static inline struct aa_label *aa_get_newest_cred_label_condref(const struct cred *cred, 51 67 bool *needput) 52 68 { 53 - struct aa_label *l = aa_cred_raw_label(cred); 69 + struct aa_label *l = cred_label(cred); 54 70 55 71 if (unlikely(label_is_stale(l))) { 56 72 *needput = true; ··· 77 93 */ 78 94 static inline struct aa_label *aa_current_raw_label(void) 79 95 { 80 - return aa_cred_raw_label(current_cred()); 96 + return cred_label(current_cred()); 81 97 } 82 98 83 99 /** ··· 99 115 } 100 116 101 117 /** 102 - * __end_current_label_crit_section - end crit section begun with __begin_... 103 - * @label: label obtained from __begin_current_label_crit_section 104 - * @needput: output: bool set by __begin_current_label_crit_section 118 + * __end_cred_crit_section - end crit section begun with __begin_... 119 + * @label: label obtained from __begin_cred_crit_section 120 + * @needput: output: bool set by __begin_cred_crit_section 105 121 * 106 - * Returns: label to use for this crit section 122 + * While the cred passed to __begin is guaranteed to not change 123 + * and the cred and label could be passed here instead of needput 124 + * using needput with a local var makes it easier for the compiler 125 + * and processor to optimize and speculatively execute the comparison 126 + * than chasing a pointer in the cred struct. 107 127 */ 108 - static inline void __end_current_label_crit_section(struct aa_label *label, 128 + static inline void __end_cred_crit_section(struct aa_label *label, 109 129 bool needput) 110 130 { 111 131 if (unlikely(needput)) 112 132 aa_put_label(label); 133 + } 134 + 135 + /** 136 + * __begin_cred_crit_section - @cred's confining label 137 + * @cred: current's cred to start a crit section on its label 138 + * @needput: store whether the label needs to be put when ending crit section 139 + * 140 + * Returns: up to date confining label or the ns unconfined label (NOT NULL) 141 + * 142 + * safe to call inside locks 143 + * 144 + * The returned reference must be put with __end_cred_crit_section() 145 + * This must NOT be used if the task cred could be updated within the 146 + * critical section between 147 + * __begin_cred_crit_section() .. __end_cred_crit_section() 148 + * 149 + * The crit section is an optimization to avoid having to get and put 150 + * the newest version of the label. While the cred won't change and 151 + * hence the label it contains won't change, the newest version of the 152 + * label can. During the crit section the newest versions of the label 153 + * will be used until the end of the crit section. 154 + * 155 + * If the label has not been updated at the start of the crit section 156 + * no refcount is taken, the cred's refcount is enough to hold the 157 + * label for the duration of the crit section. 158 + * 159 + * If the label has been updated then a refcount will be taken and the 160 + * newest version of the label will be returned. While the cred label 161 + * and the returned label could be compared at the end of the crit 162 + * section, needput is used because it allows better optimization by 163 + * the compiler and the processor's speculative execution. 164 + */ 165 + static inline struct aa_label *__begin_cred_crit_section(const struct cred *cred, 166 + bool *needput) 167 + { 168 + struct aa_label *label = cred_label(cred); 169 + 170 + if (label_is_stale(label)) { 171 + *needput = true; 172 + return aa_get_newest_label(label); 173 + } 174 + 175 + *needput = false; 176 + return label; 177 + } 178 + 179 + /** 180 + * __end_current_label_crit_section - end crit section begun with __begin_... 181 + * @label: label obtained from __begin_current_label_crit_section 182 + * @needput: output: bool set by __begin_current_label_crit_section 183 + * 184 + * wrapper around __end_cred_crit_section() to pair nicely with 185 + * __begin_current_label_crit_section() 186 + */ 187 + static inline void __end_current_label_crit_section(struct aa_label *label, 188 + bool needput) 189 + { 190 + __end_cred_crit_section(label, needput); 113 191 } 114 192 115 193 /** ··· 203 157 */ 204 158 static inline struct aa_label *__begin_current_label_crit_section(bool *needput) 205 159 { 206 - struct aa_label *label = aa_current_raw_label(); 207 - 208 - if (label_is_stale(label)) { 209 - *needput = true; 210 - return aa_get_newest_label(label); 211 - } 212 - 213 - *needput = false; 214 - return label; 160 + return __begin_cred_crit_section(current_cred(), needput); 215 161 } 216 162 217 163 /**
+31 -8
security/apparmor/include/lib.h
··· 30 30 #define DEBUG_DOMAIN 4 31 31 #define DEBUG_POLICY 8 32 32 #define DEBUG_INTERFACE 0x10 33 + #define DEBUG_UNPACK 0x20 34 + #define DEBUG_TAGS 0x40 33 35 34 - #define DEBUG_ALL 0x1f /* update if new DEBUG_X added */ 36 + #define DEBUG_ALL 0x7f /* update if new DEBUG_X added */ 35 37 #define DEBUG_PARSE_ERROR (-1) 36 38 37 39 #define DEBUG_ON (aa_g_debug != DEBUG_NONE) ··· 47 45 #define AA_DEBUG_LABEL(LAB, X, fmt, args...) \ 48 46 do { \ 49 47 if ((LAB)->flags & FLAG_DEBUG1) \ 50 - AA_DEBUG(X, fmt, args); \ 48 + AA_DEBUG(X, fmt, ##args); \ 51 49 } while (0) 50 + 51 + #define AA_DEBUG_PROFILE(PROF, X, fmt...) AA_DEBUG_LABEL(&(PROF)->label, X, ##fmt) 52 52 53 53 #define AA_WARN(X) WARN((X), "APPARMOR WARN %s: %s\n", __func__, #X) 54 54 ··· 79 75 80 76 /* Flag indicating whether initialization completed */ 81 77 extern int apparmor_initialized; 78 + 79 + /* semantic split of scope and view */ 80 + #define aa_in_scope(SUBJ, OBJ) \ 81 + aa_ns_visible(SUBJ, OBJ, false) 82 + 83 + #define aa_in_view(SUBJ, OBJ) \ 84 + aa_ns_visible(SUBJ, OBJ, true) 85 + 86 + #define label_for_each_in_scope(I, NS, L, P) \ 87 + label_for_each_in_ns(I, NS, L, P) 88 + 89 + #define fn_for_each_in_scope(L, P, FN) \ 90 + fn_for_each_in_ns(L, P, FN) 82 91 83 92 /* fn's in lib */ 84 93 const char *skipn_spaces(const char *str, size_t n); ··· 136 119 return !(dentry->d_sb->s_flags & SB_NOUSER); 137 120 } 138 121 139 - struct aa_str_table { 122 + struct aa_str_table_ent { 123 + int count; 140 124 int size; 141 - char **table; 125 + char *strs; 142 126 }; 143 127 144 - void aa_free_str_table(struct aa_str_table *table); 128 + struct aa_str_table { 129 + int size; 130 + struct aa_str_table_ent *table; 131 + }; 132 + 145 133 bool aa_resize_str_table(struct aa_str_table *t, int newsize, gfp_t gfp); 134 + void aa_destroy_str_table(struct aa_str_table *table); 146 135 147 136 struct counted_str { 148 137 struct kref count; ··· 329 306 }) 330 307 331 308 332 - #define __fn_build_in_ns(NS, P, NS_FN, OTHER_FN) \ 309 + #define __fn_build_in_scope(NS, P, NS_FN, OTHER_FN) \ 333 310 ({ \ 334 311 struct aa_label *__new; \ 335 312 if ((P)->ns != (NS)) \ ··· 339 316 (__new); \ 340 317 }) 341 318 342 - #define fn_label_build_in_ns(L, P, GFP, NS_FN, OTHER_FN) \ 319 + #define fn_label_build_in_scope(L, P, GFP, NS_FN, OTHER_FN) \ 343 320 ({ \ 344 321 fn_label_build((L), (P), (GFP), \ 345 - __fn_build_in_ns(labels_ns(L), (P), (NS_FN), (OTHER_FN))); \ 322 + __fn_build_in_scope(labels_ns(L), (P), (NS_FN), (OTHER_FN))); \ 346 323 }) 347 324 348 325 #endif /* __AA_LIB_H */
+7 -5
security/apparmor/include/match.h
··· 104 104 struct table_header *tables[YYTD_ID_TSIZE]; 105 105 }; 106 106 107 - #define byte_to_byte(X) (X) 108 - 109 107 #define UNPACK_ARRAY(TABLE, BLOB, LEN, TTYPE, BTYPE, NTOHX) \ 110 108 do { \ 111 109 typeof(LEN) __i; \ 112 110 TTYPE *__t = (TTYPE *) TABLE; \ 113 111 BTYPE *__b = (BTYPE *) BLOB; \ 114 - for (__i = 0; __i < LEN; __i++) { \ 115 - __t[__i] = NTOHX(__b[__i]); \ 116 - } \ 112 + BUILD_BUG_ON(sizeof(TTYPE) != sizeof(BTYPE)); \ 113 + if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) \ 114 + memcpy(__t, __b, (LEN) * sizeof(BTYPE)); \ 115 + else /* copy & convert from big-endian */ \ 116 + for (__i = 0; __i < LEN; __i++) { \ 117 + __t[__i] = NTOHX(&__b[__i]); \ 118 + } \ 117 119 } while (0) 118 120 119 121 static inline size_t table_size(size_t len, size_t el_size)
+28 -4
security/apparmor/include/policy.h
··· 79 79 }; 80 80 81 81 82 + struct aa_tags_header { 83 + u32 mask; /* bit mask matching permissions */ 84 + u32 count; /* number of strings per entry */ 85 + u32 size; /* size of all strings covered by count */ 86 + u32 tags; /* index into string table */ 87 + }; 88 + 89 + struct aa_tags_struct { 90 + struct { 91 + u32 size; /* number of entries in tagsets */ 92 + u32 *table; /* indexes into headers & strs */ 93 + } sets; 94 + struct { 95 + u32 size; /* number of headers == num of strs */ 96 + struct aa_tags_header *table; 97 + } hdrs; 98 + struct aa_str_table strs; 99 + }; 100 + 82 101 /* struct aa_policydb - match engine for a policy 83 - * count: refcount for the pdb 84 - * dfa: dfa pattern match 85 - * perms: table of permissions 86 - * strs: table of strings, index by x 102 + * @count: refcount for the pdb 103 + * @dfa: dfa pattern match 104 + * @perms: table of permissions 105 + * @size: number of entries in @perms 106 + * @trans: table of strings, index by x 107 + * @tags: table of tags that perms->tag indexes 108 + * @start:_states to start in for each class 87 109 * start: set of start states for the different classes of data 88 110 */ 89 111 struct aa_policydb { ··· 116 94 u32 size; 117 95 }; 118 96 struct aa_str_table trans; 97 + struct aa_tags_struct tags; 119 98 aa_state_t start[AA_CLASS_LAST + 1]; 120 99 }; 121 100 122 101 extern struct aa_policydb *nullpdb; 123 102 103 + void aa_destroy_tags(struct aa_tags_struct *tags); 124 104 struct aa_policydb *aa_alloc_pdb(gfp_t gfp); 125 105 void aa_pdb_free_kref(struct kref *kref); 126 106
+25 -30
security/apparmor/label.c
··· 1274 1274 * @rules: ruleset to search 1275 1275 * @label: label to check access permissions for 1276 1276 * @state: state to start match in 1277 - * @subns: whether to do permission checks on components in a subns 1277 + * @inview: whether to match labels in view or only in scope 1278 1278 * @request: permissions to request 1279 1279 * @perms: perms struct to set 1280 1280 * 1281 - * Returns: 0 on success else ERROR 1281 + * Returns: state match stopped at or DFA_NOMATCH if aborted early 1282 1282 * 1283 1283 * For the label A//&B//&C this does the perm match for A//&B//&C 1284 1284 * @perms should be preinitialized with allperms OR a previous permission ··· 1287 1287 static int label_compound_match(struct aa_profile *profile, 1288 1288 struct aa_ruleset *rules, 1289 1289 struct aa_label *label, 1290 - aa_state_t state, bool subns, u32 request, 1290 + aa_state_t state, bool inview, u32 request, 1291 1291 struct aa_perms *perms) 1292 1292 { 1293 1293 struct aa_profile *tp; ··· 1295 1295 1296 1296 /* find first subcomponent that is visible */ 1297 1297 label_for_each(i, label, tp) { 1298 - if (!aa_ns_visible(profile->ns, tp->ns, subns)) 1298 + if (!aa_ns_visible(profile->ns, tp->ns, inview)) 1299 1299 continue; 1300 1300 state = match_component(profile, rules, tp, state); 1301 1301 if (!state) ··· 1305 1305 1306 1306 /* no component visible */ 1307 1307 *perms = allperms; 1308 - return 0; 1308 + return state; 1309 1309 1310 1310 next: 1311 1311 label_for_each_cont(i, label, tp) { 1312 - if (!aa_ns_visible(profile->ns, tp->ns, subns)) 1312 + if (!aa_ns_visible(profile->ns, tp->ns, inview)) 1313 1313 continue; 1314 1314 state = aa_dfa_match(rules->policy->dfa, state, "//&"); 1315 1315 state = match_component(profile, rules, tp, state); ··· 1317 1317 goto fail; 1318 1318 } 1319 1319 *perms = *aa_lookup_perms(rules->policy, state); 1320 - aa_apply_modes_to_perms(profile, perms); 1321 - if ((perms->allow & request) != request) 1322 - return -EACCES; 1323 - 1324 - return 0; 1320 + return state; 1325 1321 1326 1322 fail: 1327 1323 *perms = nullperms; 1328 - return state; 1324 + return DFA_NOMATCH; 1329 1325 } 1330 1326 1331 1327 /** ··· 1330 1334 * @rules: ruleset to search 1331 1335 * @label: label to check access permissions for 1332 1336 * @start: state to start match in 1333 - * @subns: whether to do permission checks on components in a subns 1337 + * @inview: whether to match labels in view or only in scope 1334 1338 * @request: permissions to request 1335 1339 * @perms: an initialized perms struct to add accumulation to 1336 1340 * 1337 - * Returns: 0 on success else ERROR 1341 + * Returns: the state the match finished in, may be the none matching state 1338 1342 * 1339 1343 * For the label A//&B//&C this does the perm match for each of A and B and C 1340 1344 * @perms should be preinitialized with allperms OR a previous permission ··· 1343 1347 static int label_components_match(struct aa_profile *profile, 1344 1348 struct aa_ruleset *rules, 1345 1349 struct aa_label *label, aa_state_t start, 1346 - bool subns, u32 request, 1350 + bool inview, u32 request, 1347 1351 struct aa_perms *perms) 1348 1352 { 1349 1353 struct aa_profile *tp; ··· 1353 1357 1354 1358 /* find first subcomponent to test */ 1355 1359 label_for_each(i, label, tp) { 1356 - if (!aa_ns_visible(profile->ns, tp->ns, subns)) 1360 + if (!aa_ns_visible(profile->ns, tp->ns, inview)) 1357 1361 continue; 1358 1362 state = match_component(profile, rules, tp, start); 1359 1363 if (!state) ··· 1362 1366 } 1363 1367 1364 1368 /* no subcomponents visible - no change in perms */ 1365 - return 0; 1369 + return state; 1366 1370 1367 1371 next: 1368 1372 tmp = *aa_lookup_perms(rules->policy, state); 1369 - aa_apply_modes_to_perms(profile, &tmp); 1370 1373 aa_perms_accum(perms, &tmp); 1371 1374 label_for_each_cont(i, label, tp) { 1372 - if (!aa_ns_visible(profile->ns, tp->ns, subns)) 1375 + if (!aa_ns_visible(profile->ns, tp->ns, inview)) 1373 1376 continue; 1374 1377 state = match_component(profile, rules, tp, start); 1375 1378 if (!state) 1376 1379 goto fail; 1377 1380 tmp = *aa_lookup_perms(rules->policy, state); 1378 - aa_apply_modes_to_perms(profile, &tmp); 1379 1381 aa_perms_accum(perms, &tmp); 1380 1382 } 1381 1383 1382 1384 if ((perms->allow & request) != request) 1383 - return -EACCES; 1385 + return DFA_NOMATCH; 1384 1386 1385 - return 0; 1387 + return state; 1386 1388 1387 1389 fail: 1388 1390 *perms = nullperms; 1389 - return -EACCES; 1391 + return DFA_NOMATCH; 1390 1392 } 1391 1393 1392 1394 /** ··· 1393 1399 * @rules: ruleset to search 1394 1400 * @label: label to match (NOT NULL) 1395 1401 * @state: state to start in 1396 - * @subns: whether to match subns components 1402 + * @inview: whether to match labels in view or only in scope 1397 1403 * @request: permission request 1398 1404 * @perms: Returns computed perms (NOT NULL) 1399 1405 * 1400 1406 * Returns: the state the match finished in, may be the none matching state 1401 1407 */ 1402 1408 int aa_label_match(struct aa_profile *profile, struct aa_ruleset *rules, 1403 - struct aa_label *label, aa_state_t state, bool subns, 1409 + struct aa_label *label, aa_state_t state, bool inview, 1404 1410 u32 request, struct aa_perms *perms) 1405 1411 { 1406 - int error = label_compound_match(profile, rules, label, state, subns, 1407 - request, perms); 1408 - if (!error) 1409 - return error; 1412 + aa_state_t tmp = label_compound_match(profile, rules, label, state, 1413 + inview, request, perms); 1414 + if ((perms->allow & request) == request) 1415 + return tmp; 1410 1416 1417 + /* failed compound_match try component matches */ 1411 1418 *perms = allperms; 1412 - return label_components_match(profile, rules, label, state, subns, 1419 + return label_components_match(profile, rules, label, state, inview, 1413 1420 request, perms); 1414 1421 } 1415 1422
+15 -14
security/apparmor/lib.c
··· 44 44 { "domain", DEBUG_DOMAIN }, 45 45 { "policy", DEBUG_POLICY }, 46 46 { "interface", DEBUG_INTERFACE }, 47 + { "unpack", DEBUG_UNPACK }, 48 + { "tags", DEBUG_TAGS }, 47 49 { NULL, 0 } 48 50 }; 49 51 ··· 120 118 121 119 bool aa_resize_str_table(struct aa_str_table *t, int newsize, gfp_t gfp) 122 120 { 123 - char **n; 121 + struct aa_str_table_ent *n; 124 122 int i; 125 123 126 124 if (t->size == newsize) ··· 131 129 for (i = 0; i < min(t->size, newsize); i++) 132 130 n[i] = t->table[i]; 133 131 for (; i < t->size; i++) 134 - kfree_sensitive(t->table[i]); 132 + kfree_sensitive(t->table[i].strs); 135 133 if (newsize > t->size) 136 134 memset(&n[t->size], 0, (newsize-t->size)*sizeof(*n)); 137 135 kfree_sensitive(t->table); ··· 142 140 } 143 141 144 142 /** 145 - * aa_free_str_table - free entries str table 143 + * aa_destroy_str_table - free entries str table 146 144 * @t: the string table to free (MAYBE NULL) 147 145 */ 148 - void aa_free_str_table(struct aa_str_table *t) 146 + void aa_destroy_str_table(struct aa_str_table *t) 149 147 { 150 148 int i; 151 149 ··· 154 152 return; 155 153 156 154 for (i = 0; i < t->size; i++) 157 - kfree_sensitive(t->table[i]); 155 + kfree_sensitive(t->table[i].strs); 158 156 kfree_sensitive(t->table); 159 157 t->table = NULL; 160 158 t->size = 0; ··· 480 478 const char *name, gfp_t gfp) 481 479 { 482 480 char *hname; 481 + size_t hname_sz; 483 482 483 + hname_sz = (prefix ? strlen(prefix) + 2 : 0) + strlen(name) + 1; 484 484 /* freed by policy_free */ 485 - if (prefix) { 486 - hname = aa_str_alloc(strlen(prefix) + strlen(name) + 3, gfp); 487 - if (hname) 488 - sprintf(hname, "%s//%s", prefix, name); 489 - } else { 490 - hname = aa_str_alloc(strlen(name) + 1, gfp); 491 - if (hname) 492 - strcpy(hname, name); 493 - } 485 + hname = aa_str_alloc(hname_sz, gfp); 494 486 if (!hname) 495 487 return false; 488 + if (prefix) 489 + scnprintf(hname, hname_sz, "%s//%s", prefix, name); 490 + else 491 + strscpy(hname, name, hname_sz); 496 492 policy->hname = hname; 497 493 /* base.name is a substring of fqname */ 498 494 policy->name = basename(policy->hname); ··· 512 512 /* don't free name as its a subset of hname */ 513 513 aa_put_str(policy->hname); 514 514 } 515 +
+39 -27
security/apparmor/lsm.c
··· 520 520 aa_put_label(rcu_access_pointer(ctx->label)); 521 521 } 522 522 523 - static int common_file_perm(const char *op, struct file *file, u32 mask, 524 - bool in_atomic) 523 + static int common_file_perm(const char *op, struct file *file, u32 mask) 525 524 { 526 525 struct aa_label *label; 527 526 int error = 0; 528 - bool needput; 529 527 530 - /* don't reaudit files closed during inheritance */ 531 - if (unlikely(file->f_path.dentry == aa_null.dentry)) 532 - return -EACCES; 533 - 534 - label = __begin_current_label_crit_section(&needput); 535 - error = aa_file_perm(op, current_cred(), label, file, mask, in_atomic); 536 - __end_current_label_crit_section(label, needput); 528 + label = begin_current_label_crit_section(); 529 + error = aa_file_perm(op, current_cred(), label, file, mask, false); 530 + end_current_label_crit_section(label); 537 531 538 532 return error; 539 533 } 540 534 541 535 static int apparmor_file_receive(struct file *file) 542 536 { 543 - return common_file_perm(OP_FRECEIVE, file, aa_map_file_to_perms(file), 544 - false); 537 + return common_file_perm(OP_FRECEIVE, file, aa_map_file_to_perms(file)); 545 538 } 546 539 547 540 static int apparmor_file_permission(struct file *file, int mask) 548 541 { 549 - return common_file_perm(OP_FPERM, file, mask, false); 542 + return common_file_perm(OP_FPERM, file, mask); 550 543 } 551 544 552 545 static int apparmor_file_lock(struct file *file, unsigned int cmd) ··· 549 556 if (cmd == F_WRLCK) 550 557 mask |= MAY_WRITE; 551 558 552 - return common_file_perm(OP_FLOCK, file, mask, false); 559 + return common_file_perm(OP_FLOCK, file, mask); 553 560 } 554 561 555 562 static int common_mmap(const char *op, struct file *file, unsigned long prot, 556 - unsigned long flags, bool in_atomic) 563 + unsigned long flags) 557 564 { 558 565 int mask = 0; 559 566 ··· 571 578 if (prot & PROT_EXEC) 572 579 mask |= AA_EXEC_MMAP; 573 580 574 - return common_file_perm(op, file, mask, in_atomic); 581 + return common_file_perm(op, file, mask); 575 582 } 576 583 577 584 static int apparmor_mmap_file(struct file *file, unsigned long reqprot, 578 585 unsigned long prot, unsigned long flags) 579 586 { 580 - return common_mmap(OP_FMMAP, file, prot, flags, GFP_ATOMIC); 587 + return common_mmap(OP_FMMAP, file, prot, flags); 581 588 } 582 589 583 590 static int apparmor_file_mprotect(struct vm_area_struct *vma, 584 591 unsigned long reqprot, unsigned long prot) 585 592 { 586 593 return common_mmap(OP_FMPROT, vma->vm_file, prot, 587 - !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0, 588 - false); 594 + !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); 589 595 } 590 596 591 597 #ifdef CONFIG_IO_URING ··· 2125 2133 return 0; 2126 2134 } 2127 2135 2136 + /* arbitrary cap on how long to hold buffer because contention was 2137 + * encountered before trying to put it back into the global pool 2138 + */ 2139 + #define MAX_HOLD_COUNT 64 2140 + 2141 + /* the hold count is a heuristic for lock contention, and can be 2142 + * incremented async to actual buffer alloc/free. Because buffers 2143 + * may be put back onto a percpu cache different than the ->hold was 2144 + * added to the counts can be out of sync. Guard against underflow 2145 + * and overflow 2146 + */ 2147 + static void cache_hold_inc(unsigned int *hold) 2148 + { 2149 + if (*hold > MAX_HOLD_COUNT) 2150 + (*hold)++; 2151 + } 2152 + 2128 2153 char *aa_get_buffer(bool in_atomic) 2129 2154 { 2130 2155 union aa_buffer *aa_buf; ··· 2154 2145 if (!list_empty(&cache->head)) { 2155 2146 aa_buf = list_first_entry(&cache->head, union aa_buffer, list); 2156 2147 list_del(&aa_buf->list); 2157 - cache->hold--; 2148 + if (cache->hold) 2149 + cache->hold--; 2158 2150 cache->count--; 2159 2151 put_cpu_ptr(&aa_local_buffers); 2160 2152 return &aa_buf->buffer[0]; 2161 2153 } 2154 + /* exit percpu as spinlocks may sleep on realtime kernels */ 2162 2155 put_cpu_ptr(&aa_local_buffers); 2163 2156 2164 2157 if (!spin_trylock(&aa_buffers_lock)) { 2158 + /* had contention on lock so increase hold count. Doesn't 2159 + * really matter if recorded before or after the spin lock 2160 + * as there is no way to guarantee the buffer will be put 2161 + * back on the same percpu cache. Instead rely on holds 2162 + * roughly averaging out over time. 2163 + */ 2165 2164 cache = get_cpu_ptr(&aa_local_buffers); 2166 - cache->hold += 1; 2165 + cache_hold_inc(&cache->hold); 2167 2166 put_cpu_ptr(&aa_local_buffers); 2168 2167 spin_lock(&aa_buffers_lock); 2169 - } else { 2170 - cache = get_cpu_ptr(&aa_local_buffers); 2171 - put_cpu_ptr(&aa_local_buffers); 2172 2168 } 2173 2169 retry: 2174 2170 if (buffer_count > reserve_count || ··· 2228 2214 list_add(&aa_buf->list, &aa_global_buffers); 2229 2215 buffer_count++; 2230 2216 spin_unlock(&aa_buffers_lock); 2231 - cache = get_cpu_ptr(&aa_local_buffers); 2232 - put_cpu_ptr(&aa_local_buffers); 2233 2217 return; 2234 2218 } 2235 2219 /* contention on global list, fallback to percpu */ 2236 2220 cache = get_cpu_ptr(&aa_local_buffers); 2237 - cache->hold += 1; 2221 + cache_hold_inc(&cache->hold); 2238 2222 } 2239 2223 2240 2224 /* cache in percpu list */
+11 -11
security/apparmor/match.c
··· 15 15 #include <linux/vmalloc.h> 16 16 #include <linux/err.h> 17 17 #include <linux/kref.h> 18 + #include <linux/unaligned.h> 18 19 19 20 #include "include/lib.h" 20 21 #include "include/match.h" ··· 43 42 /* loaded td_id's start at 1, subtract 1 now to avoid doing 44 43 * it every time we use td_id as an index 45 44 */ 46 - th.td_id = be16_to_cpu(*(__be16 *) (blob)) - 1; 45 + th.td_id = get_unaligned_be16(blob) - 1; 47 46 if (th.td_id > YYTD_ID_MAX) 48 47 goto out; 49 - th.td_flags = be16_to_cpu(*(__be16 *) (blob + 2)); 50 - th.td_lolen = be32_to_cpu(*(__be32 *) (blob + 8)); 48 + th.td_flags = get_unaligned_be16(blob + 2); 49 + th.td_lolen = get_unaligned_be32(blob + 8); 51 50 blob += sizeof(struct table_header); 52 51 53 52 if (!(th.td_flags == YYTD_DATA16 || th.td_flags == YYTD_DATA32 || ··· 67 66 table->td_flags = th.td_flags; 68 67 table->td_lolen = th.td_lolen; 69 68 if (th.td_flags == YYTD_DATA8) 70 - UNPACK_ARRAY(table->td_data, blob, th.td_lolen, 71 - u8, u8, byte_to_byte); 69 + memcpy(table->td_data, blob, th.td_lolen); 72 70 else if (th.td_flags == YYTD_DATA16) 73 71 UNPACK_ARRAY(table->td_data, blob, th.td_lolen, 74 - u16, __be16, be16_to_cpu); 72 + u16, __be16, get_unaligned_be16); 75 73 else if (th.td_flags == YYTD_DATA32) 76 74 UNPACK_ARRAY(table->td_data, blob, th.td_lolen, 77 - u32, __be32, be32_to_cpu); 75 + u32, __be32, get_unaligned_be32); 78 76 else 79 77 goto fail; 80 78 /* if table was vmalloced make sure the page tables are synced ··· 313 313 if (size < sizeof(struct table_set_header)) 314 314 goto fail; 315 315 316 - if (ntohl(*(__be32 *) data) != YYTH_MAGIC) 316 + if (get_unaligned_be32(data) != YYTH_MAGIC) 317 317 goto fail; 318 318 319 - hsize = ntohl(*(__be32 *) (data + 4)); 319 + hsize = get_unaligned_be32(data + 4); 320 320 if (size < hsize) 321 321 goto fail; 322 322 323 - dfa->flags = ntohs(*(__be16 *) (data + 12)); 323 + dfa->flags = get_unaligned_be16(data + 12); 324 324 if (dfa->flags & ~(YYTH_FLAGS)) 325 325 goto fail; 326 326 ··· 329 329 * if (dfa->flags & YYTH_FLAGS_OOB_TRANS) { 330 330 * if (hsize < 16 + 4) 331 331 * goto fail; 332 - * dfa->max_oob = ntol(*(__be32 *) (data + 16)); 332 + * dfa->max_oob = get_unaligned_be32(data + 16); 333 333 * if (dfa->max <= MAX_OOB_SUPPORTED) { 334 334 * pr_err("AppArmor DFA OOB greater than supported\n"); 335 335 * goto fail;
+4 -2
security/apparmor/net.c
··· 326 326 struct socket *sock = (struct socket *) file->private_data; 327 327 328 328 AA_BUG(!label); 329 - AA_BUG(!sock); 330 - AA_BUG(!sock->sk); 329 + 330 + /* sock && sock->sk can be NULL for sockets being set up or torn down */ 331 + if (!sock || !sock->sk) 332 + return 0; 331 333 332 334 if (sock->sk->sk_family == PF_UNIX) 333 335 return aa_unix_file_perm(subj_cred, label, op, request, file);
+8 -5
security/apparmor/path.c
··· 164 164 } 165 165 166 166 out: 167 - /* 168 - * Append "/" to the pathname. The root directory is a special 169 - * case; it already ends in slash. 167 + /* Append "/" to directory paths, except for root "/" which 168 + * already ends in a slash. 170 169 */ 171 - if (!error && isdir && ((*name)[1] != '\0' || (*name)[0] != '/')) 172 - strcpy(&buf[aa_g_path_max - 2], "/"); 170 + if (!error && isdir) { 171 + bool is_root = (*name)[0] == '/' && (*name)[1] == '\0'; 172 + 173 + if (!is_root) 174 + buf[aa_g_path_max - 2] = '/'; 175 + } 173 176 174 177 return error; 175 178 }
+24 -7
security/apparmor/policy.c
··· 98 98 "user", 99 99 }; 100 100 101 + void aa_destroy_tags(struct aa_tags_struct *tags) 102 + { 103 + kfree_sensitive(tags->hdrs.table); 104 + kfree_sensitive(tags->sets.table); 105 + aa_destroy_str_table(&tags->strs); 106 + memset(tags, 0, sizeof(*tags)); 107 + } 101 108 102 109 static void aa_free_pdb(struct aa_policydb *pdb) 103 110 { 104 111 if (pdb) { 105 112 aa_put_dfa(pdb->dfa); 106 113 kvfree(pdb->perms); 107 - aa_free_str_table(&pdb->trans); 114 + aa_destroy_str_table(&pdb->trans); 115 + aa_destroy_tags(&pdb->tags); 108 116 kfree(pdb); 109 117 } 110 118 } ··· 232 224 { 233 225 struct aa_data *data = ptr; 234 226 227 + if (!ptr) 228 + return; 229 + 235 230 kvfree_sensitive(data->data, data->size); 236 231 kfree_sensitive(data->key); 237 232 kfree_sensitive(data); ··· 243 232 static void free_attachment(struct aa_attachment *attach) 244 233 { 245 234 int i; 235 + 236 + if (!attach) 237 + return; 246 238 247 239 for (i = 0; i < attach->xattr_count; i++) 248 240 kfree_sensitive(attach->xattrs[i]); ··· 711 697 struct aa_profile *p, *profile; 712 698 const char *bname; 713 699 char *name = NULL; 700 + size_t name_sz; 714 701 715 702 AA_BUG(!parent); 716 703 717 704 if (base) { 718 - name = kmalloc(strlen(parent->base.hname) + 8 + strlen(base), 719 - gfp); 705 + name_sz = strlen(parent->base.hname) + 8 + strlen(base); 706 + name = kmalloc(name_sz, gfp); 720 707 if (name) { 721 - sprintf(name, "%s//null-%s", parent->base.hname, base); 708 + snprintf(name, name_sz, "%s//null-%s", 709 + parent->base.hname, base); 722 710 goto name; 723 711 } 724 712 /* fall through to try shorter uniq */ 725 713 } 726 714 727 - name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, gfp); 715 + name_sz = strlen(parent->base.hname) + 2 + 7 + 8; 716 + name = kmalloc(name_sz, gfp); 728 717 if (!name) 729 718 return NULL; 730 - sprintf(name, "%s//null-%x", parent->base.hname, 731 - atomic_inc_return(&parent->ns->uniq_null)); 719 + snprintf(name, name_sz, "%s//null-%x", parent->base.hname, 720 + atomic_inc_return(&parent->ns->uniq_null)); 732 721 733 722 name: 734 723 /* lookup to see if this is a dup creation */
+8 -2
security/apparmor/policy_compat.c
··· 263 263 *size = state_count; 264 264 265 265 /* zero init so skip the trap state (state == 0) */ 266 - for (state = 1; state < state_count; state++) 266 + for (state = 1; state < state_count; state++) { 267 267 table[state] = compute_perms_entry(dfa, state, version); 268 - 268 + AA_DEBUG(DEBUG_UNPACK, 269 + "[%d]: (0x%x/0x%x/0x%x//0x%x/0x%x//0x%x), converted from accept1: 0x%x, accept2: 0x%x", 270 + state, table[state].allow, table[state].deny, 271 + table[state].prompt, table[state].audit, 272 + table[state].quiet, table[state].xindex, 273 + ACCEPT_TABLE(dfa)[state], ACCEPT_TABLE2(dfa)[state]); 274 + } 269 275 return table; 270 276 } 271 277
+291 -45
security/apparmor/policy_unpack.c
··· 450 450 return dfa; 451 451 } 452 452 453 + static int process_strs_entry(char *str, int size, bool multi) 454 + { 455 + int c = 1; 456 + 457 + if (size <= 0) 458 + return -1; 459 + if (multi) { 460 + if (size < 2) 461 + return -2; 462 + /* multi ends with double \0 */ 463 + if (str[size - 2]) 464 + return -3; 465 + } 466 + 467 + char *save = str; 468 + char *pos = str; 469 + char *end = multi ? str + size - 2 : str + size - 1; 470 + /* count # of internal \0 */ 471 + while (str < end) { 472 + if (str == pos) { 473 + /* starts with ... */ 474 + if (!*str) { 475 + AA_DEBUG(DEBUG_UNPACK, 476 + "starting with null save=%lu size %d c=%d", 477 + (unsigned long)(str - save), size, c); 478 + return -4; 479 + } 480 + if (isspace(*str)) 481 + return -5; 482 + if (*str == ':') { 483 + /* :ns_str\0str\0 484 + * first character after : must be valid 485 + */ 486 + if (!str[1]) 487 + return -6; 488 + } 489 + } else if (!*str) { 490 + if (*pos == ':') 491 + *str = ':'; 492 + else 493 + c++; 494 + pos = str + 1; 495 + } 496 + str++; 497 + } /* while */ 498 + 499 + return c; 500 + } 501 + 453 502 /** 454 - * unpack_trans_table - unpack a profile transition table 503 + * unpack_strs_table - unpack a profile transition table 455 504 * @e: serialized data extent information (NOT NULL) 505 + * @name: name of table (MAY BE NULL) 506 + * @multi: allow multiple strings on a single entry 456 507 * @strs: str table to unpack to (NOT NULL) 457 508 * 458 - * Returns: true if table successfully unpacked or not present 509 + * Returns: 0 if table successfully unpacked or not present, else error 459 510 */ 460 - static bool unpack_trans_table(struct aa_ext *e, struct aa_str_table *strs) 511 + static int unpack_strs_table(struct aa_ext *e, const char *name, bool multi, 512 + struct aa_str_table *strs) 461 513 { 462 514 void *saved_pos = e->pos; 463 - char **table = NULL; 515 + struct aa_str_table_ent *table = NULL; 516 + int error = -EPROTO; 464 517 465 518 /* exec table is optional */ 466 - if (aa_unpack_nameX(e, AA_STRUCT, "xtable")) { 519 + if (aa_unpack_nameX(e, AA_STRUCT, name)) { 467 520 u16 size; 468 521 int i; 469 522 ··· 528 475 * for size check here 529 476 */ 530 477 goto fail; 531 - table = kcalloc(size, sizeof(char *), GFP_KERNEL); 532 - if (!table) 478 + table = kcalloc(size, sizeof(struct aa_str_table_ent), 479 + GFP_KERNEL); 480 + if (!table) { 481 + error = -ENOMEM; 533 482 goto fail; 534 - 483 + } 535 484 strs->table = table; 536 485 strs->size = size; 537 486 for (i = 0; i < size; i++) { 538 487 char *str; 539 - int c, j, pos, size2 = aa_unpack_strdup(e, &str, NULL); 488 + int c, size2 = aa_unpack_strdup(e, &str, NULL); 540 489 /* aa_unpack_strdup verifies that the last character is 541 490 * null termination byte. 542 491 */ 543 - if (!size2) 492 + c = process_strs_entry(str, size2, multi); 493 + if (c <= 0) { 494 + AA_DEBUG(DEBUG_UNPACK, "process_strs %d i %d pos %ld", 495 + c, i, 496 + (unsigned long)(e->pos - saved_pos)); 544 497 goto fail; 545 - table[i] = str; 546 - /* verify that name doesn't start with space */ 547 - if (isspace(*str)) 548 - goto fail; 549 - 550 - /* count internal # of internal \0 */ 551 - for (c = j = 0; j < size2 - 1; j++) { 552 - if (!str[j]) { 553 - pos = j; 554 - c++; 555 - } 556 498 } 557 - if (*str == ':') { 558 - /* first character after : must be valid */ 559 - if (!str[1]) 560 - goto fail; 561 - /* beginning with : requires an embedded \0, 562 - * verify that exactly 1 internal \0 exists 563 - * trailing \0 already verified by aa_unpack_strdup 564 - * 565 - * convert \0 back to : for label_parse 566 - */ 567 - if (c == 1) 568 - str[pos] = ':'; 569 - else if (c > 1) 570 - goto fail; 571 - } else if (c) 499 + if (!multi && c > 1) { 500 + AA_DEBUG(DEBUG_UNPACK, "!multi && c > 1"); 572 501 /* fail - all other cases with embedded \0 */ 573 502 goto fail; 503 + } 504 + table[i].strs = str; 505 + table[i].count = c; 506 + table[i].size = size2; 574 507 } 575 508 if (!aa_unpack_nameX(e, AA_ARRAYEND, NULL)) 576 509 goto fail; 577 510 if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL)) 578 511 goto fail; 579 512 } 580 - return true; 513 + return 0; 581 514 582 515 fail: 583 - aa_free_str_table(strs); 516 + aa_destroy_str_table(strs); 584 517 e->pos = saved_pos; 585 - return false; 518 + return error; 586 519 } 587 520 588 521 static bool unpack_xattrs(struct aa_ext *e, struct aa_profile *profile) ··· 683 644 return false; 684 645 } 685 646 647 + 648 + static bool verify_tags(struct aa_tags_struct *tags, const char **info) 649 + { 650 + if ((tags->hdrs.size && !tags->hdrs.table) || 651 + (!tags->hdrs.size && tags->hdrs.table)) { 652 + *info = "failed verification tag.hdrs disagree"; 653 + return false; 654 + } 655 + if ((tags->sets.size && !tags->sets.table) || 656 + (!tags->sets.size && tags->sets.table)) { 657 + *info = "failed verification tag.sets disagree"; 658 + return false; 659 + } 660 + if ((tags->strs.size && !tags->strs.table) || 661 + (!tags->strs.size && tags->strs.table)) { 662 + *info = "failed verification tags->strs disagree"; 663 + return false; 664 + } 665 + /* no data present */ 666 + if (!tags->sets.size && !tags->hdrs.size && !tags->strs.size) { 667 + return true; 668 + } else if (!(tags->sets.size && tags->hdrs.size && tags->strs.size)) { 669 + /* some data present but not all */ 670 + *info = "failed verification tags partial data present"; 671 + return false; 672 + } 673 + 674 + u32 i; 675 + 676 + for (i = 0; i < tags->sets.size; i++) { 677 + /* count followed by count indexes into hdrs */ 678 + u32 cnt = tags->sets.table[i]; 679 + 680 + if (i+cnt >= tags->sets.size) { 681 + AA_DEBUG(DEBUG_UNPACK, 682 + "tagset too large %d+%d > sets.table[%d]", 683 + i, cnt, tags->sets.size); 684 + *info = "failed verification tagset too large"; 685 + return false; 686 + } 687 + for (; cnt; cnt--) { 688 + if (tags->sets.table[++i] >= tags->hdrs.size) { 689 + AA_DEBUG(DEBUG_UNPACK, 690 + "tagsets idx out of bounds cnt %d sets.table[%d] >= %d", 691 + cnt, i-1, tags->hdrs.size); 692 + *info = "failed verification tagsets idx out of bounds"; 693 + return false; 694 + } 695 + } 696 + } 697 + for (i = 0; i < tags->hdrs.size; i++) { 698 + u32 idx = tags->hdrs.table[i].tags; 699 + 700 + if (idx >= tags->strs.size) { 701 + AA_DEBUG(DEBUG_UNPACK, 702 + "tag.hdrs idx oob idx %d > tags->strs.size=%d", 703 + idx, tags->strs.size); 704 + *info = "failed verification tags.hdrs idx out of bounds"; 705 + return false; 706 + } 707 + if (tags->hdrs.table[i].count != tags->strs.table[idx].count) { 708 + AA_DEBUG(DEBUG_UNPACK, "hdrs.table[%d].count=%d != tags->strs.table[%d]=%d", 709 + i, tags->hdrs.table[i].count, idx, tags->strs.table[idx].count); 710 + *info = "failed verification tagd.hdrs[idx].count"; 711 + return false; 712 + } 713 + if (tags->hdrs.table[i].size != tags->strs.table[idx].size) { 714 + AA_DEBUG(DEBUG_UNPACK, "hdrs.table[%d].size=%d != strs.table[%d].size=%d", 715 + i, tags->hdrs.table[i].size, idx, tags->strs.table[idx].size); 716 + *info = "failed verification tagd.hdrs[idx].size"; 717 + return false; 718 + } 719 + } 720 + 721 + return true; 722 + } 723 + 724 + static int unpack_tagsets(struct aa_ext *e, struct aa_tags_struct *tags) 725 + { 726 + u32 *sets; 727 + u16 i, size; 728 + int error = -EPROTO; 729 + void *pos = e->pos; 730 + 731 + if (!aa_unpack_array(e, "sets", &size)) 732 + goto fail_reset; 733 + sets = kcalloc(size, sizeof(u32), GFP_KERNEL); 734 + if (!sets) { 735 + error = -ENOMEM; 736 + goto fail_reset; 737 + } 738 + for (i = 0; i < size; i++) { 739 + if (!aa_unpack_u32(e, &sets[i], NULL)) 740 + goto fail; 741 + } 742 + if (!aa_unpack_nameX(e, AA_ARRAYEND, NULL)) 743 + goto fail; 744 + 745 + tags->sets.size = size; 746 + tags->sets.table = sets; 747 + 748 + return 0; 749 + 750 + fail: 751 + kfree_sensitive(sets); 752 + fail_reset: 753 + e->pos = pos; 754 + return error; 755 + } 756 + 757 + static bool unpack_tag_header_ent(struct aa_ext *e, struct aa_tags_header *h) 758 + { 759 + return aa_unpack_u32(e, &h->mask, NULL) && 760 + aa_unpack_u32(e, &h->count, NULL) && 761 + aa_unpack_u32(e, &h->size, NULL) && 762 + aa_unpack_u32(e, &h->tags, NULL); 763 + } 764 + 765 + static int unpack_tag_headers(struct aa_ext *e, struct aa_tags_struct *tags) 766 + { 767 + struct aa_tags_header *hdrs; 768 + u16 i, size; 769 + int error = -EPROTO; 770 + void *pos = e->pos; 771 + 772 + if (!aa_unpack_array(e, "hdrs", &size)) 773 + goto fail_reset; 774 + hdrs = kcalloc(size, sizeof(struct aa_tags_header), GFP_KERNEL); 775 + if (!hdrs) { 776 + error = -ENOMEM; 777 + goto fail_reset; 778 + } 779 + for (i = 0; i < size; i++) { 780 + if (!unpack_tag_header_ent(e, &hdrs[i])) 781 + goto fail; 782 + } 783 + if (!aa_unpack_nameX(e, AA_ARRAYEND, NULL)) 784 + goto fail; 785 + 786 + tags->hdrs.size = size; 787 + tags->hdrs.table = hdrs; 788 + AA_DEBUG(DEBUG_UNPACK, "headers %ld size %d", (long) hdrs, size); 789 + return true; 790 + 791 + fail: 792 + kfree_sensitive(hdrs); 793 + fail_reset: 794 + e->pos = pos; 795 + return error; 796 + } 797 + 798 + 799 + static int unpack_tags(struct aa_ext *e, struct aa_tags_struct *tags, 800 + const char **info) 801 + { 802 + int error = -EPROTO; 803 + void *pos = e->pos; 804 + 805 + AA_BUG(!tags); 806 + /* policy tags are optional */ 807 + if (aa_unpack_nameX(e, AA_STRUCT, "tags")) { 808 + u32 version; 809 + 810 + if (!aa_unpack_u32(e, &version, "version") || version != 1) { 811 + *info = "invalid tags version"; 812 + goto fail_reset; 813 + } 814 + error = unpack_strs_table(e, "strs", true, &tags->strs); 815 + if (error) { 816 + *info = "failed to unpack profile tag.strs"; 817 + goto fail; 818 + } 819 + error = unpack_tag_headers(e, tags); 820 + if (error) { 821 + *info = "failed to unpack profile tag.headers"; 822 + goto fail; 823 + } 824 + error = unpack_tagsets(e, tags); 825 + if (error) { 826 + *info = "failed to unpack profile tag.sets"; 827 + goto fail; 828 + } 829 + if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL)) 830 + goto fail; 831 + 832 + if (!verify_tags(tags, info)) 833 + goto fail; 834 + } 835 + 836 + return 0; 837 + 838 + fail: 839 + aa_destroy_tags(tags); 840 + fail_reset: 841 + e->pos = pos; 842 + return error; 843 + } 844 + 686 845 static bool unpack_perm(struct aa_ext *e, u32 version, struct aa_perms *perm) 687 846 { 688 847 u32 reserved; ··· 924 687 if (!aa_unpack_array(e, NULL, &size)) 925 688 goto fail_reset; 926 689 *perms = kcalloc(size, sizeof(struct aa_perms), GFP_KERNEL); 927 - if (!*perms) 928 - goto fail_reset; 690 + if (!*perms) { 691 + e->pos = pos; 692 + return -ENOMEM; 693 + } 929 694 for (i = 0; i < size; i++) { 930 695 if (!unpack_perm(e, version, &(*perms)[i])) 931 696 goto fail; ··· 961 722 pdb = aa_alloc_pdb(GFP_KERNEL); 962 723 if (!pdb) 963 724 return -ENOMEM; 725 + 726 + AA_DEBUG(DEBUG_UNPACK, "unpacking tags"); 727 + if (unpack_tags(e, &pdb->tags, info) < 0) 728 + goto fail; 729 + AA_DEBUG(DEBUG_UNPACK, "done unpacking tags"); 964 730 965 731 size = unpack_perms_table(e, &pdb->perms); 966 732 if (size < 0) { ··· 1039 795 * transition table may be present even when the dfa is 1040 796 * not. For compatibility reasons unpack and discard. 1041 797 */ 1042 - if (!unpack_trans_table(e, &pdb->trans) && required_trans) { 798 + error = unpack_strs_table(e, "xtable", false, &pdb->trans); 799 + if (error && required_trans) { 1043 800 *info = "failed to unpack profile transition table"; 1044 801 goto fail; 1045 802 } 1046 803 1047 804 if (!pdb->dfa && pdb->trans.table) 1048 - aa_free_str_table(&pdb->trans); 805 + aa_destroy_str_table(&pdb->trans); 1049 806 1050 807 /* TODO: 1051 808 * - move compat mapping here, requires dfa merging first ··· 1303 1058 goto fail; 1304 1059 } else if (rules->file->dfa) { 1305 1060 if (!rules->file->perms) { 1061 + AA_DEBUG(DEBUG_UNPACK, "compat mapping perms"); 1306 1062 error = aa_compat_map_file(rules->file); 1307 1063 if (error) { 1308 1064 info = "failed to remap file permission table"; ··· 1506 1260 if (xmax < xidx) 1507 1261 xmax = xidx; 1508 1262 } 1509 - if (pdb->perms[i].tag && pdb->perms[i].tag >= pdb->trans.size) 1263 + if (pdb->perms[i].tag && pdb->perms[i].tag >= pdb->tags.sets.size) 1510 1264 return false; 1511 1265 if (pdb->perms[i].label && 1512 1266 pdb->perms[i].label >= pdb->trans.size) ··· 1514 1268 } 1515 1269 /* deal with incorrectly constructed string tables */ 1516 1270 if (xmax == -1) { 1517 - aa_free_str_table(&pdb->trans); 1271 + aa_destroy_str_table(&pdb->trans); 1518 1272 } else if (pdb->trans.size > xmax + 1) { 1519 1273 if (!aa_resize_str_table(&pdb->trans, xmax + 1, GFP_KERNEL)) 1520 1274 return false;
+5
security/apparmor/resource.c
··· 196 196 rules->rlimits.limits[j].rlim_max); 197 197 /* soft limit should not exceed hard limit */ 198 198 rlim->rlim_cur = min(rlim->rlim_cur, rlim->rlim_max); 199 + if (j == RLIMIT_CPU && 200 + rlim->rlim_cur != RLIM_INFINITY && 201 + IS_ENABLED(CONFIG_POSIX_TIMERS)) 202 + (void) update_rlimit_cpu(current->group_leader, 203 + rlim->rlim_cur); 199 204 } 200 205 } 201 206 }
+32
security/apparmor/task.c
··· 15 15 #include <linux/gfp.h> 16 16 #include <linux/ptrace.h> 17 17 18 + #include "include/path.h" 18 19 #include "include/audit.h" 19 20 #include "include/cred.h" 20 21 #include "include/policy.h" ··· 301 300 xrequest, &sa)); 302 301 } 303 302 303 + static const char *get_current_exe_path(char *buffer, int buffer_size) 304 + { 305 + struct file *exe_file; 306 + struct path p; 307 + const char *path_str; 308 + 309 + exe_file = get_task_exe_file(current); 310 + if (!exe_file) 311 + return ERR_PTR(-ENOENT); 312 + p = exe_file->f_path; 313 + path_get(&p); 314 + 315 + if (aa_path_name(&p, FLAG_VIEW_SUBNS, buffer, &path_str, NULL, NULL)) 316 + return ERR_PTR(-ENOMEM); 317 + 318 + fput(exe_file); 319 + path_put(&p); 320 + 321 + return path_str; 322 + } 323 + 304 324 /* call back to audit ptrace fields */ 305 325 static void audit_ns_cb(struct audit_buffer *ab, void *va) 306 326 { 307 327 struct apparmor_audit_data *ad = aad_of_va(va); 328 + char *buffer; 329 + const char *path; 308 330 309 331 if (ad->request & AA_USERNS_CREATE) 310 332 audit_log_format(ab, " requested=\"userns_create\""); 311 333 312 334 if (ad->denied & AA_USERNS_CREATE) 313 335 audit_log_format(ab, " denied=\"userns_create\""); 336 + 337 + buffer = aa_get_buffer(false); 338 + if (!buffer) 339 + return; // OOM 340 + path = get_current_exe_path(buffer, aa_g_path_max); 341 + if (!IS_ERR(path)) 342 + audit_log_format(ab, " execpath=\"%s\"", path); 343 + aa_put_buffer(buffer); 314 344 } 315 345 316 346 int aa_profile_ns_perm(struct aa_profile *profile,