smack: Possible NULL pointer deref in cred_free hook.
Casey Schaufler
casey at schaufler-ca.com
Wed Feb 7 01:39:34 UTC 2024
On 2/6/2024 6:31 AM, Tetsuo Handa wrote:
> Hello, Casey.
>
> I confirmed using fault injection shown below that smack_cred_free() is not
> prepared for being called without successful smack_cred_prepare().
The failure cases for smack_cred_prepare() result from memory allocation
failures. Since init_task_smack() is called before either of the potential
memory allocations the state of the cred will be safe for smack_cred_free().
The fault you've described here removes the init_task_smack(), which will
always succeed, and which is sufficient to prevent the smack_cred_free()
failure below. Are you suggesting that there is a case where a cred will
be freed without ever having been "prepared"?
>
> diff --git a/security/security.c b/security/security.c
> index 10adc4d3c5e0..690136b60d28 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -3059,7 +3059,7 @@ int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp)
> if (rc)
> return rc;
>
> - rc = call_int_hook(cred_prepare, 0, new, old, gfp);
> + rc = -EPERM; //call_int_hook(cred_prepare, 0, new, old, gfp);
> if (unlikely(rc))
> security_cred_free(new);
> return rc;
>
> [ 11.366204] LSM: initializing lsm=capability,yama,smack,integrity
> [ 11.368005] Yama: becoming mindful.
> [ 11.371997] Smack: Initializing.
> [ 11.373957] Smack: IPv6 port labeling enabled.
> [ 11.379072] Mount-cache hash table entries: 16384 (order: 5, 131072 bytes, linear)
> [ 11.383676] Mountpoint-cache hash table entries: 16384 (order: 5, 131072 bytes, linear)
> [ 11.400071] Running RCU synchronous self tests
> [ 11.450967] Running RCU synchronous self tests
> [ 11.453956] BUG: kernel NULL pointer dereference, address: 0000000000000000
> [ 11.454948] #PF: supervisor read access in kernel mode
> [ 11.454948] #PF: error_code(0x0000) - not-present page
> [ 11.454948] PGD 0 P4D 0
> [ 11.454948] Oops: 0000 [#1] PREEMPT SMP PTI
> [ 11.454948] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 6.8.0-rc3+ #47
> [ 11.454948] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 11/12/2020
> [ 11.454948] RIP: 0010:smk_destroy_label_list+0x11/0x40
> [ 11.454948] Code: 2e 0f 1f 84 00 00 00 00 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 66 0f 1f 00 0f 1f 44 00 00 55 48 89 fd 53 48 8b 3f <48> 8b 1f 48 39 fd 74 13 e8 c2 bd e2 ff 48 89 d8 48 89 df 48 8b 1b
> [ 11.454948] RSP: 0000:ffffffffa3003c50 EFLAGS: 00010286
> [ 11.454948] RAX: ffffffffa1f763e0 RBX: ffffffffa2f19b00 RCX: 0000000000000100
> [ 11.454948] RDX: 00000000000000d8 RSI: 0000000000000000 RDI: 0000000000000000
> [ 11.454948] RBP: ffff8d71c03300c8 R08: 0000000000400dc0 R09: 00000000ffffffff
> [ 11.454948] R10: ffffffffa3003c48 R11: ffff8d7259ffc220 R12: ffff8d71c0330018
> [ 11.454948] R13: ffffffffa3003e48 R14: ffff8d71c03c8000 R15: 0000000000000001
> [ 11.454948] FS: 0000000000000000(0000) GS:ffff8d7259e00000(0000) knlGS:0000000000000000
> [ 11.454948] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> [ 11.454948] CR2: 0000000000000000 CR3: 000000014e03c000 CR4: 0000000000350ef0
> [ 11.454948] Call Trace:
> [ 11.454948] <TASK>
> [ 11.454948] ? __die_body+0x1e/0x60
> [ 11.454948] ? page_fault_oops+0x15b/0x470
> [ 11.454948] ? local_clock+0x15/0x30
> [ 11.454948] ? lock_release+0x275/0x350
> [ 11.454948] ? exc_page_fault+0x74/0x1e0
> [ 11.454948] ? asm_exc_page_fault+0x26/0x30
> [ 11.454948] ? __pfx_smack_cred_free+0x10/0x10
> [ 11.454948] ? smk_destroy_label_list+0x11/0x40
> [ 11.454948] smack_cred_free+0x30/0xa0
> [ 11.454948] security_cred_free+0x2f/0x60
> [ 11.454948] security_prepare_creds+0x2b/0x60
> [ 11.454948] prepare_creds+0x1b3/0x2c0
> [ 11.454948] copy_creds+0x2e/0x380
> [ 11.454948] copy_process+0x47e/0x2080
> [ 11.454948] ? find_held_lock+0x34/0xa0
> [ 11.454948] kernel_clone+0x9f/0x6c0
> [ 11.454948] ? __mutex_unlock_slowpath+0x43/0x2c0
> [ 11.454948] ? synchronize_rcu_expedited+0x3fa/0x4e0
> [ 11.454948] user_mode_thread+0x5f/0x90
> [ 11.454948] ? __pfx_kernel_init+0x10/0x10
> [ 11.454948] rest_init+0x22/0x1d0
> [ 11.454948] arch_call_rest_init+0xe/0x30
> [ 11.454948] start_kernel+0x63e/0x720
> [ 11.454948] x86_64_start_reservations+0x18/0x30
> [ 11.454948] x86_64_start_kernel+0x91/0xa0
> [ 11.454948] secondary_startup_64_no_verify+0x184/0x18b
> [ 11.454948] </TASK>
> [ 11.454948] Modules linked in:
> [ 11.454948] CR2: 0000000000000000
> [ 11.454948] ---[ end trace 0000000000000000 ]---
> [ 11.454948] RIP: 0010:smk_destroy_label_list+0x11/0x40
> [ 11.454948] Code: 2e 0f 1f 84 00 00 00 00 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 66 0f 1f 00 0f 1f 44 00 00 55 48 89 fd 53 48 8b 3f <48> 8b 1f 48 39 fd 74 13 e8 c2 bd e2 ff 48 89 d8 48 89 df 48 8b 1b
> [ 11.454948] RSP: 0000:ffffffffa3003c50 EFLAGS: 00010286
> [ 11.454948] RAX: ffffffffa1f763e0 RBX: ffffffffa2f19b00 RCX: 0000000000000100
> [ 11.454948] RDX: 00000000000000d8 RSI: 0000000000000000 RDI: 0000000000000000
> [ 11.454948] RBP: ffff8d71c03300c8 R08: 0000000000400dc0 R09: 00000000ffffffff
> [ 11.454948] R10: ffffffffa3003c48 R11: ffff8d7259ffc220 R12: ffff8d71c0330018
> [ 11.454948] R13: ffffffffa3003e48 R14: ffff8d71c03c8000 R15: 0000000000000001
> [ 11.454948] FS: 0000000000000000(0000) GS:ffff8d7259e00000(0000) knlGS:0000000000000000
> [ 11.454948] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> [ 11.454948] CR2: 0000000000000000 CR3: 000000014e03c000 CR4: 0000000000350ef0
> [ 11.454948] Kernel panic - not syncing: Fatal exception
> [ 11.454948] ---[ end Kernel panic - not syncing: Fatal exception ]---
>
> On 2024/01/27 23:36, Tetsuo Handa wrote:
>> The cred_free hook might be called without successful cred_prepare
>> as long as cred->security != NULL. It seems to me that smack_cred_free()
>> is not prepared for that case. Please check.
>>
>> prepare_creds() {
>> new = kmem_cache_alloc(cred_jar, GFP_KERNEL);
>> if (!new)
>> return NULL;
>> new->security = NULL;
>> security_prepare_creds(new, old, GFP_KERNEL_ACCOUNT) {
>> int rc = lsm_cred_alloc(new, gfp);
>> if (rc)
>> return rc;
>> rc = call_int_hook(cred_prepare, 0, new, old, gfp) {
>> // cred->security != NULL and one of callbacks returned an error
>> }
>> if (unlikely(rc)) {
>> security_cred_free(new) {
>> call_void_hook(cred_free, cred) {
>> // at least one callback is called without successful cred_prepare
>> }
>> kfree(cred->security);
>> cred->security = NULL;
>> }
>> }
>> }
>> goto error;
>> error:
>> abort_creds(new)
>> return NULL;
>> }
>
More information about the Linux-security-module-archive
mailing list