From: GNU Libc Maintainers Date: Wed, 19 Apr 2023 21:17:51 +0000 (+0100) Subject: local-CVE-2021-33574-mq_notify-use-after-free X-Git-Tag: archive/raspbian/2.31-13+rpi1+deb11u6^2~1 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=22a35269bdeaf60082bb8275a9f23fa78b1d9a67;p=glibc.git local-CVE-2021-33574-mq_notify-use-after-free This is basically a backport of the following upstream commits below with __pthread_attr_copy implemented as a static function in mq_notify.c Indeed it has been added upstream in glibc 2.32 and is exported as a GLIBC_PRIVATE symbol from libpthread.so while __mq_notify is exported by librt.so. As Debian supports online upgrades, We need to support the case where 1) a process is started loading libpthread.so, 2) the glibc libraries are upgraded 3) librt.so is dlopened. This scenario might happens for instance with OpenJDK. commit 331c6e8a184167dd21a9f0b3fc165aeefea6eeca Author: Florian Weimer Date: Tue May 19 12:32:39 2020 +0200 nptl: Add __pthread_attr_copy for copying pthread_attr_t objects commit 79474303223c5665bec75ffbdb2a86ee04a2514b Author: Nikita Popov Date: Mon Aug 9 20:17:34 2021 +0530 librt: fix NULL pointer dereference (bug 28213) Helper thread frees copied attribute on NOTIFY_REMOVED message received from the OS kernel. Unfortunately, it fails to check whether copied attribute actually exists (data.attr != NULL). This worked earlier because free() checks passed pointer before actually attempting to release corresponding memory. But __pthread_attr_destroy assumes pointer is not NULL. So passing NULL pointer to __pthread_attr_destroy will result in segmentation fault. This scenario is possible if notification->sigev_notify_attributes == NULL (which means default thread attributes should be used). Signed-off-by: Nikita Popov Reviewed-by: Siddhesh Poyarekar (cherry picked from commit b805aebd42364fe696e417808a700fdb9800c9e8) commit 42d359350510506b87101cf77202fefcbfc790cb Author: Andreas Schwab Date: Thu May 27 12:49:47 2021 +0200 Use __pthread_attr_copy in mq_notify (bug 27896) Make a deep copy of the pthread attribute object to remove a potential use-after-free issue. commit 217b6dc298156bdb0d6aea9ea93e7e394a5ff091 Author: Florian Weimer Date: Tue Jun 1 17:51:41 2021 +0200 Fix use of __pthread_attr_copy in mq_notify (bug 27896) __pthread_attr_copy can fail and does not initialize the attribute structure in that case. If __pthread_attr_copy is never called and there is no allocated attribute, pthread_attr_destroy should not be called, otherwise there is a null pointer dereference in rt/tst-mqueue6. Fixes commit 42d359350510506b87101cf77202fefcbfc790cb ("Use __pthread_attr_copy in mq_notify (bug 27896)"). Reviewed-by: Siddhesh Poyarekar Gbp-Pq: Topic any Gbp-Pq: Name local-CVE-2021-33574-mq_notify-use-after-free.diff --- diff --git a/sysdeps/unix/sysv/linux/mq_notify.c b/sysdeps/unix/sysv/linux/mq_notify.c index f288bac47..e78ae74f0 100644 --- a/sysdeps/unix/sysv/linux/mq_notify.c +++ b/sysdeps/unix/sysv/linux/mq_notify.c @@ -134,9 +134,12 @@ helper_thread (void *arg) to wait until it is done with it. */ (void) __pthread_barrier_wait (¬ify_barrier); } - else if (data.raw[NOTIFY_COOKIE_LEN - 1] == NOTIFY_REMOVED) - /* The only state we keep is the copy of the thread attributes. */ - free (data.attr); + else if (data.raw[NOTIFY_COOKIE_LEN - 1] == NOTIFY_REMOVED && data.attr != NULL) + { + /* The only state we keep is the copy of the thread attributes. */ + pthread_attr_destroy (data.attr); + free (data.attr); + } } return NULL; } @@ -214,6 +217,42 @@ init_mq_netlink (void) } } +static int +__pthread_attr_copy (pthread_attr_t *target, const pthread_attr_t *source) +{ + /* Avoid overwriting *TARGET until all allocations have + succeeded. */ + union + { + pthread_attr_t external; + struct pthread_attr internal; + } temp; + + + temp.external = *source; + + /* Force new allocation. This function has full ownership of temp. */ + temp.internal.cpuset = NULL; + temp.internal.cpusetsize = 0; + + struct pthread_attr *isource = (struct pthread_attr *) source; + + /* Propagate affinity mask information. */ + if (isource->cpuset != NULL && isource->cpusetsize > 0) + { + temp.internal.cpuset = (cpu_set_t *) malloc (isource->cpusetsize); + if (temp.internal.cpuset == NULL) + return ENOMEM; + + temp.internal.cpusetsize = isource->cpusetsize; + memcpy (temp.internal.cpuset, isource->cpuset, isource->cpusetsize); + } + + /* Transfer ownership. *target is not assumed to have been + initialized. */ + *target = temp.external; + return 0; +} /* Register notification upon message arrival to an empty message queue MQDES. */ @@ -257,8 +296,14 @@ mq_notify (mqd_t mqdes, const struct sigevent *notification) if (data.attr == NULL) return -1; - memcpy (data.attr, notification->sigev_notify_attributes, - sizeof (pthread_attr_t)); + int ret = __pthread_attr_copy (data.attr, + notification->sigev_notify_attributes); + if (ret != 0) + { + free (data.attr); + __set_errno (ret); + return -1; + } } /* Construct the new request. */ @@ -271,8 +316,11 @@ mq_notify (mqd_t mqdes, const struct sigevent *notification) int retval = INLINE_SYSCALL (mq_notify, 2, mqdes, &se); /* If it failed, free the allocated memory. */ - if (__glibc_unlikely (retval != 0)) - free (data.attr); + if (retval != 0 && data.attr != NULL) + { + pthread_attr_destroy (data.attr); + free (data.attr); + } return retval; }