aboutsummaryrefslogtreecommitdiffstats
path: root/src/gmalloc.c
diff options
context:
space:
mode:
authorPaul Eggert2017-06-21 11:45:05 -0700
committerPaul Eggert2017-06-21 12:18:56 -0700
commitac1ceadce8916ac71c8d549b66631d499077494d (patch)
treecae7d50bf7e028da328458b815e44d2314ce8da8 /src/gmalloc.c
parent1de9e2986ca25d8153681e9fab19199a00021b05 (diff)
downloademacs-ac1ceadce8916ac71c8d549b66631d499077494d.tar.gz
emacs-ac1ceadce8916ac71c8d549b66631d499077494d.zip
Fix temacs hybrid_malloc core dump
Without this patch, ./temacs would dump core sometimes on Fedora 25 x86-64. The problem was that the hybrid allocator assumed that all pointers into bss_sbrk_buffer are allocated via gmalloc. This assumption is not true on Fedora, because the standard memory allocator calls gdefault_morecore, which means its blocks are interleaved with our blocks. Usually the code happened to work, because our data structures agreed with the glibc data structures, but this was merely luck due to a shared pedigree, and as glibc mutates our luck has run out. * src/gmalloc.c (ALLOCATED_BEFORE_DUMPING) [HYBRID_MALLOC]: Remove; no longer needed. (BLOCK): Use unsigned division, as that does the right thing near zero. (register_heapinfo, __malloc_internal_nolock, __free_internal_nolock) (_realloc_internal_nolock): Big blocks now have type -1, not 0, as 0 now means the block is not ours. (morecore_nolock): Omit now-unnecessary casts to size_t. (allocated_via_gmalloc) [HYBRID_MALLOC]: New function. (hybrid_free, hybrid_realloc) [HYBRID_MALLOC]: Use it, to avoid calling the wrong free or realloc function in some cases.
Diffstat (limited to 'src/gmalloc.c')
-rw-r--r--src/gmalloc.c71
1 files changed, 41 insertions, 30 deletions
diff --git a/src/gmalloc.c b/src/gmalloc.c
index 49f1fafccc0..103c19156be 100644
--- a/src/gmalloc.c
+++ b/src/gmalloc.c
@@ -77,11 +77,6 @@ extern void *(*__morecore) (ptrdiff_t);
77#ifdef HYBRID_MALLOC 77#ifdef HYBRID_MALLOC
78# include "sheap.h" 78# include "sheap.h"
79# define DUMPED bss_sbrk_did_unexec 79# define DUMPED bss_sbrk_did_unexec
80static bool
81ALLOCATED_BEFORE_DUMPING (char *p)
82{
83 return bss_sbrk_buffer <= p && p < bss_sbrk_buffer + STATIC_HEAP_SIZE;
84}
85#endif 80#endif
86 81
87#ifdef __cplusplus 82#ifdef __cplusplus
@@ -133,8 +128,13 @@ typedef union
133 /* Heap information for a busy block. */ 128 /* Heap information for a busy block. */
134 struct 129 struct
135 { 130 {
136 /* Zero for a large (multiblock) object, or positive giving the 131 /* Zero for a block that is not one of ours (typically,
137 logarithm to the base two of the fragment size. */ 132 allocated by system malloc), positive for the log base 2 of
133 the fragment size of a fragmented block, -1 for the first
134 block of a multiblock object, and unspecified for later
135 blocks of that object. Type-0 blocks can be present
136 because the system malloc can be invoked by library
137 functions in an undumped Emacs. */
138 int type; 138 int type;
139 union 139 union
140 { 140 {
@@ -166,7 +166,7 @@ extern char *_heapbase;
166extern malloc_info *_heapinfo; 166extern malloc_info *_heapinfo;
167 167
168/* Address to block number and vice versa. */ 168/* Address to block number and vice versa. */
169#define BLOCK(A) (((char *) (A) - _heapbase) / BLOCKSIZE + 1) 169#define BLOCK(A) ((size_t) ((char *) (A) - _heapbase) / BLOCKSIZE + 1)
170#define ADDRESS(B) ((void *) (((B) - 1) * BLOCKSIZE + _heapbase)) 170#define ADDRESS(B) ((void *) (((B) - 1) * BLOCKSIZE + _heapbase))
171 171
172/* Current search index for the heap table. */ 172/* Current search index for the heap table. */
@@ -491,7 +491,7 @@ register_heapinfo (void)
491 ++_chunks_used; 491 ++_chunks_used;
492 492
493 /* Describe the heapinfo block itself in the heapinfo. */ 493 /* Describe the heapinfo block itself in the heapinfo. */
494 _heapinfo[block].busy.type = 0; 494 _heapinfo[block].busy.type = -1;
495 _heapinfo[block].busy.info.size = blocks; 495 _heapinfo[block].busy.info.size = blocks;
496 /* Leave back-pointers for malloc_find_address. */ 496 /* Leave back-pointers for malloc_find_address. */
497 while (--blocks > 0) 497 while (--blocks > 0)
@@ -608,7 +608,7 @@ morecore_nolock (size_t size)
608 PROTECT_MALLOC_STATE (0); 608 PROTECT_MALLOC_STATE (0);
609 609
610 /* Check if we need to grow the info table. */ 610 /* Check if we need to grow the info table. */
611 if ((size_t) BLOCK ((char *) result + size) > heapsize) 611 if (heapsize < BLOCK ((char *) result + size))
612 { 612 {
613 /* Calculate the new _heapinfo table size. We do not account for the 613 /* Calculate the new _heapinfo table size. We do not account for the
614 added blocks in the table itself, as we hope to place them in 614 added blocks in the table itself, as we hope to place them in
@@ -617,7 +617,7 @@ morecore_nolock (size_t size)
617 newsize = heapsize; 617 newsize = heapsize;
618 do 618 do
619 newsize *= 2; 619 newsize *= 2;
620 while ((size_t) BLOCK ((char *) result + size) > newsize); 620 while (newsize < BLOCK ((char *) result + size));
621 621
622 /* We must not reuse existing core for the new info table when called 622 /* We must not reuse existing core for the new info table when called
623 from realloc in the case of growing a large block, because the 623 from realloc in the case of growing a large block, because the
@@ -665,8 +665,7 @@ morecore_nolock (size_t size)
665 665
666 /* Is it big enough to record status for its own space? 666 /* Is it big enough to record status for its own space?
667 If so, we win. */ 667 If so, we win. */
668 if ((size_t) BLOCK ((char *) newinfo 668 if (BLOCK ((char *) newinfo + newsize * sizeof (malloc_info))
669 + newsize * sizeof (malloc_info))
670 < newsize) 669 < newsize)
671 break; 670 break;
672 671
@@ -883,7 +882,7 @@ _malloc_internal_nolock (size_t size)
883 --_chunks_free; 882 --_chunks_free;
884 } 883 }
885 884
886 _heapinfo[block].busy.type = 0; 885 _heapinfo[block].busy.type = -1;
887 _heapinfo[block].busy.info.size = blocks; 886 _heapinfo[block].busy.info.size = blocks;
888 ++_chunks_used; 887 ++_chunks_used;
889 _bytes_used += blocks * BLOCKSIZE; 888 _bytes_used += blocks * BLOCKSIZE;
@@ -1026,7 +1025,7 @@ _free_internal_nolock (void *ptr)
1026 type = _heapinfo[block].busy.type; 1025 type = _heapinfo[block].busy.type;
1027 switch (type) 1026 switch (type)
1028 { 1027 {
1029 case 0: 1028 case -1:
1030 /* Get as many statistics as early as we can. */ 1029 /* Get as many statistics as early as we can. */
1031 --_chunks_used; 1030 --_chunks_used;
1032 _bytes_used -= _heapinfo[block].busy.info.size * BLOCKSIZE; 1031 _bytes_used -= _heapinfo[block].busy.info.size * BLOCKSIZE;
@@ -1187,7 +1186,7 @@ _free_internal_nolock (void *ptr)
1187 prev->prev->next = next; 1186 prev->prev->next = next;
1188 if (next != NULL) 1187 if (next != NULL)
1189 next->prev = prev->prev; 1188 next->prev = prev->prev;
1190 _heapinfo[block].busy.type = 0; 1189 _heapinfo[block].busy.type = -1;
1191 _heapinfo[block].busy.info.size = 1; 1190 _heapinfo[block].busy.info.size = 1;
1192 1191
1193 /* Keep the statistics accurate. */ 1192 /* Keep the statistics accurate. */
@@ -1326,7 +1325,7 @@ _realloc_internal_nolock (void *ptr, size_t size)
1326 type = _heapinfo[block].busy.type; 1325 type = _heapinfo[block].busy.type;
1327 switch (type) 1326 switch (type)
1328 { 1327 {
1329 case 0: 1328 case -1:
1330 /* Maybe reallocate a large block to a small fragment. */ 1329 /* Maybe reallocate a large block to a small fragment. */
1331 if (size <= BLOCKSIZE / 2) 1330 if (size <= BLOCKSIZE / 2)
1332 { 1331 {
@@ -1346,7 +1345,7 @@ _realloc_internal_nolock (void *ptr, size_t size)
1346 { 1345 {
1347 /* The new size is smaller; return 1346 /* The new size is smaller; return
1348 excess memory to the free list. */ 1347 excess memory to the free list. */
1349 _heapinfo[block + blocks].busy.type = 0; 1348 _heapinfo[block + blocks].busy.type = -1;
1350 _heapinfo[block + blocks].busy.info.size 1349 _heapinfo[block + blocks].busy.info.size
1351 = _heapinfo[block].busy.info.size - blocks; 1350 = _heapinfo[block].busy.info.size - blocks;
1352 _heapinfo[block].busy.info.size = blocks; 1351 _heapinfo[block].busy.info.size = blocks;
@@ -1721,6 +1720,20 @@ extern void *aligned_alloc (size_t alignment, size_t size);
1721extern int posix_memalign (void **memptr, size_t alignment, size_t size); 1720extern int posix_memalign (void **memptr, size_t alignment, size_t size);
1722#endif 1721#endif
1723 1722
1723/* Assuming PTR was allocated via the hybrid malloc, return true if
1724 PTR was allocated via gmalloc, not the system malloc. Also, return
1725 true if _heaplimit is zero; this can happen temporarily when
1726 gmalloc calls itself for internal use, and in that case PTR is
1727 already known to be allocated via gmalloc. */
1728
1729static bool
1730allocated_via_gmalloc (void *ptr)
1731{
1732 size_t block = BLOCK (ptr);
1733 size_t blockmax = _heaplimit - 1;
1734 return block <= blockmax && _heapinfo[block].busy.type != 0;
1735}
1736
1724/* See the comments near the beginning of this file for explanations 1737/* See the comments near the beginning of this file for explanations
1725 of the following functions. */ 1738 of the following functions. */
1726 1739
@@ -1743,13 +1756,10 @@ hybrid_calloc (size_t nmemb, size_t size)
1743void 1756void
1744hybrid_free (void *ptr) 1757hybrid_free (void *ptr)
1745{ 1758{
1746 if (!DUMPED) 1759 if (allocated_via_gmalloc (ptr))
1747 gfree (ptr); 1760 gfree (ptr);
1748 else if (!ALLOCATED_BEFORE_DUMPING (ptr)) 1761 else
1749 free (ptr); 1762 free (ptr);
1750 /* Otherwise the dumped emacs is trying to free something allocated
1751 before dumping; do nothing. */
1752 return;
1753} 1763}
1754 1764
1755#if defined HAVE_ALIGNED_ALLOC || defined HAVE_POSIX_MEMALIGN 1765#if defined HAVE_ALIGNED_ALLOC || defined HAVE_POSIX_MEMALIGN
@@ -1775,19 +1785,20 @@ hybrid_realloc (void *ptr, size_t size)
1775 int type; 1785 int type;
1776 size_t block, oldsize; 1786 size_t block, oldsize;
1777 1787
1788 if (!ptr)
1789 return hybrid_malloc (size);
1790 if (!allocated_via_gmalloc (ptr))
1791 return realloc (ptr, size);
1778 if (!DUMPED) 1792 if (!DUMPED)
1779 return grealloc (ptr, size); 1793 return grealloc (ptr, size);
1780 if (!ALLOCATED_BEFORE_DUMPING (ptr))
1781 return realloc (ptr, size);
1782 1794
1783 /* The dumped emacs is trying to realloc storage allocated before 1795 /* The dumped emacs is trying to realloc storage allocated before
1784 dumping. We just malloc new space and copy the data. */ 1796 dumping via gmalloc. Allocate new space and copy the data. Do
1785 if (size == 0 || ptr == NULL) 1797 not bother with gfree (ptr), as that would just waste time. */
1786 return malloc (size); 1798 block = BLOCK (ptr);
1787 block = ((char *) ptr - _heapbase) / BLOCKSIZE + 1;
1788 type = _heapinfo[block].busy.type; 1799 type = _heapinfo[block].busy.type;
1789 oldsize = 1800 oldsize =
1790 type == 0 ? _heapinfo[block].busy.info.size * BLOCKSIZE 1801 type < 0 ? _heapinfo[block].busy.info.size * BLOCKSIZE
1791 : (size_t) 1 << type; 1802 : (size_t) 1 << type;
1792 result = malloc (size); 1803 result = malloc (size);
1793 if (result) 1804 if (result)