[Raring CVE-2013-1792] KEYS: Fix race with concurrent install_user_keyrings()
Luis Henriques
luis.henriques at canonical.com
Tue Mar 12 17:48:37 UTC 2013
Please ignore for now this patch. I'll resubmit it in a minute with a
SHA1.
Cheers,
--
Luis
On Tue, Mar 12, 2013 at 04:32:06PM +0000, Luis Henriques wrote:
> From: David Howells <dhowells at redhat.com>
>
> CVE-2013-1792
>
> BugLink: http://bugs.launchpad.net/bugs/1152788
>
> This fixes CVE-2013-1792.
>
> There is a race in install_user_keyrings() that can cause a NULL pointer
> dereference when called concurrently for the same user if the uid and
> uid-session keyrings are not yet created. It might be possible for an
> unprivileged user to trigger this by calling keyctl() from userspace in
> parallel immediately after logging in.
>
> Assume that we have two threads both executing lookup_user_key(), both looking
> for KEY_SPEC_USER_SESSION_KEYRING.
>
> THREAD A THREAD B
> =============================== ===============================
> ==>call install_user_keyrings();
> if (!cred->user->session_keyring)
> ==>call install_user_keyrings()
> ...
> user->uid_keyring = uid_keyring;
> if (user->uid_keyring)
> return 0;
> <==
> key = cred->user->session_keyring [== NULL]
> user->session_keyring = session_keyring;
> atomic_inc(&key->usage); [oops]
>
> At the point thread A dereferences cred->user->session_keyring, thread B hasn't
> updated user->session_keyring yet, but thread A assumes it is populated because
> install_user_keyrings() returned ok.
>
> The race window is really small but can be exploited if, for example, thread B
> is interrupted or preempted after initializing uid_keyring, but before doing
> setting session_keyring.
>
> This couldn't be reproduced on a stock kernel. However, after placing
> systemtap probe on 'user->session_keyring = session_keyring;' that introduced
> some delay, the kernel could be crashed reliably.
>
> Fix this by checking both pointers before deciding whether to return.
> Alternatively, the test could be done away with entirely as it is checked
> inside the mutex - but since the mutex is global, that may not be the best way.
>
> Reported-by: Mateusz Guzik <mguzik at redhat.com>
> Signed-off-by: David Howells <dhowells at redhat.com>
> Signed-off-by: Luis Henriques <luis.henriques at canonical.com>
> ---
> security/keys/process_keys.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
> index 58dfe08..c5ec083 100644
> --- a/security/keys/process_keys.c
> +++ b/security/keys/process_keys.c
> @@ -57,7 +57,7 @@ int install_user_keyrings(void)
>
> kenter("%p{%u}", user, uid);
>
> - if (user->uid_keyring) {
> + if (user->uid_keyring && user->session_keyring) {
> kleave(" = 0 [exist]");
> return 0;
> }
> --
> 1.8.1.2
>
> --
> kernel-team mailing list
> kernel-team at lists.ubuntu.com
> https://lists.ubuntu.com/mailman/listinfo/kernel-team
More information about the kernel-team
mailing list