oss-sec mailing list archives

Re: CVE request - kernel: execve: must clear current->clear_child_tid


From: "Michael K. Johnson" <johnsonm () rpath com>
Date: Wed, 5 Aug 2009 15:26:21 -0400

This seems to me a potential security issue specifically because as
far as I can see clear_child_tid isn't reset on exec of set{u,g}id
executables.  (Otherwise it would just be a bug...)  Regarding a
non-threaded setuid program, a direct attack seems hard since the
maps and smaps files are hidden to other users.  Am I missing some
of the potential impact here?

On Tue, Aug 04, 2009 at 04:09:09PM +0800, Eugene Teo wrote:
clone() syscall has special support for TID of created threads.  This
support includes two features.

One (CLONE_CHILD_SETTID) is to set an integer into user memory with the
TID value.

One (CLONE_CHILD_CLEARTID) is to clear this same integer once the
created thread dies.

The integer location is a user provided pointer, provided at clone() time.

kernel keeps this pointer value into current->clear_child_tid.

At execve() time, we should make sure kernel doesnt keep this user
provided pointer, as full user memory is replaced by a new one.

As glibc fork() actually uses clone() syscall with CLONE_CHILD_SETTID
and CLONE_CHILD_CLEARTID set, chances are high that we might corrupt
user memory in forked processes.

Following sequence could happen:

1) bash (or any program) starts a new process, by a fork() call that
glibc maps to a clone( ...  CLONE_CHILD_SETTID |
CLONE_CHILD_CLEARTID...) syscall

2) When new process starts, its current->clear_child_tid is set to a
location that has a meaning only in bash (or initial program) context
(&THREAD_SELF->tid)

3) This new process does the execve() syscall to start a new program.
current->clear_child_tid is left unchanged (a non NULL value)

4) If this new program creates some threads, and initial thread exits,
kernel will attempt to clear the integer pointed by
current->clear_child_tid from mm_release() :

        if (tsk->clear_child_tid
            && !(tsk->flags & PF_SIGNALED)
            && atomic_read(&mm->mm_users) > 1) {
                u32 __user * tidptr = tsk->clear_child_tid;
                tsk->clear_child_tid = NULL;

                /*
                 * We don't check the error code - if userspace has
                 * not set up a proper pointer then tough luck.
                 */
<< here >>      put_user(0, tidptr);
                sys_futex(tidptr, FUTEX_WAKE, 1, NULL, NULL, 0);
        }

5) OR : if new program is not multi-threaded, but spied by /proc/pid
users (ps command for example), mm_users > 1, and the exiting program
could corrupt 4 bytes in a persistent memory area (shm or memory mapped
file)

If current->clear_child_tid points to a writeable portion of memory of
the new program, kernel happily and silently corrupts 4 bytes of memory,
with unexpected effects.

References:
http://article.gmane.org/gmane.linux.kernel/871942
https://bugzilla.redhat.com/show_bug.cgi?id=515423

Patch is not in upstream kernel yet.

Thanks, Eugene


Current thread: