diff --git a/0001-reorder-thread-list-unlink-in-pthread_exit-after-all.patch b/0001-reorder-thread-list-unlink-in-pthread_exit-after-all.patch new file mode 100644 index 0000000000000000000000000000000000000000..3fc65921c5c71d8d4cbd875a9cf7b54376f48511 --- /dev/null +++ b/0001-reorder-thread-list-unlink-in-pthread_exit-after-all.patch @@ -0,0 +1,55 @@ +From 4d5aa20a94a2d3fae3e69289dc23ecafbd0c16c4 Mon Sep 17 00:00:00 2001 +From: Rich Felker +Date: Fri, 22 May 2020 17:35:14 -0400 +Subject: [PATCH] reorder thread list unlink in pthread_exit after all locks + +since the backend for LOCK() skips locking if single-threaded, it's +unsafe to make the process appear single-threaded before the last use +of lock. + +this fixes potential unsynchronized access to a linked list via +__dl_thread_cleanup. +--- + src/thread/pthread_create.c | 19 +++++++++++-------- + 1 file changed, 11 insertions(+), 8 deletions(-) + +diff --git a/src/thread/pthread_create.c b/src/thread/pthread_create.c +index 5f491092..6a3b0c21 100644 +--- a/src/thread/pthread_create.c ++++ b/src/thread/pthread_create.c +@@ -90,14 +90,7 @@ _Noreturn void __pthread_exit(void *result) + exit(0); + } + +- /* At this point we are committed to thread termination. Unlink +- * the thread from the list. This change will not be visible +- * until the lock is released, which only happens after SYS_exit +- * has been called, via the exit futex address pointing at the lock. */ +- libc.threads_minus_1--; +- self->next->prev = self->prev; +- self->prev->next = self->next; +- self->prev = self->next = self; ++ /* At this point we are committed to thread termination. */ + + /* Process robust list in userspace to handle non-pshared mutexes + * and the detached thread case where the robust list head will +@@ -121,6 +114,16 @@ _Noreturn void __pthread_exit(void *result) + __do_orphaned_stdio_locks(); + __dl_thread_cleanup(); + ++ /* Last, unlink thread from the list. This change will not be visible ++ * until the lock is released, which only happens after SYS_exit ++ * has been called, via the exit futex address pointing at the lock. ++ * This needs to happen after any possible calls to LOCK() that might ++ * skip locking if libc.threads_minus_1 is zero. */ ++ libc.threads_minus_1--; ++ self->next->prev = self->prev; ++ self->prev->next = self->next; ++ self->prev = self->next = self; ++ + /* This atomic potentially competes with a concurrent pthread_detach + * call; the loser is responsible for freeing thread resources. */ + int state = a_cas(&self->detach_state, DT_JOINABLE, DT_EXITING); +-- +2.27.0 + diff --git a/0002-don-t-use-libc.threads_minus_1-as-relaxed-atomic-for.patch b/0002-don-t-use-libc.threads_minus_1-as-relaxed-atomic-for.patch new file mode 100644 index 0000000000000000000000000000000000000000..a193e0469920f8820f952f745def643cc4fbba22 --- /dev/null +++ b/0002-don-t-use-libc.threads_minus_1-as-relaxed-atomic-for.patch @@ -0,0 +1,78 @@ +From e01b5939b38aea5ecbe41670643199825874b26c Mon Sep 17 00:00:00 2001 +From: Rich Felker +Date: Thu, 21 May 2020 23:32:45 -0400 +Subject: [PATCH] don't use libc.threads_minus_1 as relaxed atomic for skipping + locks + +after all but the last thread exits, the next thread to observe +libc.threads_minus_1==0 and conclude that it can skip locking fails to +synchronize with any changes to memory that were made by the +last-exiting thread. this can produce data races. + +on some archs, at least x86, memory synchronization is unlikely to be +a problem; however, with the inline locks in malloc, skipping the lock +also eliminated the compiler barrier, and caused code that needed to +re-check chunk in-use bits after obtaining the lock to reuse a stale +value, possibly from before the process became single-threaded. this +in turn produced corruption of the heap state. + +some uses of libc.threads_minus_1 remain, especially for allocation of +new TLS in the dynamic linker; otherwise, it could be removed +entirely. it's made non-volatile to reflect that the remaining +accesses are only made under lock on the thread list. + +instead of libc.threads_minus_1, libc.threaded is now used for +skipping locks. the difference is that libc.threaded is permanently +true once an additional thread has been created. this will produce +some performance regression in processes that are mostly +single-threaded but occasionally creating threads. in the future it +may be possible to bring back the full lock-skipping, but more care +needs to be taken to produce a safe design. +--- + src/internal/libc.h | 2 +- + src/malloc/malloc.c | 2 +- + src/thread/__lock.c | 2 +- + 3 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/internal/libc.h b/src/internal/libc.h +index ac97dc7e..c0614852 100644 +--- a/src/internal/libc.h ++++ b/src/internal/libc.h +@@ -21,7 +21,7 @@ struct __libc { + int can_do_threads; + int threaded; + int secure; +- volatile int threads_minus_1; ++ int threads_minus_1; + size_t *auxv; + struct tls_module *tls_head; + size_t tls_size, tls_align, tls_cnt; +diff --git a/src/malloc/malloc.c b/src/malloc/malloc.c +index 96982596..2553a62e 100644 +--- a/src/malloc/malloc.c ++++ b/src/malloc/malloc.c +@@ -26,7 +26,7 @@ int __malloc_replaced; + + static inline void lock(volatile int *lk) + { +- if (libc.threads_minus_1) ++ if (libc.threaded) + while(a_swap(lk, 1)) __wait(lk, lk+1, 1, 1); + } + +diff --git a/src/thread/__lock.c b/src/thread/__lock.c +index 45557c88..5b9b144e 100644 +--- a/src/thread/__lock.c ++++ b/src/thread/__lock.c +@@ -18,7 +18,7 @@ + + void __lock(volatile int *l) + { +- if (!libc.threads_minus_1) return; ++ if (!libc.threaded) return; + /* fast path: INT_MIN for the lock, +1 for the congestion */ + int current = a_cas(l, 0, INT_MIN + 1); + if (!current) return; +-- +2.27.0 + diff --git a/0003-restore-lock-skipping-for-processes-that-return-to-s.patch b/0003-restore-lock-skipping-for-processes-that-return-to-s.patch new file mode 100644 index 0000000000000000000000000000000000000000..5dace44d08fc04c7204367bef512e847ce4b56d7 --- /dev/null +++ b/0003-restore-lock-skipping-for-processes-that-return-to-s.patch @@ -0,0 +1,104 @@ +From 540d5bd85c8dc9efda0f01b6fc26fbec6ff5011e Mon Sep 17 00:00:00 2001 +From: zhuyan +Date: Fri, 5 Nov 2021 19:43:36 +0800 +Subject: [PATCH] restore lock-skipping for processes that return to + single-threaded state + +the design used here relies on the barrier provided by the first lock +operation after the process returns to single-threaded state to +synchronize with actions by the last thread that exited. by storing +the intent to change modes in the same object used to detect whether +locking is needed, it's possible to avoid an extra (possibly costly) +memory load after the lock is taken. + +Signed-off-by: Rich Felker +Signed-off-by: zhuyan +--- + src/internal/libc.h | 1 + + src/malloc/malloc.c | 5 ++++- + src/thread/__lock.c | 4 +++- + src/thread/pthread_create.c | 8 ++++---- + 4 files changed, 12 insertions(+), 6 deletions(-) + +diff --git a/src/internal/libc.h b/src/internal/libc.h +index c061485..a1ef634 100644 +--- a/src/internal/libc.h ++++ b/src/internal/libc.h +@@ -21,6 +21,7 @@ struct __libc { + int can_do_threads; + int threaded; + int secure; ++ volatile signed char need_locks; + int threads_minus_1; + size_t *auxv; + struct tls_module *tls_head; +diff --git a/src/malloc/malloc.c b/src/malloc/malloc.c +index 2553a62..a803d4c 100644 +--- a/src/malloc/malloc.c ++++ b/src/malloc/malloc.c +@@ -26,8 +26,11 @@ int __malloc_replaced; + + static inline void lock(volatile int *lk) + { +- if (libc.threaded) ++ int need_locks = libc.need_locks; ++ if (need_locks) { + while(a_swap(lk, 1)) __wait(lk, lk+1, 1, 1); ++ if (need_locks < 0) libc.need_locks = 0; ++ } + } + + static inline void unlock(volatile int *lk) +diff --git a/src/thread/__lock.c b/src/thread/__lock.c +index 5b9b144..60eece4 100644 +--- a/src/thread/__lock.c ++++ b/src/thread/__lock.c +@@ -18,9 +18,11 @@ + + void __lock(volatile int *l) + { +- if (!libc.threaded) return; ++ int need_locks = libc.need_locks; ++ if (!need_locks) return; + /* fast path: INT_MIN for the lock, +1 for the congestion */ + int current = a_cas(l, 0, INT_MIN + 1); ++ if (need_locks < 0) libc.need_locks = 0; + if (!current) return; + /* A first spin loop, for medium congestion. */ + for (unsigned i = 0; i < 10; ++i) { +diff --git a/src/thread/pthread_create.c b/src/thread/pthread_create.c +index 6a3b0c2..6bdfb44 100644 +--- a/src/thread/pthread_create.c ++++ b/src/thread/pthread_create.c +@@ -118,8 +118,8 @@ _Noreturn void __pthread_exit(void *result) + * until the lock is released, which only happens after SYS_exit + * has been called, via the exit futex address pointing at the lock. + * This needs to happen after any possible calls to LOCK() that might +- * skip locking if libc.threads_minus_1 is zero. */ +- libc.threads_minus_1--; ++ * skip locking if process appears single-threaded. */ ++ if (!--libc.threads_minus_1) libc.need_locks = -1; + self->next->prev = self->prev; + self->prev->next = self->next; + self->prev = self->next = self; +@@ -339,7 +339,7 @@ int __pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict att + ~(1UL<<((SIGCANCEL-1)%(8*sizeof(long)))); + + __tl_lock(); +- libc.threads_minus_1++; ++ if (!libc.threads_minus_1++) libc.need_locks = 1; + ret = __clone((c11 ? start_c11 : start), stack, flags, args, &new->tid, TP_ADJ(new), &__thread_list_lock); + + /* All clone failures translate to EAGAIN. If explicit scheduling +@@ -363,7 +363,7 @@ int __pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict att + new->next->prev = new; + new->prev->next = new; + } else { +- libc.threads_minus_1--; ++ if (!--libc.threads_minus_1) libc.need_locks = 0; + } + __tl_unlock(); + __restore_sigs(&set); +-- +2.27.0 + diff --git a/0004-fix-unbounded-heap-expansion-race-in-malloc.patch b/0004-fix-unbounded-heap-expansion-race-in-malloc.patch new file mode 100644 index 0000000000000000000000000000000000000000..ef8b30e64e763f12a2337757e9928185a71711db --- /dev/null +++ b/0004-fix-unbounded-heap-expansion-race-in-malloc.patch @@ -0,0 +1,406 @@ +From 3e16313f8fe2ed143ae0267fd79d63014c24779f Mon Sep 17 00:00:00 2001 +From: Rich Felker +Date: Tue, 2 Jun 2020 17:37:14 -0400 +Subject: [PATCH] fix unbounded heap expansion race in malloc + +this has been a longstanding issue reported many times over the years, +with it becoming increasingly clear that it could be hit in practice. +under concurrent malloc and free from multiple threads, it's possible +to hit usage patterns where unbounded amounts of new memory are +obtained via brk/mmap despite the total nominal usage being small and +bounded. + +the underlying cause is that, as a fundamental consequence of keeping +locking as fine-grained as possible, the state where free has unbinned +an already-free chunk to merge it with a newly-freed one, but has not +yet re-binned the combined chunk, is exposed to other threads. this is +bad even with small chunks, and leads to suboptimal use of memory, but +where it really blows up is where the already-freed chunk in question +is the large free region "at the top of the heap". in this situation, +other threads momentarily see a state of having almost no free memory, +and conclude that they need to obtain more. + +as far as I can tell there is no fix for this that does not harm +performance. the fix made here forces all split/merge of free chunks +to take place under a single lock, which also takes the place of the +old free_lock, being held at least momentarily at the time of free to +determine whether there are neighboring free chunks that need merging. + +as a consequence, the pretrim, alloc_fwd, and alloc_rev operations no +longer make sense and are deleted. simplified merging now takes place +inline in free (__bin_chunk) and realloc. + +as commented in the source, holding the split_merge_lock precludes any +chunk transition from in-use to free state. for the most part, it also +precludes change to chunk header sizes. however, __memalign may still +modify the sizes of an in-use chunk to split it into two in-use +chunks. arguably this should require holding the split_merge_lock, but +that would necessitate refactoring to expose it externally, which is a +mess. and it turns out not to be necessary, at least assuming the +existing sloppy memory model malloc has been using, because if free +(__bin_chunk) or realloc sees any unsynchronized change to the size, +it will also see the in-use bit being set, and thereby can't do +anything with the neighboring chunk that changed size. +--- + src/malloc/malloc.c | 239 ++++++++++++++++---------------------------- + 1 file changed, 87 insertions(+), 152 deletions(-) + +diff --git a/src/malloc/malloc.c b/src/malloc/malloc.c +index a803d4c9..20598ec3 100644 +--- a/src/malloc/malloc.c ++++ b/src/malloc/malloc.c +@@ -17,7 +17,7 @@ + static struct { + volatile uint64_t binmap; + struct bin bins[64]; +- volatile int free_lock[2]; ++ volatile int split_merge_lock[2]; + } mal; + + int __malloc_replaced; +@@ -128,7 +128,6 @@ void __dump_heap(int x) + + static struct chunk *expand_heap(size_t n) + { +- static int heap_lock[2]; + static void *end; + void *p; + struct chunk *w; +@@ -138,13 +137,8 @@ static struct chunk *expand_heap(size_t n) + * we need room for an extra zero-sized sentinel chunk. */ + n += SIZE_ALIGN; + +- lock(heap_lock); +- + p = __expand_heap(&n); +- if (!p) { +- unlock(heap_lock); +- return 0; +- } ++ if (!p) return 0; + + /* If not just expanding existing space, we need to make a + * new sentinel chunk below the allocated space. */ +@@ -167,8 +161,6 @@ static struct chunk *expand_heap(size_t n) + w = MEM_TO_CHUNK(p); + w->csize = n | C_INUSE; + +- unlock(heap_lock); +- + return w; + } + +@@ -198,96 +190,44 @@ static void unbin(struct chunk *c, int i) + NEXT_CHUNK(c)->psize |= C_INUSE; + } + +-static int alloc_fwd(struct chunk *c) +-{ +- int i; +- size_t k; +- while (!((k=c->csize) & C_INUSE)) { +- i = bin_index(k); +- lock_bin(i); +- if (c->csize == k) { +- unbin(c, i); +- unlock_bin(i); +- return 1; +- } +- unlock_bin(i); +- } +- return 0; +-} +- +-static int alloc_rev(struct chunk *c) ++static void bin_chunk(struct chunk *self, int i) + { +- int i; +- size_t k; +- while (!((k=c->psize) & C_INUSE)) { +- i = bin_index(k); +- lock_bin(i); +- if (c->psize == k) { +- unbin(PREV_CHUNK(c), i); +- unlock_bin(i); +- return 1; +- } +- unlock_bin(i); +- } +- return 0; ++ self->next = BIN_TO_CHUNK(i); ++ self->prev = mal.bins[i].tail; ++ self->next->prev = self; ++ self->prev->next = self; ++ if (self->prev == BIN_TO_CHUNK(i)) ++ a_or_64(&mal.binmap, 1ULL<= n1 - DONTCARE) return; + + next = NEXT_CHUNK(self); + split = (void *)((char *)self + n); + +- split->prev = self->prev; +- split->next = self->next; +- split->prev->next = split; +- split->next->prev = split; + split->psize = n | C_INUSE; + split->csize = n1-n; + next->psize = n1-n; + self->csize = n | C_INUSE; +- return 1; +-} + +-static void trim(struct chunk *self, size_t n) +-{ +- size_t n1 = CHUNK_SIZE(self); +- struct chunk *next, *split; +- +- if (n >= n1 - DONTCARE) return; ++ int i = bin_index(n1-n); ++ lock_bin(i); + +- next = NEXT_CHUNK(self); +- split = (void *)((char *)self + n); +- +- split->psize = n | C_INUSE; +- split->csize = n1-n | C_INUSE; +- next->psize = n1-n | C_INUSE; +- self->csize = n | C_INUSE; ++ bin_chunk(split, i); + +- __bin_chunk(split); ++ unlock_bin(i); + } + + void *malloc(size_t n) + { + struct chunk *c; + int i, j; ++ uint64_t mask; + + if (adjust_size(&n) < 0) return 0; + +@@ -303,33 +243,37 @@ void *malloc(size_t n) + } + + i = bin_index_up(n); +- for (;;) { +- uint64_t mask = mal.binmap & -(1ULL<psize = c->csize = +- x->csize + CHUNK_SIZE(c); +- } +- break; ++ if (i<63 && (mal.binmap & (1ULL<psize; + char *base = (char *)self - extra; +@@ -408,27 +354,24 @@ void *realloc(void *p, size_t n) + /* Crash on corrupted footer (likely from buffer overflow) */ + if (next->psize != self->csize) a_crash(); + +- /* Merge adjacent chunks if we need more space. This is not +- * a waste of time even if we fail to get enough space, because our +- * subsequent call to free would otherwise have to do the merge. */ +- if (n > n1 && alloc_fwd(next)) { +- n1 += CHUNK_SIZE(next); +- next = NEXT_CHUNK(next); +- } +- /* FIXME: find what's wrong here and reenable it..? */ +- if (0 && n > n1 && alloc_rev(self)) { +- self = PREV_CHUNK(self); +- n1 += CHUNK_SIZE(self); +- } +- self->csize = n1 | C_INUSE; +- next->psize = n1 | C_INUSE; ++ lock(mal.split_merge_lock); + +- /* If we got enough space, split off the excess and return */ +- if (n <= n1) { +- //memmove(CHUNK_TO_MEM(self), p, n0-OVERHEAD); +- trim(self, n); +- return CHUNK_TO_MEM(self); ++ size_t nsize = next->csize & C_INUSE ? 0 : CHUNK_SIZE(next); ++ if (n0+nsize >= n) { ++ int i = bin_index(nsize); ++ lock_bin(i); ++ if (!(next->csize & C_INUSE)) { ++ unbin(next, i); ++ unlock_bin(i); ++ next = NEXT_CHUNK(next); ++ self->csize = next->psize = n0+nsize | C_INUSE; ++ trim(self, n); ++ unlock(mal.split_merge_lock); ++ return CHUNK_TO_MEM(self); ++ } ++ unlock_bin(i); + } ++ unlock(mal.split_merge_lock); + + copy_realloc: + /* As a last resort, allocate a new chunk and copy to it. */ +@@ -443,59 +386,51 @@ copy_free_ret: + void __bin_chunk(struct chunk *self) + { + struct chunk *next = NEXT_CHUNK(self); +- size_t final_size, new_size, size; +- int reclaim=0; +- int i; +- +- final_size = new_size = CHUNK_SIZE(self); + + /* Crash on corrupted footer (likely from buffer overflow) */ + if (next->psize != self->csize) a_crash(); + +- for (;;) { +- if (self->psize & next->csize & C_INUSE) { +- self->csize = final_size | C_INUSE; +- next->psize = final_size | C_INUSE; +- i = bin_index(final_size); +- lock_bin(i); +- lock(mal.free_lock); +- if (self->psize & next->csize & C_INUSE) +- break; +- unlock(mal.free_lock); +- unlock_bin(i); +- } ++ lock(mal.split_merge_lock); + +- if (alloc_rev(self)) { +- self = PREV_CHUNK(self); +- size = CHUNK_SIZE(self); +- final_size += size; +- if (new_size+size > RECLAIM && (new_size+size^size) > size) +- reclaim = 1; +- } ++ size_t osize = CHUNK_SIZE(self), size = osize; ++ ++ /* Since we hold split_merge_lock, only transition from free to ++ * in-use can race; in-use to free is impossible */ ++ size_t psize = self->psize & C_INUSE ? 0 : CHUNK_PSIZE(self); ++ size_t nsize = next->csize & C_INUSE ? 0 : CHUNK_SIZE(next); + +- if (alloc_fwd(next)) { +- size = CHUNK_SIZE(next); +- final_size += size; +- if (new_size+size > RECLAIM && (new_size+size^size) > size) +- reclaim = 1; ++ if (psize) { ++ int i = bin_index(psize); ++ lock_bin(i); ++ if (!(self->psize & C_INUSE)) { ++ struct chunk *prev = PREV_CHUNK(self); ++ unbin(prev, i); ++ self = prev; ++ size += psize; ++ } ++ unlock_bin(i); ++ } ++ if (nsize) { ++ int i = bin_index(nsize); ++ lock_bin(i); ++ if (!(next->csize & C_INUSE)) { ++ unbin(next, i); + next = NEXT_CHUNK(next); ++ size += nsize; + } ++ unlock_bin(i); + } + +- if (!(mal.binmap & 1ULL<csize = final_size; +- next->psize = final_size; +- unlock(mal.free_lock); ++ int i = bin_index(size); ++ lock_bin(i); + +- self->next = BIN_TO_CHUNK(i); +- self->prev = mal.bins[i].tail; +- self->next->prev = self; +- self->prev->next = self; ++ self->csize = size; ++ next->psize = size; ++ bin_chunk(self, i); ++ unlock(mal.split_merge_lock); + + /* Replace middle of large chunks with fresh zero pages */ +- if (reclaim) { ++ if (size > RECLAIM && (size^(size-osize)) > size-osize) { + uintptr_t a = (uintptr_t)self + SIZE_ALIGN+PAGE_SIZE-1 & -PAGE_SIZE; + uintptr_t b = (uintptr_t)next - SIZE_ALIGN & -PAGE_SIZE; + #if 1 +-- +2.27.0 + diff --git a/0005-fix-memset-overflow-in-oldmalloc-race-fix-overhaul.patch b/0005-fix-memset-overflow-in-oldmalloc-race-fix-overhaul.patch new file mode 100644 index 0000000000000000000000000000000000000000..61c6a69b0822a143341476eb9f795971588e28b8 --- /dev/null +++ b/0005-fix-memset-overflow-in-oldmalloc-race-fix-overhaul.patch @@ -0,0 +1,32 @@ +From cb5babdc8d624a3e3e7bea0b4e28a677a2f2fc46 Mon Sep 17 00:00:00 2001 +From: Rich Felker +Date: Tue, 16 Jun 2020 00:34:12 -0400 +Subject: [PATCH] fix memset overflow in oldmalloc race fix overhaul + +commit 3e16313f8fe2ed143ae0267fd79d63014c24779f introduced this bug by +making the copy case reachable with n (new size) smaller than n0 +(original size). this was left as the only way of shrinking an +allocation because it reduces fragmentation if a free chunk of the +appropriate size is available. when that's not the case, another +approach may be better, but any such improvement would be independent +of fixing this bug. +--- + src/malloc/malloc.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/malloc/malloc.c b/src/malloc/malloc.c +index 0a38690c..52af1975 100644 +--- a/src/malloc/malloc.c ++++ b/src/malloc/malloc.c +@@ -409,7 +409,7 @@ copy_realloc: + new = malloc(n-OVERHEAD); + if (!new) return 0; + copy_free_ret: +- memcpy(new, p, n0-OVERHEAD); ++ memcpy(new, p, (n +Date: Tue, 16 Jun 2020 00:53:57 -0400 +Subject: [PATCH] only use memcpy realloc to shrink if an exact-sized free + chunk exists + +otherwise, shrink in-place. as explained in the description of commit +3e16313f8fe2ed143ae0267fd79d63014c24779f, the split here is valid +without holding split_merge_lock because all chunks involved are in +the in-use state. +--- + src/malloc/malloc.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/src/malloc/malloc.c b/src/malloc/malloc.c +index 52af1975..c0997ad8 100644 +--- a/src/malloc/malloc.c ++++ b/src/malloc/malloc.c +@@ -385,6 +385,18 @@ void *realloc(void *p, size_t n) + /* Crash on corrupted footer (likely from buffer overflow) */ + if (next->psize != self->csize) a_crash(); + ++ if (n < n0) { ++ int i = bin_index_up(n); ++ int j = bin_index(n0); ++ if (icsize = split->psize = n | C_INUSE; ++ split->csize = next->psize = n0-n | C_INUSE; ++ __bin_chunk(split); ++ return CHUNK_TO_MEM(self); ++ } ++ + lock(mal.split_merge_lock); + + size_t nsize = next->csize & C_INUSE ? 0 : CHUNK_SIZE(next); +-- +2.27.0 + diff --git a/musl.spec b/musl.spec index d4359d569b83eac7c7a3b70901d2cbf4ab102481..15ebaa6981a4cc17e646196a99556c10a34c5d17 100644 --- a/musl.spec +++ b/musl.spec @@ -46,13 +46,20 @@ Name: musl Version: 1.2.0 -Release: 2 +Release: 3 Summary: An implementation of the standard library for Linux-based systems License: MIT URL: https://musl.libc.org Source0: %{url}/releases/%{name}-%{version}.tar.gz +Patch: 0001-reorder-thread-list-unlink-in-pthread_exit-after-all.patch +Patch: 0002-don-t-use-libc.threads_minus_1-as-relaxed-atomic-for.patch +Patch: 0003-restore-lock-skipping-for-processes-that-return-to-s.patch +Patch: 0004-fix-unbounded-heap-expansion-race-in-malloc.patch +Patch: 0005-fix-memset-overflow-in-oldmalloc-race-fix-overhaul.patch +Patch: 0006-only-use-memcpy-realloc-to-shrink-if-an-exact-sized-.patch + BuildRequires: gcc BuildRequires: make BuildRequires: gnupg2 @@ -124,7 +131,7 @@ This package provides a wrapper around gcc to compile programs and libraries with musl easily. %prep -%setup +%autosetup -n %{name}-%{version} -p1 %build export LDFLAGS="%{?build_ldflags} -Wl,-soname,ld-musl.so.1" @@ -180,6 +187,9 @@ ln -sr %{buildroot}%{_libdir}/libc.so %{buildroot}%{_libdir}/libutil.so.1 %{_libdir}/musl-gcc.specs %changelog +* Fri Nov 5 2021 zhuyan - 1.2.0-3 +- fix unbounded heap expansion race in malloc + * Tue May 11 2021 Jiajie Li - 1.2.0-2 - Add musl-gcc support