diff options
| author | otto <> | 2023-10-22 12:19:26 +0000 |
|---|---|---|
| committer | otto <> | 2023-10-22 12:19:26 +0000 |
| commit | 5fb161ca99c3827ce96bd2eafb54ade287c31caf (patch) | |
| tree | 5ec26e0f9f313f1e2f4272f4fe5c220a9c209e05 /src/lib/libc/stdlib | |
| parent | 403429e3508621ceec1a156d42df5ac1394229c0 (diff) | |
| download | openbsd-5fb161ca99c3827ce96bd2eafb54ade287c31caf.tar.gz openbsd-5fb161ca99c3827ce96bd2eafb54ade287c31caf.tar.bz2 openbsd-5fb161ca99c3827ce96bd2eafb54ade287c31caf.zip | |
When option D is active, store callers for all chunks; this avoids
the 0x0 call sites for leak reports. Also display more info on
detected write of free chunks: print the info about where the chunk
was allocated, and for the preceding chunk as well.
ok asou@
Diffstat (limited to 'src/lib/libc/stdlib')
| -rw-r--r-- | src/lib/libc/stdlib/malloc.3 | 38 | ||||
| -rw-r--r-- | src/lib/libc/stdlib/malloc.c | 226 |
2 files changed, 178 insertions, 86 deletions
diff --git a/src/lib/libc/stdlib/malloc.3 b/src/lib/libc/stdlib/malloc.3 index b700add823..667baa9b7c 100644 --- a/src/lib/libc/stdlib/malloc.3 +++ b/src/lib/libc/stdlib/malloc.3 | |||
| @@ -30,9 +30,9 @@ | |||
| 30 | .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 30 | .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 31 | .\" SUCH DAMAGE. | 31 | .\" SUCH DAMAGE. |
| 32 | .\" | 32 | .\" |
| 33 | .\" $OpenBSD: malloc.3,v 1.137 2023/07/01 18:35:14 otto Exp $ | 33 | .\" $OpenBSD: malloc.3,v 1.138 2023/10/22 12:19:26 otto Exp $ |
| 34 | .\" | 34 | .\" |
| 35 | .Dd $Mdocdate: July 1 2023 $ | 35 | .Dd $Mdocdate: October 22 2023 $ |
| 36 | .Dt MALLOC 3 | 36 | .Dt MALLOC 3 |
| 37 | .Os | 37 | .Os |
| 38 | .Sh NAME | 38 | .Sh NAME |
| @@ -307,7 +307,7 @@ These malloc options imply | |||
| 307 | .Cm D . | 307 | .Cm D . |
| 308 | .It Cm F | 308 | .It Cm F |
| 309 | .Dq Freecheck . | 309 | .Dq Freecheck . |
| 310 | Enable more extensive double free and use after free detection. | 310 | Enable more extensive double free and write after free detection. |
| 311 | All chunks in the delayed free list will be checked for double frees and | 311 | All chunks in the delayed free list will be checked for double frees and |
| 312 | write after frees. | 312 | write after frees. |
| 313 | Unused pages on the freelist are read and write protected to | 313 | Unused pages on the freelist are read and write protected to |
| @@ -641,18 +641,34 @@ or | |||
| 641 | reallocate an unallocated pointer was made. | 641 | reallocate an unallocated pointer was made. |
| 642 | .It Dq double free | 642 | .It Dq double free |
| 643 | There was an attempt to free an allocation that had already been freed. | 643 | There was an attempt to free an allocation that had already been freed. |
| 644 | .It Dq write after free | 644 | .It Dq write to free mem Va address Ns [ Va start Ns .. Ns Va end Ns ]@ Ns Va size |
| 645 | An allocation has been modified after it was freed. | 645 | An allocation has been modified after it was freed, |
| 646 | or a chunk that was never allocated was written to. | ||
| 647 | The | ||
| 648 | .Va range | ||
| 649 | at which corruption was detected is printed between [ and ]. | ||
| 650 | .Pp | ||
| 651 | Enabling option | ||
| 652 | .Cm D | ||
| 653 | allows malloc to print information about where the allocation | ||
| 654 | was done. | ||
| 646 | .It Dq modified chunk-pointer | 655 | .It Dq modified chunk-pointer |
| 647 | The pointer passed to | 656 | The pointer passed to |
| 648 | .Fn free | 657 | .Fn free |
| 649 | or a reallocation function has been modified. | 658 | or a reallocation function has been modified. |
| 650 | .It Dq canary corrupted address offset@length | 659 | .It Dq canary corrupted Va address Ns [ Va offset Ns ]@ Ns Va length Ns / Ns Va size |
| 651 | A byte after the requested size has been overwritten, | 660 | A byte after the requested |
| 661 | .Va length has been overwritten, | ||
| 652 | indicating a heap overflow. | 662 | indicating a heap overflow. |
| 653 | The offset at which corruption was detected is printed before the @, | 663 | The |
| 654 | and the requested length of the allocation after the @. | 664 | .Va offset |
| 655 | .It Dq recorded size oldsize inconsistent with size | 665 | at which corruption was detected is printed between [ and ], |
| 666 | the requested | ||
| 667 | .Va length | ||
| 668 | of the allocation is printed before the / and the | ||
| 669 | .Va size | ||
| 670 | of the allocation after the /. | ||
| 671 | .It Dq recorded size Va oldsize No inconsistent with Va size | ||
| 656 | .Fn recallocarray | 672 | .Fn recallocarray |
| 657 | or | 673 | or |
| 658 | .Fn freezero | 674 | .Fn freezero |
| @@ -676,7 +692,7 @@ functions nor utilize any other functions which may call | |||
| 676 | (e.g., | 692 | (e.g., |
| 677 | .Xr stdio 3 | 693 | .Xr stdio 3 |
| 678 | routines). | 694 | routines). |
| 679 | .It Dq unknown char in MALLOC_OPTIONS | 695 | .It Dq unknown char in Ev MALLOC_OPTIONS |
| 680 | We found something we didn't understand. | 696 | We found something we didn't understand. |
| 681 | .It any other error | 697 | .It any other error |
| 682 | .Fn malloc | 698 | .Fn malloc |
diff --git a/src/lib/libc/stdlib/malloc.c b/src/lib/libc/stdlib/malloc.c index 814a417145..c3b2332251 100644 --- a/src/lib/libc/stdlib/malloc.c +++ b/src/lib/libc/stdlib/malloc.c | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | /* $OpenBSD: malloc.c,v 1.290 2023/09/09 06:52:40 asou Exp $ */ | 1 | /* $OpenBSD: malloc.c,v 1.291 2023/10/22 12:19:26 otto Exp $ */ |
| 2 | /* | 2 | /* |
| 3 | * Copyright (c) 2008, 2010, 2011, 2016, 2023 Otto Moerbeek <otto@drijf.net> | 3 | * Copyright (c) 2008, 2010, 2011, 2016, 2023 Otto Moerbeek <otto@drijf.net> |
| 4 | * Copyright (c) 2012 Matthew Dempsky <matthew@openbsd.org> | 4 | * Copyright (c) 2012 Matthew Dempsky <matthew@openbsd.org> |
| @@ -112,7 +112,7 @@ struct region_info { | |||
| 112 | void *p; /* page; low bits used to mark chunks */ | 112 | void *p; /* page; low bits used to mark chunks */ |
| 113 | uintptr_t size; /* size for pages, or chunk_info pointer */ | 113 | uintptr_t size; /* size for pages, or chunk_info pointer */ |
| 114 | #ifdef MALLOC_STATS | 114 | #ifdef MALLOC_STATS |
| 115 | void *f; /* where allocated from */ | 115 | void **f; /* where allocated from */ |
| 116 | #endif | 116 | #endif |
| 117 | }; | 117 | }; |
| 118 | 118 | ||
| @@ -146,7 +146,7 @@ struct dir_info { | |||
| 146 | size_t regions_total; /* number of region slots */ | 146 | size_t regions_total; /* number of region slots */ |
| 147 | size_t regions_free; /* number of free slots */ | 147 | size_t regions_free; /* number of free slots */ |
| 148 | size_t rbytesused; /* random bytes used */ | 148 | size_t rbytesused; /* random bytes used */ |
| 149 | char *func; /* current function */ | 149 | const char *func; /* current function */ |
| 150 | int malloc_junk; /* junk fill? */ | 150 | int malloc_junk; /* junk fill? */ |
| 151 | int mmap_flag; /* extra flag for mmap */ | 151 | int mmap_flag; /* extra flag for mmap */ |
| 152 | int mutex; | 152 | int mutex; |
| @@ -166,6 +166,7 @@ struct dir_info { | |||
| 166 | void *chunk_pages; | 166 | void *chunk_pages; |
| 167 | size_t chunk_pages_used; | 167 | size_t chunk_pages_used; |
| 168 | #ifdef MALLOC_STATS | 168 | #ifdef MALLOC_STATS |
| 169 | void *caller; | ||
| 169 | size_t inserts; | 170 | size_t inserts; |
| 170 | size_t insert_collisions; | 171 | size_t insert_collisions; |
| 171 | size_t finds; | 172 | size_t finds; |
| @@ -183,12 +184,16 @@ struct dir_info { | |||
| 183 | #define STATS_INC(x) ((x)++) | 184 | #define STATS_INC(x) ((x)++) |
| 184 | #define STATS_ZERO(x) ((x) = 0) | 185 | #define STATS_ZERO(x) ((x) = 0) |
| 185 | #define STATS_SETF(x,y) ((x)->f = (y)) | 186 | #define STATS_SETF(x,y) ((x)->f = (y)) |
| 187 | #define STATS_SETFN(x,k,y) ((x)->f[k] = (y)) | ||
| 188 | #define SET_CALLER(x,y) if (DO_STATS) ((x)->caller = (y)) | ||
| 186 | #else | 189 | #else |
| 187 | #define STATS_ADD(x,y) /* nothing */ | 190 | #define STATS_ADD(x,y) /* nothing */ |
| 188 | #define STATS_SUB(x,y) /* nothing */ | 191 | #define STATS_SUB(x,y) /* nothing */ |
| 189 | #define STATS_INC(x) /* nothing */ | 192 | #define STATS_INC(x) /* nothing */ |
| 190 | #define STATS_ZERO(x) /* nothing */ | 193 | #define STATS_ZERO(x) /* nothing */ |
| 191 | #define STATS_SETF(x,y) /* nothing */ | 194 | #define STATS_SETF(x,y) /* nothing */ |
| 195 | #define STATS_SETFN(x,k,y) /* nothing */ | ||
| 196 | #define SET_CALLER(x,y) /* nothing */ | ||
| 192 | #endif /* MALLOC_STATS */ | 197 | #endif /* MALLOC_STATS */ |
| 193 | u_int32_t canary2; | 198 | u_int32_t canary2; |
| 194 | }; | 199 | }; |
| @@ -212,6 +217,8 @@ struct chunk_info { | |||
| 212 | u_short bits[1]; /* which chunks are free */ | 217 | u_short bits[1]; /* which chunks are free */ |
| 213 | }; | 218 | }; |
| 214 | 219 | ||
| 220 | #define CHUNK_FREE(i, n) ((i)->bits[(n) / MALLOC_BITS] & (1U << ((n) % MALLOC_BITS))) | ||
| 221 | |||
| 215 | struct malloc_readonly { | 222 | struct malloc_readonly { |
| 216 | /* Main bookkeeping information */ | 223 | /* Main bookkeeping information */ |
| 217 | struct dir_info *malloc_pool[_MALLOC_MUTEXES]; | 224 | struct dir_info *malloc_pool[_MALLOC_MUTEXES]; |
| @@ -227,7 +234,7 @@ struct malloc_readonly { | |||
| 227 | u_int junk_loc; /* variation in location of junk */ | 234 | u_int junk_loc; /* variation in location of junk */ |
| 228 | size_t malloc_guard; /* use guard pages after allocations? */ | 235 | size_t malloc_guard; /* use guard pages after allocations? */ |
| 229 | #ifdef MALLOC_STATS | 236 | #ifdef MALLOC_STATS |
| 230 | int malloc_stats; /* dump leak report at end */ | 237 | int malloc_stats; /* save callers, dump leak report at end */ |
| 231 | int malloc_verbose; /* dump verbose statistics at end */ | 238 | int malloc_verbose; /* dump verbose statistics at end */ |
| 232 | #define DO_STATS mopts.malloc_stats | 239 | #define DO_STATS mopts.malloc_stats |
| 233 | #else | 240 | #else |
| @@ -254,6 +261,7 @@ static __dead void wrterror(struct dir_info *d, char *msg, ...) | |||
| 254 | void malloc_dump(void); | 261 | void malloc_dump(void); |
| 255 | PROTO_NORMAL(malloc_dump); | 262 | PROTO_NORMAL(malloc_dump); |
| 256 | static void malloc_exit(void); | 263 | static void malloc_exit(void); |
| 264 | static void print_chunk_details(struct dir_info *, void *, size_t, size_t); | ||
| 257 | #endif | 265 | #endif |
| 258 | 266 | ||
| 259 | #if defined(__aarch64__) || \ | 267 | #if defined(__aarch64__) || \ |
| @@ -530,7 +538,7 @@ omalloc_init(void) | |||
| 530 | do { | 538 | do { |
| 531 | mopts.chunk_canaries = arc4random(); | 539 | mopts.chunk_canaries = arc4random(); |
| 532 | } while ((u_char)mopts.chunk_canaries == 0 || | 540 | } while ((u_char)mopts.chunk_canaries == 0 || |
| 533 | (u_char)mopts.chunk_canaries == SOME_FREEJUNK); | 541 | (u_char)mopts.chunk_canaries == SOME_FREEJUNK); |
| 534 | } | 542 | } |
| 535 | 543 | ||
| 536 | static void | 544 | static void |
| @@ -714,14 +722,14 @@ junk_free(int junk, void *p, size_t sz) | |||
| 714 | } | 722 | } |
| 715 | 723 | ||
| 716 | static inline void | 724 | static inline void |
| 717 | validate_junk(struct dir_info *pool, void *p, size_t sz) | 725 | validate_junk(struct dir_info *pool, void *p, size_t argsz) |
| 718 | { | 726 | { |
| 719 | size_t i, step = 1; | 727 | size_t i, sz, step = 1; |
| 720 | uint64_t *lp = p; | 728 | uint64_t *lp = p; |
| 721 | 729 | ||
| 722 | if (pool->malloc_junk == 0 || sz == 0) | 730 | if (pool->malloc_junk == 0 || argsz == 0) |
| 723 | return; | 731 | return; |
| 724 | sz /= sizeof(uint64_t); | 732 | sz = argsz / sizeof(uint64_t); |
| 725 | if (pool->malloc_junk == 1) { | 733 | if (pool->malloc_junk == 1) { |
| 726 | if (sz > MALLOC_PAGESIZE / sizeof(uint64_t)) | 734 | if (sz > MALLOC_PAGESIZE / sizeof(uint64_t)) |
| 727 | sz = MALLOC_PAGESIZE / sizeof(uint64_t); | 735 | sz = MALLOC_PAGESIZE / sizeof(uint64_t); |
| @@ -731,14 +739,23 @@ validate_junk(struct dir_info *pool, void *p, size_t sz) | |||
| 731 | } | 739 | } |
| 732 | /* see junk_free */ | 740 | /* see junk_free */ |
| 733 | for (i = mopts.junk_loc % step; i < sz; i += step) { | 741 | for (i = mopts.junk_loc % step; i < sz; i += step) { |
| 734 | if (lp[i] != SOME_FREEJUNK_ULL) | 742 | if (lp[i] != SOME_FREEJUNK_ULL) { |
| 735 | wrterror(pool, "write after free %p", p); | 743 | #ifdef MALLOC_STATS |
| 744 | if (DO_STATS && argsz <= MALLOC_MAXCHUNK) | ||
| 745 | print_chunk_details(pool, lp, argsz, i); | ||
| 746 | else | ||
| 747 | #endif | ||
| 748 | wrterror(pool, | ||
| 749 | "write to free mem %p[%zu..%zu]@%zu", | ||
| 750 | lp, i * sizeof(uint64_t), | ||
| 751 | (i + 1) * sizeof(uint64_t) - 1, argsz); | ||
| 752 | } | ||
| 736 | } | 753 | } |
| 737 | } | 754 | } |
| 738 | 755 | ||
| 739 | 756 | ||
| 740 | /* | 757 | /* |
| 741 | * Cache maintenance. | 758 | * Cache maintenance. |
| 742 | * Opposed to the regular region data structure, the sizes in the | 759 | * Opposed to the regular region data structure, the sizes in the |
| 743 | * cache are in MALLOC_PAGESIZE units. | 760 | * cache are in MALLOC_PAGESIZE units. |
| 744 | */ | 761 | */ |
| @@ -809,7 +826,7 @@ unmap(struct dir_info *d, void *p, size_t sz, size_t clear) | |||
| 809 | i = getrbyte(d) & (cache->max - 1); | 826 | i = getrbyte(d) & (cache->max - 1); |
| 810 | r = cache->pages[i]; | 827 | r = cache->pages[i]; |
| 811 | fresh = (uintptr_t)r & 1; | 828 | fresh = (uintptr_t)r & 1; |
| 812 | *(uintptr_t*)&r &= ~1ULL; | 829 | *(uintptr_t*)&r &= ~1UL; |
| 813 | if (!fresh && !mopts.malloc_freeunmap) | 830 | if (!fresh && !mopts.malloc_freeunmap) |
| 814 | validate_junk(d, r, sz); | 831 | validate_junk(d, r, sz); |
| 815 | if (munmap(r, sz)) | 832 | if (munmap(r, sz)) |
| @@ -995,11 +1012,18 @@ omalloc_make_chunks(struct dir_info *d, u_int bucket, u_int listnum) | |||
| 995 | { | 1012 | { |
| 996 | struct chunk_info *bp; | 1013 | struct chunk_info *bp; |
| 997 | void *pp; | 1014 | void *pp; |
| 1015 | void *ff = NULL; | ||
| 998 | 1016 | ||
| 999 | /* Allocate a new bucket */ | 1017 | /* Allocate a new bucket */ |
| 1000 | pp = map(d, MALLOC_PAGESIZE, 0); | 1018 | pp = map(d, MALLOC_PAGESIZE, 0); |
| 1001 | if (pp == MAP_FAILED) | 1019 | if (pp == MAP_FAILED) |
| 1002 | return NULL; | 1020 | return NULL; |
| 1021 | if (DO_STATS) { | ||
| 1022 | ff = map(d, MALLOC_PAGESIZE, 0); | ||
| 1023 | if (ff == MAP_FAILED) | ||
| 1024 | goto err; | ||
| 1025 | memset(ff, 0, sizeof(void *) * MALLOC_PAGESIZE / B2ALLOC(bucket)); | ||
| 1026 | } | ||
| 1003 | 1027 | ||
| 1004 | /* memory protect the page allocated in the malloc(0) case */ | 1028 | /* memory protect the page allocated in the malloc(0) case */ |
| 1005 | if (bucket == 0 && mprotect(pp, MALLOC_PAGESIZE, PROT_NONE) == -1) | 1029 | if (bucket == 0 && mprotect(pp, MALLOC_PAGESIZE, PROT_NONE) == -1) |
| @@ -1011,7 +1035,7 @@ omalloc_make_chunks(struct dir_info *d, u_int bucket, u_int listnum) | |||
| 1011 | bp->page = pp; | 1035 | bp->page = pp; |
| 1012 | 1036 | ||
| 1013 | if (insert(d, (void *)((uintptr_t)pp | (bucket + 1)), (uintptr_t)bp, | 1037 | if (insert(d, (void *)((uintptr_t)pp | (bucket + 1)), (uintptr_t)bp, |
| 1014 | NULL)) | 1038 | ff)) |
| 1015 | goto err; | 1039 | goto err; |
| 1016 | LIST_INSERT_HEAD(&d->chunk_dir[bucket][listnum], bp, entries); | 1040 | LIST_INSERT_HEAD(&d->chunk_dir[bucket][listnum], bp, entries); |
| 1017 | 1041 | ||
| @@ -1022,6 +1046,8 @@ omalloc_make_chunks(struct dir_info *d, u_int bucket, u_int listnum) | |||
| 1022 | 1046 | ||
| 1023 | err: | 1047 | err: |
| 1024 | unmap(d, pp, MALLOC_PAGESIZE, 0); | 1048 | unmap(d, pp, MALLOC_PAGESIZE, 0); |
| 1049 | if (ff != NULL && ff != MAP_FAILED) | ||
| 1050 | unmap(d, ff, MALLOC_PAGESIZE, 0); | ||
| 1025 | return NULL; | 1051 | return NULL; |
| 1026 | } | 1052 | } |
| 1027 | 1053 | ||
| @@ -1101,7 +1127,7 @@ fill_canary(char *ptr, size_t sz, size_t allocated) | |||
| 1101 | * Allocate a chunk | 1127 | * Allocate a chunk |
| 1102 | */ | 1128 | */ |
| 1103 | static void * | 1129 | static void * |
| 1104 | malloc_bytes(struct dir_info *d, size_t size, void *f) | 1130 | malloc_bytes(struct dir_info *d, size_t size) |
| 1105 | { | 1131 | { |
| 1106 | u_int i, r, bucket, listnum; | 1132 | u_int i, r, bucket, listnum; |
| 1107 | size_t k; | 1133 | size_t k; |
| @@ -1153,11 +1179,6 @@ malloc_bytes(struct dir_info *d, size_t size, void *f) | |||
| 1153 | } | 1179 | } |
| 1154 | } | 1180 | } |
| 1155 | found: | 1181 | found: |
| 1156 | if (i == 0 && k == 0 && DO_STATS) { | ||
| 1157 | struct region_info *r = find(d, bp->page); | ||
| 1158 | STATS_SETF(r, f); | ||
| 1159 | } | ||
| 1160 | |||
| 1161 | *lp ^= 1 << k; | 1182 | *lp ^= 1 << k; |
| 1162 | 1183 | ||
| 1163 | /* If there are no more free, remove from free-list */ | 1184 | /* If there are no more free, remove from free-list */ |
| @@ -1170,6 +1191,11 @@ found: | |||
| 1170 | if (mopts.chunk_canaries && size > 0) | 1191 | if (mopts.chunk_canaries && size > 0) |
| 1171 | bp->bits[bp->offset + k] = size; | 1192 | bp->bits[bp->offset + k] = size; |
| 1172 | 1193 | ||
| 1194 | if (DO_STATS) { | ||
| 1195 | struct region_info *r = find(d, bp->page); | ||
| 1196 | STATS_SETFN(r, k, d->caller); | ||
| 1197 | } | ||
| 1198 | |||
| 1173 | k *= B2ALLOC(bp->bucket); | 1199 | k *= B2ALLOC(bp->bucket); |
| 1174 | 1200 | ||
| 1175 | p = (char *)bp->page + k; | 1201 | p = (char *)bp->page + k; |
| @@ -1194,8 +1220,8 @@ validate_canary(struct dir_info *d, u_char *ptr, size_t sz, size_t allocated) | |||
| 1194 | 1220 | ||
| 1195 | while (p < q) { | 1221 | while (p < q) { |
| 1196 | if (*p != (u_char)mopts.chunk_canaries && *p != SOME_JUNK) { | 1222 | if (*p != (u_char)mopts.chunk_canaries && *p != SOME_JUNK) { |
| 1197 | wrterror(d, "canary corrupted %p %#tx@%#zx%s", | 1223 | wrterror(d, "canary corrupted %p[%tu]@%zu/%zu%s", |
| 1198 | ptr, p - ptr, sz, | 1224 | ptr, p - ptr, sz, allocated, |
| 1199 | *p == SOME_FREEJUNK ? " (double free?)" : ""); | 1225 | *p == SOME_FREEJUNK ? " (double free?)" : ""); |
| 1200 | } | 1226 | } |
| 1201 | p++; | 1227 | p++; |
| @@ -1215,8 +1241,7 @@ find_chunknum(struct dir_info *d, struct chunk_info *info, void *ptr, int check) | |||
| 1215 | 1241 | ||
| 1216 | if ((uintptr_t)ptr & (MALLOC_MINSIZE - 1)) | 1242 | if ((uintptr_t)ptr & (MALLOC_MINSIZE - 1)) |
| 1217 | wrterror(d, "modified chunk-pointer %p", ptr); | 1243 | wrterror(d, "modified chunk-pointer %p", ptr); |
| 1218 | if (info->bits[chunknum / MALLOC_BITS] & | 1244 | if (CHUNK_FREE(info, chunknum)) |
| 1219 | (1U << (chunknum % MALLOC_BITS))) | ||
| 1220 | wrterror(d, "double free %p", ptr); | 1245 | wrterror(d, "double free %p", ptr); |
| 1221 | if (check && info->bucket > 0) { | 1246 | if (check && info->bucket > 0) { |
| 1222 | validate_canary(d, ptr, info->bits[info->offset + chunknum], | 1247 | validate_canary(d, ptr, info->bits[info->offset + chunknum], |
| @@ -1239,9 +1264,6 @@ free_bytes(struct dir_info *d, struct region_info *r, void *ptr) | |||
| 1239 | info = (struct chunk_info *)r->size; | 1264 | info = (struct chunk_info *)r->size; |
| 1240 | chunknum = find_chunknum(d, info, ptr, 0); | 1265 | chunknum = find_chunknum(d, info, ptr, 0); |
| 1241 | 1266 | ||
| 1242 | if (chunknum == 0) | ||
| 1243 | STATS_SETF(r, NULL); | ||
| 1244 | |||
| 1245 | info->bits[chunknum / MALLOC_BITS] |= 1U << (chunknum % MALLOC_BITS); | 1267 | info->bits[chunknum / MALLOC_BITS] |= 1U << (chunknum % MALLOC_BITS); |
| 1246 | info->free++; | 1268 | info->free++; |
| 1247 | 1269 | ||
| @@ -1261,18 +1283,22 @@ free_bytes(struct dir_info *d, struct region_info *r, void *ptr) | |||
| 1261 | if (info->bucket == 0 && !mopts.malloc_freeunmap) | 1283 | if (info->bucket == 0 && !mopts.malloc_freeunmap) |
| 1262 | mprotect(info->page, MALLOC_PAGESIZE, PROT_READ | PROT_WRITE); | 1284 | mprotect(info->page, MALLOC_PAGESIZE, PROT_READ | PROT_WRITE); |
| 1263 | unmap(d, info->page, MALLOC_PAGESIZE, 0); | 1285 | unmap(d, info->page, MALLOC_PAGESIZE, 0); |
| 1286 | #ifdef MALLOC_STATS | ||
| 1287 | if (r->f != NULL) { | ||
| 1288 | unmap(d, r->f, MALLOC_PAGESIZE, MALLOC_PAGESIZE); | ||
| 1289 | r->f = NULL; | ||
| 1290 | } | ||
| 1291 | #endif | ||
| 1264 | 1292 | ||
| 1265 | delete(d, r); | 1293 | delete(d, r); |
| 1266 | mp = &d->chunk_info_list[info->bucket]; | 1294 | mp = &d->chunk_info_list[info->bucket]; |
| 1267 | LIST_INSERT_HEAD(mp, info, entries); | 1295 | LIST_INSERT_HEAD(mp, info, entries); |
| 1268 | } | 1296 | } |
| 1269 | 1297 | ||
| 1270 | |||
| 1271 | |||
| 1272 | static void * | 1298 | static void * |
| 1273 | omalloc(struct dir_info *pool, size_t sz, int zero_fill, void *f) | 1299 | omalloc(struct dir_info *pool, size_t sz, int zero_fill) |
| 1274 | { | 1300 | { |
| 1275 | void *p; | 1301 | void *p, *caller = NULL; |
| 1276 | size_t psz; | 1302 | size_t psz; |
| 1277 | 1303 | ||
| 1278 | if (sz > MALLOC_MAXCHUNK) { | 1304 | if (sz > MALLOC_MAXCHUNK) { |
| @@ -1287,7 +1313,11 @@ omalloc(struct dir_info *pool, size_t sz, int zero_fill, void *f) | |||
| 1287 | errno = ENOMEM; | 1313 | errno = ENOMEM; |
| 1288 | return NULL; | 1314 | return NULL; |
| 1289 | } | 1315 | } |
| 1290 | if (insert(pool, p, sz, f)) { | 1316 | #ifdef MALLOC_STATS |
| 1317 | if (DO_STATS) | ||
| 1318 | caller = pool->caller; | ||
| 1319 | #endif | ||
| 1320 | if (insert(pool, p, sz, caller)) { | ||
| 1291 | unmap(pool, p, psz, 0); | 1321 | unmap(pool, p, psz, 0); |
| 1292 | errno = ENOMEM; | 1322 | errno = ENOMEM; |
| 1293 | return NULL; | 1323 | return NULL; |
| @@ -1324,7 +1354,7 @@ omalloc(struct dir_info *pool, size_t sz, int zero_fill, void *f) | |||
| 1324 | 1354 | ||
| 1325 | } else { | 1355 | } else { |
| 1326 | /* takes care of SOME_JUNK */ | 1356 | /* takes care of SOME_JUNK */ |
| 1327 | p = malloc_bytes(pool, sz, f); | 1357 | p = malloc_bytes(pool, sz); |
| 1328 | if (zero_fill && p != NULL && sz > 0) | 1358 | if (zero_fill && p != NULL && sz > 0) |
| 1329 | memset(p, 0, sz); | 1359 | memset(p, 0, sz); |
| 1330 | } | 1360 | } |
| @@ -1473,7 +1503,8 @@ malloc(size_t size) | |||
| 1473 | int saved_errno = errno; | 1503 | int saved_errno = errno; |
| 1474 | 1504 | ||
| 1475 | PROLOGUE(getpool(), "malloc") | 1505 | PROLOGUE(getpool(), "malloc") |
| 1476 | r = omalloc(d, size, 0, caller()); | 1506 | SET_CALLER(d, caller()); |
| 1507 | r = omalloc(d, size, 0); | ||
| 1477 | EPILOGUE() | 1508 | EPILOGUE() |
| 1478 | return r; | 1509 | return r; |
| 1479 | } | 1510 | } |
| @@ -1487,7 +1518,8 @@ malloc_conceal(size_t size) | |||
| 1487 | int saved_errno = errno; | 1518 | int saved_errno = errno; |
| 1488 | 1519 | ||
| 1489 | PROLOGUE(mopts.malloc_pool[0], "malloc_conceal") | 1520 | PROLOGUE(mopts.malloc_pool[0], "malloc_conceal") |
| 1490 | r = omalloc(d, size, 0, caller()); | 1521 | SET_CALLER(d, caller()); |
| 1522 | r = omalloc(d, size, 0); | ||
| 1491 | EPILOGUE() | 1523 | EPILOGUE() |
| 1492 | return r; | 1524 | return r; |
| 1493 | } | 1525 | } |
| @@ -1495,7 +1527,7 @@ DEF_WEAK(malloc_conceal); | |||
| 1495 | 1527 | ||
| 1496 | static struct region_info * | 1528 | static struct region_info * |
| 1497 | findpool(void *p, struct dir_info *argpool, struct dir_info **foundpool, | 1529 | findpool(void *p, struct dir_info *argpool, struct dir_info **foundpool, |
| 1498 | char **saved_function) | 1530 | const char ** saved_function) |
| 1499 | { | 1531 | { |
| 1500 | struct dir_info *pool = argpool; | 1532 | struct dir_info *pool = argpool; |
| 1501 | struct region_info *r = find(pool, p); | 1533 | struct region_info *r = find(pool, p); |
| @@ -1533,7 +1565,7 @@ ofree(struct dir_info **argpool, void *p, int clear, int check, size_t argsz) | |||
| 1533 | { | 1565 | { |
| 1534 | struct region_info *r; | 1566 | struct region_info *r; |
| 1535 | struct dir_info *pool; | 1567 | struct dir_info *pool; |
| 1536 | char *saved_function; | 1568 | const char *saved_function; |
| 1537 | size_t sz; | 1569 | size_t sz; |
| 1538 | 1570 | ||
| 1539 | r = findpool(p, *argpool, &pool, &saved_function); | 1571 | r = findpool(p, *argpool, &pool, &saved_function); |
| @@ -1721,11 +1753,11 @@ freezero(void *ptr, size_t sz) | |||
| 1721 | DEF_WEAK(freezero); | 1753 | DEF_WEAK(freezero); |
| 1722 | 1754 | ||
| 1723 | static void * | 1755 | static void * |
| 1724 | orealloc(struct dir_info **argpool, void *p, size_t newsz, void *f) | 1756 | orealloc(struct dir_info **argpool, void *p, size_t newsz) |
| 1725 | { | 1757 | { |
| 1726 | struct region_info *r; | 1758 | struct region_info *r; |
| 1727 | struct dir_info *pool; | 1759 | struct dir_info *pool; |
| 1728 | char *saved_function; | 1760 | const char *saved_function; |
| 1729 | struct chunk_info *info; | 1761 | struct chunk_info *info; |
| 1730 | size_t oldsz, goldsz, gnewsz; | 1762 | size_t oldsz, goldsz, gnewsz; |
| 1731 | void *q, *ret; | 1763 | void *q, *ret; |
| @@ -1733,7 +1765,7 @@ orealloc(struct dir_info **argpool, void *p, size_t newsz, void *f) | |||
| 1733 | int forced; | 1765 | int forced; |
| 1734 | 1766 | ||
| 1735 | if (p == NULL) | 1767 | if (p == NULL) |
| 1736 | return omalloc(*argpool, newsz, 0, f); | 1768 | return omalloc(*argpool, newsz, 0); |
| 1737 | 1769 | ||
| 1738 | if (newsz >= SIZE_MAX - mopts.malloc_guard - MALLOC_PAGESIZE) { | 1770 | if (newsz >= SIZE_MAX - mopts.malloc_guard - MALLOC_PAGESIZE) { |
| 1739 | errno = ENOMEM; | 1771 | errno = ENOMEM; |
| @@ -1797,7 +1829,7 @@ orealloc(struct dir_info **argpool, void *p, size_t newsz, void *f) | |||
| 1797 | if (mopts.chunk_canaries) | 1829 | if (mopts.chunk_canaries) |
| 1798 | fill_canary(p, newsz, | 1830 | fill_canary(p, newsz, |
| 1799 | PAGEROUND(newsz)); | 1831 | PAGEROUND(newsz)); |
| 1800 | STATS_SETF(r, f); | 1832 | STATS_SETF(r, (*argpool)->caller); |
| 1801 | STATS_INC(pool->cheap_reallocs); | 1833 | STATS_INC(pool->cheap_reallocs); |
| 1802 | ret = p; | 1834 | ret = p; |
| 1803 | goto done; | 1835 | goto done; |
| @@ -1822,7 +1854,7 @@ orealloc(struct dir_info **argpool, void *p, size_t newsz, void *f) | |||
| 1822 | p = pp; | 1854 | p = pp; |
| 1823 | } else if (mopts.chunk_canaries) | 1855 | } else if (mopts.chunk_canaries) |
| 1824 | fill_canary(p, newsz, PAGEROUND(newsz)); | 1856 | fill_canary(p, newsz, PAGEROUND(newsz)); |
| 1825 | STATS_SETF(r, f); | 1857 | STATS_SETF(r, (*argpool)->caller); |
| 1826 | ret = p; | 1858 | ret = p; |
| 1827 | goto done; | 1859 | goto done; |
| 1828 | } else { | 1860 | } else { |
| @@ -1844,7 +1876,7 @@ orealloc(struct dir_info **argpool, void *p, size_t newsz, void *f) | |||
| 1844 | if (mopts.chunk_canaries) | 1876 | if (mopts.chunk_canaries) |
| 1845 | fill_canary(p, newsz, PAGEROUND(newsz)); | 1877 | fill_canary(p, newsz, PAGEROUND(newsz)); |
| 1846 | } | 1878 | } |
| 1847 | STATS_SETF(r, f); | 1879 | STATS_SETF(r, (*argpool)->caller); |
| 1848 | ret = p; | 1880 | ret = p; |
| 1849 | goto done; | 1881 | goto done; |
| 1850 | } | 1882 | } |
| @@ -1859,12 +1891,12 @@ orealloc(struct dir_info **argpool, void *p, size_t newsz, void *f) | |||
| 1859 | info->bits[info->offset + chunknum] = newsz; | 1891 | info->bits[info->offset + chunknum] = newsz; |
| 1860 | fill_canary(p, newsz, B2SIZE(info->bucket)); | 1892 | fill_canary(p, newsz, B2SIZE(info->bucket)); |
| 1861 | } | 1893 | } |
| 1862 | if (DO_STATS && chunknum == 0) | 1894 | if (DO_STATS) |
| 1863 | STATS_SETF(r, f); | 1895 | STATS_SETFN(r, chunknum, (*argpool)->caller); |
| 1864 | ret = p; | 1896 | ret = p; |
| 1865 | } else if (newsz != oldsz || forced) { | 1897 | } else if (newsz != oldsz || forced) { |
| 1866 | /* create new allocation */ | 1898 | /* create new allocation */ |
| 1867 | q = omalloc(pool, newsz, 0, f); | 1899 | q = omalloc(pool, newsz, 0); |
| 1868 | if (q == NULL) { | 1900 | if (q == NULL) { |
| 1869 | ret = NULL; | 1901 | ret = NULL; |
| 1870 | goto done; | 1902 | goto done; |
| @@ -1877,8 +1909,8 @@ orealloc(struct dir_info **argpool, void *p, size_t newsz, void *f) | |||
| 1877 | /* oldsz == newsz */ | 1909 | /* oldsz == newsz */ |
| 1878 | if (newsz != 0) | 1910 | if (newsz != 0) |
| 1879 | wrterror(pool, "realloc internal inconsistency"); | 1911 | wrterror(pool, "realloc internal inconsistency"); |
| 1880 | if (DO_STATS && chunknum == 0) | 1912 | if (DO_STATS) |
| 1881 | STATS_SETF(r, f); | 1913 | STATS_SETFN(r, chunknum, (*argpool)->caller); |
| 1882 | ret = p; | 1914 | ret = p; |
| 1883 | } | 1915 | } |
| 1884 | done: | 1916 | done: |
| @@ -1897,7 +1929,8 @@ realloc(void *ptr, size_t size) | |||
| 1897 | int saved_errno = errno; | 1929 | int saved_errno = errno; |
| 1898 | 1930 | ||
| 1899 | PROLOGUE(getpool(), "realloc") | 1931 | PROLOGUE(getpool(), "realloc") |
| 1900 | r = orealloc(&d, ptr, size, caller()); | 1932 | SET_CALLER(d, caller()); |
| 1933 | r = orealloc(&d, ptr, size); | ||
| 1901 | EPILOGUE() | 1934 | EPILOGUE() |
| 1902 | return r; | 1935 | return r; |
| 1903 | } | 1936 | } |
| @@ -1917,6 +1950,7 @@ calloc(size_t nmemb, size_t size) | |||
| 1917 | int saved_errno = errno; | 1950 | int saved_errno = errno; |
| 1918 | 1951 | ||
| 1919 | PROLOGUE(getpool(), "calloc") | 1952 | PROLOGUE(getpool(), "calloc") |
| 1953 | SET_CALLER(d, caller()); | ||
| 1920 | if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && | 1954 | if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && |
| 1921 | nmemb > 0 && SIZE_MAX / nmemb < size) { | 1955 | nmemb > 0 && SIZE_MAX / nmemb < size) { |
| 1922 | d->active--; | 1956 | d->active--; |
| @@ -1928,7 +1962,7 @@ calloc(size_t nmemb, size_t size) | |||
| 1928 | } | 1962 | } |
| 1929 | 1963 | ||
| 1930 | size *= nmemb; | 1964 | size *= nmemb; |
| 1931 | r = omalloc(d, size, 1, caller()); | 1965 | r = omalloc(d, size, 1); |
| 1932 | EPILOGUE() | 1966 | EPILOGUE() |
| 1933 | return r; | 1967 | return r; |
| 1934 | } | 1968 | } |
| @@ -1942,6 +1976,7 @@ calloc_conceal(size_t nmemb, size_t size) | |||
| 1942 | int saved_errno = errno; | 1976 | int saved_errno = errno; |
| 1943 | 1977 | ||
| 1944 | PROLOGUE(mopts.malloc_pool[0], "calloc_conceal") | 1978 | PROLOGUE(mopts.malloc_pool[0], "calloc_conceal") |
| 1979 | SET_CALLER(d, caller()); | ||
| 1945 | if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && | 1980 | if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && |
| 1946 | nmemb > 0 && SIZE_MAX / nmemb < size) { | 1981 | nmemb > 0 && SIZE_MAX / nmemb < size) { |
| 1947 | d->active--; | 1982 | d->active--; |
| @@ -1953,7 +1988,7 @@ calloc_conceal(size_t nmemb, size_t size) | |||
| 1953 | } | 1988 | } |
| 1954 | 1989 | ||
| 1955 | size *= nmemb; | 1990 | size *= nmemb; |
| 1956 | r = omalloc(d, size, 1, caller()); | 1991 | r = omalloc(d, size, 1); |
| 1957 | EPILOGUE() | 1992 | EPILOGUE() |
| 1958 | return r; | 1993 | return r; |
| 1959 | } | 1994 | } |
| @@ -1961,16 +1996,16 @@ DEF_WEAK(calloc_conceal); | |||
| 1961 | 1996 | ||
| 1962 | static void * | 1997 | static void * |
| 1963 | orecallocarray(struct dir_info **argpool, void *p, size_t oldsize, | 1998 | orecallocarray(struct dir_info **argpool, void *p, size_t oldsize, |
| 1964 | size_t newsize, void *f) | 1999 | size_t newsize) |
| 1965 | { | 2000 | { |
| 1966 | struct region_info *r; | 2001 | struct region_info *r; |
| 1967 | struct dir_info *pool; | 2002 | struct dir_info *pool; |
| 1968 | char *saved_function; | 2003 | const char *saved_function; |
| 1969 | void *newptr; | 2004 | void *newptr; |
| 1970 | size_t sz; | 2005 | size_t sz; |
| 1971 | 2006 | ||
| 1972 | if (p == NULL) | 2007 | if (p == NULL) |
| 1973 | return omalloc(*argpool, newsize, 1, f); | 2008 | return omalloc(*argpool, newsize, 1); |
| 1974 | 2009 | ||
| 1975 | if (oldsize == newsize) | 2010 | if (oldsize == newsize) |
| 1976 | return p; | 2011 | return p; |
| @@ -2001,7 +2036,7 @@ orecallocarray(struct dir_info **argpool, void *p, size_t oldsize, | |||
| 2001 | sz - mopts.malloc_guard, oldsize); | 2036 | sz - mopts.malloc_guard, oldsize); |
| 2002 | } | 2037 | } |
| 2003 | 2038 | ||
| 2004 | newptr = omalloc(pool, newsize, 0, f); | 2039 | newptr = omalloc(pool, newsize, 0); |
| 2005 | if (newptr == NULL) | 2040 | if (newptr == NULL) |
| 2006 | goto done; | 2041 | goto done; |
| 2007 | 2042 | ||
| @@ -2086,6 +2121,7 @@ recallocarray(void *ptr, size_t oldnmemb, size_t newnmemb, size_t size) | |||
| 2086 | return recallocarray_p(ptr, oldnmemb, newnmemb, size); | 2121 | return recallocarray_p(ptr, oldnmemb, newnmemb, size); |
| 2087 | 2122 | ||
| 2088 | PROLOGUE(getpool(), "recallocarray") | 2123 | PROLOGUE(getpool(), "recallocarray") |
| 2124 | SET_CALLER(d, caller()); | ||
| 2089 | 2125 | ||
| 2090 | if ((newnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && | 2126 | if ((newnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && |
| 2091 | newnmemb > 0 && SIZE_MAX / newnmemb < size) { | 2127 | newnmemb > 0 && SIZE_MAX / newnmemb < size) { |
| @@ -2109,7 +2145,7 @@ recallocarray(void *ptr, size_t oldnmemb, size_t newnmemb, size_t size) | |||
| 2109 | oldsize = oldnmemb * size; | 2145 | oldsize = oldnmemb * size; |
| 2110 | } | 2146 | } |
| 2111 | 2147 | ||
| 2112 | r = orecallocarray(&d, ptr, oldsize, newsize, caller()); | 2148 | r = orecallocarray(&d, ptr, oldsize, newsize); |
| 2113 | EPILOGUE() | 2149 | EPILOGUE() |
| 2114 | return r; | 2150 | return r; |
| 2115 | } | 2151 | } |
| @@ -2150,11 +2186,10 @@ mapalign(struct dir_info *d, size_t alignment, size_t sz, int zero_fill) | |||
| 2150 | } | 2186 | } |
| 2151 | 2187 | ||
| 2152 | static void * | 2188 | static void * |
| 2153 | omemalign(struct dir_info *pool, size_t alignment, size_t sz, int zero_fill, | 2189 | omemalign(struct dir_info *pool, size_t alignment, size_t sz, int zero_fill) |
| 2154 | void *f) | ||
| 2155 | { | 2190 | { |
| 2156 | size_t psz; | 2191 | size_t psz; |
| 2157 | void *p; | 2192 | void *p, *caller = NULL; |
| 2158 | 2193 | ||
| 2159 | /* If between half a page and a page, avoid MALLOC_MOVE. */ | 2194 | /* If between half a page and a page, avoid MALLOC_MOVE. */ |
| 2160 | if (sz > MALLOC_MAXCHUNK && sz < MALLOC_PAGESIZE) | 2195 | if (sz > MALLOC_MAXCHUNK && sz < MALLOC_PAGESIZE) |
| @@ -2174,7 +2209,7 @@ omemalign(struct dir_info *pool, size_t alignment, size_t sz, int zero_fill, | |||
| 2174 | pof2 <<= 1; | 2209 | pof2 <<= 1; |
| 2175 | } else | 2210 | } else |
| 2176 | pof2 = sz; | 2211 | pof2 = sz; |
| 2177 | return omalloc(pool, pof2, zero_fill, f); | 2212 | return omalloc(pool, pof2, zero_fill); |
| 2178 | } | 2213 | } |
| 2179 | 2214 | ||
| 2180 | if (sz >= SIZE_MAX - mopts.malloc_guard - MALLOC_PAGESIZE) { | 2215 | if (sz >= SIZE_MAX - mopts.malloc_guard - MALLOC_PAGESIZE) { |
| @@ -2193,7 +2228,11 @@ omemalign(struct dir_info *pool, size_t alignment, size_t sz, int zero_fill, | |||
| 2193 | return NULL; | 2228 | return NULL; |
| 2194 | } | 2229 | } |
| 2195 | 2230 | ||
| 2196 | if (insert(pool, p, sz, f)) { | 2231 | #ifdef MALLOC_STATS |
| 2232 | if (DO_STATS) | ||
| 2233 | caller = pool->caller; | ||
| 2234 | #endif | ||
| 2235 | if (insert(pool, p, sz, caller)) { | ||
| 2197 | unmap(pool, p, psz, 0); | 2236 | unmap(pool, p, psz, 0); |
| 2198 | errno = ENOMEM; | 2237 | errno = ENOMEM; |
| 2199 | return NULL; | 2238 | return NULL; |
| @@ -2241,7 +2280,8 @@ posix_memalign(void **memptr, size_t alignment, size_t size) | |||
| 2241 | malloc_recurse(d); | 2280 | malloc_recurse(d); |
| 2242 | goto err; | 2281 | goto err; |
| 2243 | } | 2282 | } |
| 2244 | r = omemalign(d, alignment, size, 0, caller()); | 2283 | SET_CALLER(d, caller()); |
| 2284 | r = omemalign(d, alignment, size, 0); | ||
| 2245 | d->active--; | 2285 | d->active--; |
| 2246 | _MALLOC_UNLOCK(d->mutex); | 2286 | _MALLOC_UNLOCK(d->mutex); |
| 2247 | if (r == NULL) { | 2287 | if (r == NULL) { |
| @@ -2279,7 +2319,8 @@ aligned_alloc(size_t alignment, size_t size) | |||
| 2279 | } | 2319 | } |
| 2280 | 2320 | ||
| 2281 | PROLOGUE(getpool(), "aligned_alloc") | 2321 | PROLOGUE(getpool(), "aligned_alloc") |
| 2282 | r = omemalign(d, alignment, size, 0, caller()); | 2322 | SET_CALLER(d, caller()); |
| 2323 | r = omemalign(d, alignment, size, 0); | ||
| 2283 | EPILOGUE() | 2324 | EPILOGUE() |
| 2284 | return r; | 2325 | return r; |
| 2285 | } | 2326 | } |
| @@ -2288,6 +2329,45 @@ DEF_STRONG(aligned_alloc); | |||
| 2288 | #ifdef MALLOC_STATS | 2329 | #ifdef MALLOC_STATS |
| 2289 | 2330 | ||
| 2290 | static void | 2331 | static void |
| 2332 | print_chunk_details(struct dir_info *pool, void *p, size_t sz, size_t i) | ||
| 2333 | { | ||
| 2334 | struct region_info *r; | ||
| 2335 | struct chunk_info *chunkinfo; | ||
| 2336 | uint32_t chunknum; | ||
| 2337 | Dl_info info; | ||
| 2338 | const char *caller, *pcaller = NULL; | ||
| 2339 | const char *object = "."; | ||
| 2340 | const char *pobject = "."; | ||
| 2341 | const char *msg = ""; | ||
| 2342 | |||
| 2343 | r = find(pool, p); | ||
| 2344 | chunkinfo = (struct chunk_info *)r->size; | ||
| 2345 | chunknum = find_chunknum(pool, chunkinfo, p, 0); | ||
| 2346 | caller = r->f[chunknum]; | ||
| 2347 | if (dladdr(caller, &info) != 0) { | ||
| 2348 | caller -= (uintptr_t)info.dli_fbase; | ||
| 2349 | object = info.dli_fname; | ||
| 2350 | } | ||
| 2351 | if (chunknum > 0) { | ||
| 2352 | chunknum--; | ||
| 2353 | pcaller = r->f[chunknum]; | ||
| 2354 | if (dladdr(pcaller, &info) != 0) { | ||
| 2355 | pcaller -= (uintptr_t)info.dli_fbase; | ||
| 2356 | pobject = info.dli_fname; | ||
| 2357 | } | ||
| 2358 | if (CHUNK_FREE(chunkinfo, chunknum)) | ||
| 2359 | msg = " (now free)"; | ||
| 2360 | } | ||
| 2361 | |||
| 2362 | wrterror(pool, | ||
| 2363 | "write to free chunk %p[%zu..%zu]@%zu allocated at %s %p " | ||
| 2364 | "(preceding chunk %p allocated at %s %p%s)", | ||
| 2365 | p, i * sizeof(uint64_t), | ||
| 2366 | (i + 1) * sizeof(uint64_t) - 1, sz, object, caller, p - sz, | ||
| 2367 | pobject, pcaller, msg); | ||
| 2368 | } | ||
| 2369 | |||
| 2370 | static void | ||
| 2291 | ulog(const char *format, ...) | 2371 | ulog(const char *format, ...) |
| 2292 | { | 2372 | { |
| 2293 | va_list ap; | 2373 | va_list ap; |
| @@ -2413,23 +2493,19 @@ dump_leaks(struct leaktree *leaks) | |||
| 2413 | } | 2493 | } |
| 2414 | 2494 | ||
| 2415 | static void | 2495 | static void |
| 2416 | dump_chunk(struct leaktree* leaks, struct chunk_info *p, void *f, | 2496 | dump_chunk(struct leaktree* leaks, struct chunk_info *p, void **f, |
| 2417 | int fromfreelist) | 2497 | int fromfreelist) |
| 2418 | { | 2498 | { |
| 2419 | while (p != NULL) { | 2499 | while (p != NULL) { |
| 2420 | if (mopts.malloc_verbose) | 2500 | if (mopts.malloc_verbose) |
| 2421 | ulog("chunk %18p %18p %4zu %d/%d\n", | 2501 | ulog("chunk %18p %18p %4zu %d/%d\n", |
| 2422 | p->page, ((p->bits[0] & 1) ? NULL : f), | 2502 | p->page, NULL, |
| 2423 | B2SIZE(p->bucket), p->free, p->total); | 2503 | B2SIZE(p->bucket), p->free, p->total); |
| 2424 | if (!fromfreelist) { | 2504 | if (!fromfreelist) { |
| 2425 | size_t sz = B2SIZE(p->bucket); | 2505 | size_t i, sz = B2SIZE(p->bucket); |
| 2426 | if (p->bits[0] & 1) | 2506 | for (i = 0; i < p->total; i++) { |
| 2427 | putleakinfo(leaks, NULL, sz, p->total - | 2507 | if (!CHUNK_FREE(p, i)) |
| 2428 | p->free); | 2508 | putleakinfo(leaks, f[i], sz, 1); |
| 2429 | else { | ||
| 2430 | putleakinfo(leaks, f, sz, 1); | ||
| 2431 | putleakinfo(leaks, NULL, sz, | ||
| 2432 | p->total - p->free - 1); | ||
| 2433 | } | 2509 | } |
| 2434 | break; | 2510 | break; |
| 2435 | } | 2511 | } |
| @@ -2501,7 +2577,7 @@ malloc_dump1(int poolno, struct dir_info *d, struct leaktree *leaks) | |||
| 2501 | 2577 | ||
| 2502 | if (mopts.malloc_verbose) { | 2578 | if (mopts.malloc_verbose) { |
| 2503 | ulog("Malloc dir of %s pool %d at %p\n", __progname, poolno, d); | 2579 | ulog("Malloc dir of %s pool %d at %p\n", __progname, poolno, d); |
| 2504 | ulog("MT=%d J=%d Fl=%x\n", d->malloc_mt, d->malloc_junk, | 2580 | ulog("MT=%d J=%d Fl=%#x\n", d->malloc_mt, d->malloc_junk, |
| 2505 | d->mmap_flag); | 2581 | d->mmap_flag); |
| 2506 | ulog("Region slots free %zu/%zu\n", | 2582 | ulog("Region slots free %zu/%zu\n", |
| 2507 | d->regions_free, d->regions_total); | 2583 | d->regions_free, d->regions_total); |
| @@ -2589,7 +2665,7 @@ malloc_exit(void) | |||
| 2589 | int save_errno = errno; | 2665 | int save_errno = errno; |
| 2590 | 2666 | ||
| 2591 | ulog("******** Start dump %s *******\n", __progname); | 2667 | ulog("******** Start dump %s *******\n", __progname); |
| 2592 | ulog("M=%u I=%d F=%d U=%d J=%d R=%d X=%d C=%d cache=%u " | 2668 | ulog("M=%u I=%d F=%d U=%d J=%d R=%d X=%d C=%#x cache=%u " |
| 2593 | "G=%zu\n", | 2669 | "G=%zu\n", |
| 2594 | mopts.malloc_mutexes, | 2670 | mopts.malloc_mutexes, |
| 2595 | mopts.internal_funcs, mopts.malloc_freecheck, | 2671 | mopts.internal_funcs, mopts.malloc_freecheck, |
