/src/bind9/lib/isc/jemalloc_shim.h
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) Internet Systems Consortium, Inc. ("ISC") |
3 | | * |
4 | | * SPDX-License-Identifier: MPL-2.0 |
5 | | * |
6 | | * This Source Code Form is subject to the terms of the Mozilla Public |
7 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
8 | | * file, you can obtain one at https://mozilla.org/MPL/2.0/. |
9 | | * |
10 | | * See the COPYRIGHT file distributed with this work for additional |
11 | | * information regarding copyright ownership. |
12 | | */ |
13 | | |
14 | | #pragma once |
15 | | |
16 | | #if !defined(HAVE_JEMALLOC) |
17 | | |
18 | | #include <limits.h> |
19 | | #include <stddef.h> |
20 | | #include <stdint.h> |
21 | | #include <stdlib.h> |
22 | | #include <string.h> |
23 | | #include <strings.h> |
24 | | |
25 | | #include <isc/overflow.h> |
26 | | #include <isc/util.h> |
27 | | |
28 | | const char *malloc_conf = NULL; |
29 | | |
30 | | static size_t |
31 | 369M | get_aligned_size(size_t alignment, size_t size) { |
32 | | /* |
33 | | * C23 dropped the requirement that size be an integral multiple of |
34 | | * alignment, but some implementations still enforce it at runtime, |
35 | | * so round the size up. |
36 | | */ |
37 | 369M | return ISC_CHECKED_ADD(size, alignment - 1) & ~(alignment - 1); |
38 | 369M | } |
39 | | |
40 | | #ifndef HAVE_FREE_SIZED |
41 | | static void |
42 | 117M | free_sized(void *ptr, size_t size ISC_ATTR_UNUSED) { |
43 | 117M | free(ptr); |
44 | 117M | } |
45 | | #endif |
46 | | |
47 | | #ifndef HAVE_FREE_ALIGNED_SIZED |
48 | | static void |
49 | | free_aligned_sized(void *ptr, size_t alignment ISC_ATTR_UNUSED, |
50 | 33.2k | size_t size ISC_ATTR_UNUSED) { |
51 | 33.2k | free(ptr); |
52 | 33.2k | } |
53 | | #endif |
54 | | |
55 | | /* |
56 | | * The MALLOCX_ZERO, MALLOCX_ALIGN and MALLOCX_*_GET macros were taken |
57 | | * literal from jemalloc_macros.h and jemalloc_internal_types.h headers |
58 | | * respectively. |
59 | | */ |
60 | | |
61 | 117M | #define MALLOCX_ZERO ((int)0x40) |
62 | 117M | #define MALLOCX_ZERO_GET(flags) ((bool)(flags & MALLOCX_ZERO)) |
63 | | |
64 | 838M | #define MALLOCX_LG_ALIGN_MASK ((int)0x3f) |
65 | | #if __SIZEOF_POINTER__ == 4 |
66 | | #define MALLOCX_ALIGN(a) ((int)(ffs((int)(a)) - 1)) |
67 | | #else |
68 | | #define MALLOCX_ALIGN(a) \ |
69 | | ((int)(((size_t)(a) < (size_t)INT_MAX) \ |
70 | | ? ffs((int)(a)) - 1 \ |
71 | | : ffs((int)(((size_t)(a)) >> 32)) + 31)) |
72 | | #endif |
73 | | #define MALLOCX_ALIGN_GET(flags) \ |
74 | 838M | (((size_t)1) << (flags & MALLOCX_LG_ALIGN_MASK)) |
75 | | |
76 | | /* |
77 | | * malloc() already guarantees alignment suitable for any fundamental |
78 | | * type; only larger alignment requests need the aligned paths below. |
79 | | */ |
80 | | #define MALLOCX_NEEDS_ALIGN(flags) \ |
81 | 234M | (MALLOCX_ALIGN_GET(flags) > _Alignof(max_align_t)) |
82 | | |
83 | | typedef union { |
84 | | struct { |
85 | | size_t size; |
86 | | int flags; |
87 | | }; |
88 | | max_align_t __alignment; |
89 | | } size_info; |
90 | | |
91 | | /* |
92 | | * The size_info header sits a multiple of the requested alignment before the |
93 | | * returned pointer. The caller passes the matching MALLOCX_ALIGN() flag on |
94 | | * deallocation too, so the base address is always recomputable from the flags |
95 | | * and nothing needs to be stored. |
96 | | */ |
97 | | |
98 | | static inline size_t |
99 | 369M | get_header_size(int flags) { |
100 | 369M | return get_aligned_size(MALLOCX_ALIGN_GET(flags), sizeof(size_info)); |
101 | 369M | } |
102 | | |
103 | | static inline size_info * |
104 | 135M | get_size_info(void *ptr, int flags) { |
105 | 135M | return (size_info *)((uint8_t *)ptr - get_header_size(flags)); |
106 | 135M | } |
107 | | |
108 | | static inline void * |
109 | 117M | mallocx(size_t size, int flags) { |
110 | 117M | size_t alignment = MALLOCX_ALIGN_GET(flags); |
111 | 117M | size_t header_size = get_header_size(flags); |
112 | 117M | size_info *si; |
113 | | |
114 | 117M | if (MALLOCX_NEEDS_ALIGN(flags)) { |
115 | 33.3k | size_t bytes = ISC_CHECKED_ADD( |
116 | 33.3k | get_aligned_size(alignment, size), header_size); |
117 | 33.3k | si = aligned_alloc(alignment, bytes); |
118 | 117M | } else { |
119 | 117M | si = malloc(ISC_CHECKED_ADD(size, header_size)); |
120 | 117M | } |
121 | 117M | if (si == NULL) { |
122 | 0 | return NULL; |
123 | 0 | } |
124 | 117M | si->size = size; |
125 | 117M | si->flags = flags; |
126 | | |
127 | 117M | void *ptr = (uint8_t *)si + header_size; |
128 | 117M | if (MALLOCX_ZERO_GET(flags)) { |
129 | 2.89M | memset(ptr, 0, size); |
130 | 2.89M | } |
131 | | |
132 | 117M | return ptr; |
133 | 117M | } |
134 | | |
135 | | static inline void |
136 | 117M | sdallocx(void *ptr, size_t size, int flags) { |
137 | 117M | size_info *si = get_size_info(ptr, flags); |
138 | 117M | size_t alignment = MALLOCX_ALIGN_GET(flags); |
139 | 117M | size_t header_size = get_header_size(flags); |
140 | | |
141 | 117M | INSIST((flags & MALLOCX_LG_ALIGN_MASK) == |
142 | 117M | (si->flags & MALLOCX_LG_ALIGN_MASK)); |
143 | | |
144 | 117M | if (MALLOCX_NEEDS_ALIGN(flags)) { |
145 | 33.2k | size_t bytes = ISC_CHECKED_ADD( |
146 | 33.2k | get_aligned_size(alignment, size), header_size); |
147 | 33.2k | free_aligned_sized(si, alignment, bytes); |
148 | 117M | } else { |
149 | 117M | free_sized(si, ISC_CHECKED_ADD(si->size, header_size)); |
150 | 117M | } |
151 | 117M | } |
152 | | |
153 | | static inline size_t |
154 | 18.1M | sallocx(void *ptr, int flags) { |
155 | 18.1M | size_info *si = get_size_info(ptr, flags); |
156 | | |
157 | 18.1M | return si[0].size; |
158 | 18.1M | } |
159 | | |
160 | | static inline void * |
161 | 4.52k | rallocx(void *ptr, size_t size, int flags) { |
162 | 4.52k | size_t header_size = get_header_size(flags); |
163 | 4.52k | size_info *si = get_size_info(ptr, flags); |
164 | 4.52k | size_t old_size = si->size; |
165 | | |
166 | 4.52k | INSIST((flags & MALLOCX_LG_ALIGN_MASK) == |
167 | 4.52k | (si->flags & MALLOCX_LG_ALIGN_MASK)); |
168 | | |
169 | 4.52k | if (MALLOCX_NEEDS_ALIGN(flags)) { |
170 | | /* |
171 | | * realloc() cannot preserve the requested alignment; |
172 | | * move the data to a fresh aligned allocation. |
173 | | */ |
174 | 0 | void *new_ptr = mallocx(size, flags & ~MALLOCX_ZERO); |
175 | 0 | if (new_ptr == NULL) { |
176 | 0 | return NULL; |
177 | 0 | } |
178 | 0 | memmove(new_ptr, ptr, ISC_MIN(old_size, size)); |
179 | 0 | if (MALLOCX_ZERO_GET(flags) && size > old_size) { |
180 | 0 | memset((uint8_t *)new_ptr + old_size, 0, |
181 | 0 | size - old_size); |
182 | 0 | } |
183 | 0 | sdallocx(ptr, old_size, flags); |
184 | |
|
185 | 0 | si = get_size_info(new_ptr, flags); |
186 | 0 | si->flags = flags; |
187 | |
|
188 | 0 | return new_ptr; |
189 | 0 | } |
190 | | |
191 | 4.52k | si = realloc(si, ISC_CHECKED_ADD(size, header_size)); |
192 | 4.52k | if (si == NULL) { |
193 | 0 | return NULL; |
194 | 0 | } |
195 | 4.52k | si->size = size; |
196 | 4.52k | si->flags = flags; |
197 | | |
198 | 4.52k | ptr = (uint8_t *)si + header_size; |
199 | 4.52k | if (MALLOCX_ZERO_GET(flags) && size > old_size) { |
200 | 0 | memset((uint8_t *)ptr + old_size, 0, size - old_size); |
201 | 0 | } |
202 | | |
203 | 4.52k | return ptr; |
204 | 4.52k | } |
205 | | |
206 | | #endif /* !defined(HAVE_JEMALLOC) */ |