File mq-notify-use-after-free.patch of Package glibc.21693
From 42d359350510506b87101cf77202fefcbfc790cb Mon Sep 17 00:00:00 2001
From: Andreas Schwab <schwab@linux-m68k.org>
Date: Thu, 27 May 2021 12:49:47 +0200
Subject: [PATCH] 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.
CVE-2021-33574: The mq_notify function has a potential use-after-free
issue when using a notification type of SIGEV_THREAD and a thread
attribute with a non-default affinity mask.
Index: glibc-2.26/nptl/Makefile
===================================================================
--- glibc-2.26.orig/nptl/Makefile
+++ glibc-2.26/nptl/Makefile
@@ -138,7 +138,8 @@ libpthread-routines = nptl-init vars eve
pthread_mutex_getprioceiling \
pthread_mutex_setprioceiling \
pthread_setname pthread_getname \
- pthread_setattr_default_np pthread_getattr_default_np
+ pthread_setattr_default_np pthread_getattr_default_np \
+ pthread_attr_copy
# pthread_setuid pthread_seteuid pthread_setreuid \
# pthread_setresuid \
# pthread_setgid pthread_setegid pthread_setregid \
Index: glibc-2.26/nptl/Versions
===================================================================
--- glibc-2.26.orig/nptl/Versions
+++ glibc-2.26/nptl/Versions
@@ -270,6 +270,7 @@ libpthread {
__pthread_clock_gettime; __pthread_clock_settime;
__pthread_unwind; __pthread_get_minstack;
__pthread_barrier_init; __pthread_barrier_wait;
+ __pthread_attr_copy;
__shm_directory;
}
}
Index: glibc-2.26/nptl/pthreadP.h
===================================================================
--- glibc-2.26.orig/nptl/pthreadP.h
+++ glibc-2.26/nptl/pthreadP.h
@@ -446,6 +446,8 @@ extern int __pthread_mutexattr_init (pth
extern int __pthread_mutexattr_destroy (pthread_mutexattr_t *attr);
extern int __pthread_mutexattr_settype (pthread_mutexattr_t *attr, int kind);
extern int __pthread_attr_destroy (pthread_attr_t *attr);
+extern int __pthread_attr_setaffinity_np (pthread_attr_t *, size_t,
+ const cpu_set_t *);
extern int __pthread_attr_getdetachstate (const pthread_attr_t *attr,
int *detachstate);
extern int __pthread_attr_setdetachstate (pthread_attr_t *attr,
@@ -518,6 +520,7 @@ extern void __pthread_disable_asynccance
extern void __pthread_testcancel (void);
#if IS_IN (libpthread)
+hidden_proto (__pthread_attr_setaffinity_np)
hidden_proto (__pthread_mutex_init)
hidden_proto (__pthread_mutex_destroy)
hidden_proto (__pthread_mutex_lock)
@@ -616,6 +619,11 @@ extern void __wait_lookup_done (void) at
# define PTHREAD_STATIC_FN_REQUIRE(name) __asm (".globl " #name);
#endif
+/* Make a deep copy of the attribute *SOURCE in *TARGET. *TARGET is
+ not assumed to have been initialized. Returns 0 on success, or a
+ positive error code otherwise. */
+int __pthread_attr_copy (pthread_attr_t *target, const pthread_attr_t *source);
+
/* Returns 0 if POL is a valid scheduling policy. */
static inline int
check_sched_policy_attr (int pol)
Index: glibc-2.26/nptl/pthread_attr_copy.c
===================================================================
--- /dev/null
+++ glibc-2.26/nptl/pthread_attr_copy.c
@@ -0,0 +1,56 @@
+/* Deep copy of a pthread_attr_t object.
+ Copyright (C) 2020 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <errno.h>
+#include <pthreadP.h>
+#include <stdlib.h>
+
+int
+__pthread_attr_copy (pthread_attr_t *target, const pthread_attr_t *source)
+{
+ /* Avoid overwriting *TARGET until all allocations have
+ succeeded. */
+ union pthread_attr_transparent temp;
+ temp.external = *source;
+
+ /* Force new allocation. This function has full ownership of temp. */
+ temp.internal.cpuset = NULL;
+ temp.internal.cpusetsize = 0;
+
+ int ret = 0;
+
+ struct pthread_attr *isource = (struct pthread_attr *) source;
+
+ /* Propagate affinity mask information. */
+ if (isource->cpusetsize > 0)
+ ret = __pthread_attr_setaffinity_np (&temp.external,
+ isource->cpusetsize,
+ isource->cpuset);
+
+ if (ret != 0)
+ {
+ /* Deallocate because we have ownership. */
+ __pthread_attr_destroy (&temp.external);
+ return ret;
+ }
+
+ /* Transfer ownership. *target is not assumed to have been
+ initialized. */
+ *target = temp.external;
+ return 0;
+}
Index: glibc-2.26/nptl/pthread_attr_setaffinity.c
===================================================================
--- glibc-2.26.orig/nptl/pthread_attr_setaffinity.c
+++ glibc-2.26/nptl/pthread_attr_setaffinity.c
@@ -26,8 +26,8 @@
int
-__pthread_attr_setaffinity_new (pthread_attr_t *attr, size_t cpusetsize,
- const cpu_set_t *cpuset)
+__pthread_attr_setaffinity_np (pthread_attr_t *attr, size_t cpusetsize,
+ const cpu_set_t *cpuset)
{
struct pthread_attr *iattr;
@@ -57,7 +57,8 @@ __pthread_attr_setaffinity_new (pthread_
return 0;
}
-versioned_symbol (libpthread, __pthread_attr_setaffinity_new,
+hidden_def (__pthread_attr_setaffinity_np);
+versioned_symbol (libpthread, __pthread_attr_setaffinity_np,
pthread_attr_setaffinity_np, GLIBC_2_3_4);
@@ -66,7 +67,7 @@ int
__pthread_attr_setaffinity_old (pthread_attr_t *attr, cpu_set_t *cpuset)
{
/* The old interface by default assumed a 1024 processor bitmap. */
- return __pthread_attr_setaffinity_new (attr, 128, cpuset);
+ return __pthread_attr_setaffinity_np (attr, 128, cpuset);
}
compat_symbol (libpthread, __pthread_attr_setaffinity_old,
pthread_attr_setaffinity_np, GLIBC_2_3_3);
Index: glibc-2.26/sysdeps/nptl/internaltypes.h
===================================================================
--- glibc-2.26.orig/sysdeps/nptl/internaltypes.h
+++ glibc-2.26/sysdeps/nptl/internaltypes.h
@@ -50,6 +50,14 @@ struct pthread_attr
#define ATTR_FLAG_POLICY_SET 0x0040
+/* Used to allocate a pthread_attr_t object which is also accessed
+ internally. */
+union pthread_attr_transparent
+{
+ pthread_attr_t external;
+ struct pthread_attr internal;
+};
+
/* Mutex attribute data structure. */
struct pthread_mutexattr
{
Index: glibc-2.26/sysdeps/unix/sysv/linux/mq_notify.c
===================================================================
--- glibc-2.26.orig/sysdeps/unix/sysv/linux/mq_notify.c
+++ glibc-2.26/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;
}
@@ -257,8 +260,14 @@ mq_notify (mqd_t mqdes, const struct sig
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 +280,11 @@ mq_notify (mqd_t mqdes, const struct sig
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;
}