upgrade to 1.2.2

Signed-off-by: zhuyan <zhuyan34@huawei.com>
This commit is contained in:
zhuyan 2022-01-15 15:06:13 +08:00
parent 150a40cee4
commit 94310617ee
9 changed files with 12 additions and 728 deletions

View File

@ -1,55 +0,0 @@
From 4d5aa20a94a2d3fae3e69289dc23ecafbd0c16c4 Mon Sep 17 00:00:00 2001
From: Rich Felker <dalias@aerifal.cx>
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

View File

@ -1,78 +0,0 @@
From e01b5939b38aea5ecbe41670643199825874b26c Mon Sep 17 00:00:00 2001
From: Rich Felker <dalias@aerifal.cx>
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

View File

@ -1,104 +0,0 @@
From 540d5bd85c8dc9efda0f01b6fc26fbec6ff5011e Mon Sep 17 00:00:00 2001
From: zhuyan <zhuyan34@huawei.com>
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 <dalias@aerifal.cx>
Signed-off-by: zhuyan <zhuyan34@huawei.com>
---
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

View File

@ -1,406 +0,0 @@
From 3e16313f8fe2ed143ae0267fd79d63014c24779f Mon Sep 17 00:00:00 2001
From: Rich Felker <dalias@aerifal.cx>
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<<i);
}
-
-/* pretrim - trims a chunk _prior_ to removing it from its bin.
- * Must be called with i as the ideal bin for size n, j the bin
- * for the _free_ chunk self, and bin j locked. */
-static int pretrim(struct chunk *self, size_t n, int i, int j)
+static void trim(struct chunk *self, size_t n)
{
- size_t n1;
+ size_t n1 = CHUNK_SIZE(self);
struct chunk *next, *split;
- /* We cannot pretrim if it would require re-binning. */
- if (j < 40) return 0;
- if (j < i+3) {
- if (j != 63) return 0;
- n1 = CHUNK_SIZE(self);
- if (n1-n <= MMAP_THRESHOLD) return 0;
- } else {
- n1 = CHUNK_SIZE(self);
- }
- if (bin_index(n1-n) != j) return 0;
+ if (n >= 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<<i);
- if (!mask) {
- c = expand_heap(n);
- if (!c) return 0;
- if (alloc_rev(c)) {
- struct chunk *x = c;
- c = PREV_CHUNK(c);
- NEXT_CHUNK(x)->psize = c->csize =
- x->csize + CHUNK_SIZE(c);
- }
- break;
+ if (i<63 && (mal.binmap & (1ULL<<i))) {
+ lock_bin(i);
+ c = mal.bins[i].head;
+ if (c != BIN_TO_CHUNK(i) && CHUNK_SIZE(c)-n <= DONTCARE) {
+ unbin(c, i);
+ unlock_bin(i);
+ return CHUNK_TO_MEM(c);
}
+ unlock_bin(i);
+ }
+ lock(mal.split_merge_lock);
+ for (mask = mal.binmap & -(1ULL<<i); mask; mask -= (mask&-mask)) {
j = first_set(mask);
lock_bin(j);
c = mal.bins[j].head;
if (c != BIN_TO_CHUNK(j)) {
- if (!pretrim(c, n, i, j)) unbin(c, j);
+ unbin(c, j);
unlock_bin(j);
break;
}
unlock_bin(j);
}
-
- /* Now patch up in case we over-allocated */
+ if (!mask) {
+ c = expand_heap(n);
+ if (!c) {
+ unlock(mal.split_merge_lock);
+ return 0;
+ }
+ }
trim(c, n);
-
+ unlock(mal.split_merge_lock);
return CHUNK_TO_MEM(c);
}
@@ -382,6 +326,8 @@ void *realloc(void *p, size_t n)
self = MEM_TO_CHUNK(p);
n1 = n0 = CHUNK_SIZE(self);
+ if (n<=n0 && n0-n<=DONTCARE) return p;
+
if (IS_MMAPPED(self)) {
size_t extra = self->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<<i))
- a_or_64(&mal.binmap, 1ULL<<i);
-
- self->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

View File

@ -1,32 +0,0 @@
From cb5babdc8d624a3e3e7bea0b4e28a677a2f2fc46 Mon Sep 17 00:00:00 2001
From: Rich Felker <dalias@aerifal.cx>
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<n0 ? n : n0) - OVERHEAD);
free(CHUNK_TO_MEM(self));
return new;
}
--
2.27.0

View File

@ -1,40 +0,0 @@
From fca7428c096066482d8c3f52450810288e27515c Mon Sep 17 00:00:00 2001
From: Rich Felker <dalias@aerifal.cx>
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 (i<j && (mal.binmap & (1ULL << i)))
+ goto copy_realloc;
+ struct chunk *split = (void *)((char *)self + n);
+ self->csize = 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

Binary file not shown.

BIN
musl-1.2.2.tar.gz Normal file

Binary file not shown.

View File

@ -45,21 +45,14 @@
%global _includedir %{_prefix}/musl/include
Name: musl
Version: 1.2.0
Release: 3
Version: 1.2.2
Release: 2
Summary: An implementation of the standard library for Linux-based systems
License: MIT
URL: https://musl.libc.org
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
@ -131,7 +124,7 @@ This package provides a wrapper around gcc to compile
programs and libraries with musl easily.
%prep
%autosetup -n %{name}-%{version} -p1
%autosetup
%build
export LDFLAGS="%{?build_ldflags} -Wl,-soname,ld-musl.so.1"
@ -187,8 +180,14 @@ ln -sr %{buildroot}%{_libdir}/libc.so %{buildroot}%{_libdir}/libutil.so.1
%{_libdir}/musl-gcc.specs
%changelog
* Fri Nov 5 2021 zhuyan <zhuyan34@huawei.com> - 1.2.0-3
- fix unbounded heap expansion race in malloc
* Mon Oct 25 2021 zhuyan <zhuyan34@huawei.com> - 1.2.2-2
- fix compile error
* Fri Sep 24 2021 zhuyan <zhuyan34@huawei.com> - 1.2.2-1
- upgrade to 1.2.2
* Tue Aug 19 2021 zhuyan <zhuyan34@huawei.com> - 1.2.0-3
- fix CVE-2020-28928
* Tue May 11 2021 Jiajie Li <lijiajie11@huawei.com> - 1.2.0-2
- Add musl-gcc support