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.

apparmor: replace recursive profile removal with iterative approach

The profile removal code uses recursion when removing nested profiles,
which can lead to kernel stack exhaustion and system crashes.

Reproducer:
$ pf='a'; for ((i=0; i<1024; i++)); do
echo -e "profile $pf { \n }" | apparmor_parser -K -a;
pf="$pf//x";
done
$ echo -n a > /sys/kernel/security/apparmor/.remove

Replace the recursive __aa_profile_list_release() approach with an
iterative approach in __remove_profile(). The function repeatedly
finds and removes leaf profiles until the entire subtree is removed,
maintaining the same removal semantic without recursion.

Fixes: c88d4c7b049e ("AppArmor: core policy routines")
Reported-by: Qualys Security Advisory <qsa@qualys.com>
Tested-by: Salvatore Bonaccorso <carnil@debian.org>
Reviewed-by: Georgia Garcia <georgia.garcia@canonical.com>
Reviewed-by: Cengiz Can <cengiz.can@canonical.com>
Signed-off-by: Massimiliano Pellizzer <massimiliano.pellizzer@canonical.com>
Signed-off-by: John Johansen <john.johansen@canonical.com>

authored by

Massimiliano Pellizzer and committed by
John Johansen
ab092646 e38c55d9

+27 -3
+27 -3
security/apparmor/policy.c
··· 191 191 } 192 192 193 193 /** 194 - * __remove_profile - remove old profile, and children 195 - * @profile: profile to be replaced (NOT NULL) 194 + * __remove_profile - remove profile, and children 195 + * @profile: profile to be removed (NOT NULL) 196 196 * 197 197 * Requires: namespace list lock be held, or list not be shared 198 198 */ 199 199 static void __remove_profile(struct aa_profile *profile) 200 200 { 201 + struct aa_profile *curr, *to_remove; 202 + 201 203 AA_BUG(!profile); 202 204 AA_BUG(!profile->ns); 203 205 AA_BUG(!mutex_is_locked(&profile->ns->lock)); 204 206 205 207 /* release any children lists first */ 206 - __aa_profile_list_release(&profile->base.profiles); 208 + if (!list_empty(&profile->base.profiles)) { 209 + curr = list_first_entry(&profile->base.profiles, struct aa_profile, base.list); 210 + 211 + while (curr != profile) { 212 + 213 + while (!list_empty(&curr->base.profiles)) 214 + curr = list_first_entry(&curr->base.profiles, 215 + struct aa_profile, base.list); 216 + 217 + to_remove = curr; 218 + if (!list_is_last(&to_remove->base.list, 219 + &aa_deref_parent(curr)->base.profiles)) 220 + curr = list_next_entry(to_remove, base.list); 221 + else 222 + curr = aa_deref_parent(curr); 223 + 224 + /* released by free_profile */ 225 + aa_label_remove(&to_remove->label); 226 + __aafs_profile_rmdir(to_remove); 227 + __list_remove_profile(to_remove); 228 + } 229 + } 230 + 207 231 /* released by free_profile */ 208 232 aa_label_remove(&profile->label); 209 233 __aafs_profile_rmdir(profile);