diff --git a/glibc-RHEL-75555.patch b/glibc-RHEL-75555.patch new file mode 100644 index 0000000..ef56105 --- /dev/null +++ b/glibc-RHEL-75555.patch @@ -0,0 +1,115 @@ +commit abeae3c0061c0599ac2f012b270d6b4c8f59c82f +Author: Florian Weimer +Date: Thu Jan 16 18:45:25 2025 +0100 + + Linux: Fixes for getrandom fork handling + + Careful updates of grnd_alloc.len are required to ensure that + after fork, grnd_alloc.states does not contain entries that + are also encountered by __getrandom_reset_state in TCBs. + For the same reason, it is necessary to overwrite the TCB state + pointer with NULL before updating grnd_alloc.states in + __getrandom_vdso_release. + + Before this change, different TCBs could share the same getrandom + state after multi-threaded fork. This would be a critical security + bug (predictable randomness) if not caught during development. + + The additional check in stdlib/tst-arc4random-thread makes it more + likely that the test fails due to the bugs mentioned above. + + Both __getrandom_reset_state and __getrandom_vdso_release could + put reserved NULL pointers into the states array. This is also + fixed with this commit. After these changes, no null pointers were + observed in the states array during testing. + + Reviewed-by: Adhemerval Zanella + +diff --git a/stdlib/tst-arc4random-thread.c b/stdlib/tst-arc4random-thread.c +index d1259626c62af5ad..5e8a4761fc39165a 100644 +--- a/stdlib/tst-arc4random-thread.c ++++ b/stdlib/tst-arc4random-thread.c +@@ -49,7 +49,7 @@ static const int sizes[] = { 12, 15, 16, 17, 24, 31, max_size }; + struct blob + { + unsigned int size; +- int thread_id; ++ int thread_id; /* -1 means after fork. */ + unsigned int index; + unsigned char bytes[max_size]; + }; +@@ -323,6 +323,20 @@ do_test_func (const char *fname, void (*func)(unsigned char *, size_t)) + } + } + ++ for (struct blob *p = dynarray_blob_begin (&global_result); ++ p < end; ++p) ++ { ++ unsigned int sum = 0; ++ for (unsigned int i = 0; i < p->size; ++i) ++ sum += p->bytes[i]; ++ if (sum == 0) ++ { ++ support_record_failure (); ++ printf ("error: all-zero result of length %u on thread %d\n", ++ p->size, p->thread_id); ++ } ++ } ++ + dynarray_blob_free (&global_result); + + return 0; +diff --git a/sysdeps/unix/sysv/linux/getrandom.c b/sysdeps/unix/sysv/linux/getrandom.c +index d3eab66a1af6229e..d93901f6ea5cbc6b 100644 +--- a/sysdeps/unix/sysv/linux/getrandom.c ++++ b/sysdeps/unix/sysv/linux/getrandom.c +@@ -168,6 +168,11 @@ vgetrandom_get_state (void) + if (grnd_alloc.len > 0 || vgetrandom_get_state_alloc ()) + state = grnd_alloc.states[--grnd_alloc.len]; + ++ /* Barrier needed by fork: The state must be gone from the array ++ through len update before it becomes visible in the TCB. (There ++ is also a release barrier implied by the unlock, but issue a ++ stronger barrier to help fork.) */ ++ atomic_thread_fence_seq_cst (); + __libc_lock_unlock (grnd_alloc.lock); + internal_signal_restore_set (&set); + +@@ -278,7 +283,10 @@ void + __getrandom_reset_state (struct pthread *curp) + { + #ifdef HAVE_GETRANDOM_VSYSCALL +- if (grnd_alloc.states == NULL || curp->getrandom_buf == NULL) ++ /* The pointer can be reserved if the fork happened during a ++ getrandom call. */ ++ void *buf = release_ptr (curp->getrandom_buf); ++ if (grnd_alloc.states == NULL || buf == NULL) + return; + assert (grnd_alloc.len < grnd_alloc.cap); + grnd_alloc.states[grnd_alloc.len++] = release_ptr (curp->getrandom_buf); +@@ -294,11 +302,23 @@ void + __getrandom_vdso_release (struct pthread *curp) + { + #ifdef HAVE_GETRANDOM_VSYSCALL +- if (curp->getrandom_buf == NULL) ++ /* The pointer can be reserved if the thread was canceled in a ++ signal handler. */ ++ void *buf = release_ptr (curp->getrandom_buf); ++ if (buf == NULL) + return; + + __libc_lock_lock (grnd_alloc.lock); +- grnd_alloc.states[grnd_alloc.len++] = curp->getrandom_buf; ++ ++ size_t len = grnd_alloc.len; ++ grnd_alloc.states[len] = curp->getrandom_buf; ++ curp->getrandom_buf = NULL; ++ /* Barrier needed by fork: The state must vanish from the TCB before ++ it becomes visible in the states array. Also avoid exposing the ++ previous entry value at the same index in the states array (which ++ may be in use by another thread). */ ++ atomic_thread_fence_seq_cst (); ++ grnd_alloc.len = len + 1; + __libc_lock_unlock (grnd_alloc.lock); + #endif + } diff --git a/glibc.spec b/glibc.spec index 0a924d9..63699a8 100644 --- a/glibc.spec +++ b/glibc.spec @@ -145,7 +145,7 @@ Version: %{glibcversion} # - It allows using the Release number without the %%dist tag in the dependency # generator to make the generated requires interchangeable between Rawhide # and ELN (.elnYY < .fcXX). -%global baserelease 35 +%global baserelease 36 Release: %{baserelease}%{?dist} # Licenses: @@ -505,6 +505,7 @@ Patch187: glibc-upstream-2.39-144.patch Patch188: glibc-upstream-2.39-145.patch Patch189: glibc-upstream-2.39-146.patch Patch190: glibc-RHEL-75809.patch +Patch191: glibc-RHEL-75555.patch ############################################################################## # Continued list of core "glibc" package information: @@ -2500,6 +2501,9 @@ update_gconv_modules_cache () %endif %changelog +* Thu Jan 23 2025 Florian Weimer - 2.39-36 +- CVE-2025-0577: vDSO getrandom predictable randomness after fork (RHEL-75555) + * Thu Jan 23 2025 Florian Weimer - 2.39-35 - Restore compatibility with environ/malloc usage pattern (RHEL-75809)