Line data Source code
1 : /**
2 : * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 : * SPDX-License-Identifier: Apache-2.0.
4 : */
5 :
6 : #include <aws/common/assert.h>
7 : #include <aws/common/common.h>
8 : #include <aws/common/logging.h>
9 : #include <aws/common/math.h>
10 :
11 : #include <stdarg.h>
12 : #include <stdlib.h>
13 :
14 : #ifdef _WIN32
15 : # include <Windows.h>
16 : #endif
17 :
18 : #ifdef __MACH__
19 : # include <CoreFoundation/CoreFoundation.h>
20 : #endif
21 :
22 : /* turn off unused named parameter warning on msvc.*/
23 : #ifdef _MSC_VER
24 : # pragma warning(push)
25 : # pragma warning(disable : 4100)
26 : #endif
27 :
28 : #ifndef PAGE_SIZE
29 0 : # define PAGE_SIZE (4 * 1024)
30 : #endif
31 :
32 0 : bool aws_allocator_is_valid(const struct aws_allocator *alloc) {
33 0 : /* An allocator must define mem_acquire and mem_release. All other fields are optional */
34 0 : return alloc && AWS_OBJECT_PTR_IS_READABLE(alloc) && alloc->mem_acquire && alloc->mem_release;
35 0 : }
36 :
37 0 : static void *s_default_malloc(struct aws_allocator *allocator, size_t size) {
38 0 : (void)allocator;
39 0 : /* larger allocations should be aligned so that AVX and friends can avoid
40 0 : * the extra preable during unaligned versions of memcpy/memset on big buffers
41 0 : * This will also accelerate hardware CRC and SHA on ARM chips
42 0 : *
43 0 : * 64 byte alignment for > page allocations on 64 bit systems
44 0 : * 32 byte alignment for > page allocations on 32 bit systems
45 0 : * 16 byte alignment for <= page allocations on 64 bit systems
46 0 : * 8 byte alignment for <= page allocations on 32 bit systems
47 0 : *
48 0 : * We use PAGE_SIZE as the boundary because we are not aware of any allocations of
49 0 : * this size or greater that are not data buffers
50 0 : */
51 0 : const size_t alignment = sizeof(void *) * (size > PAGE_SIZE ? 8 : 2);
52 0 : #if !defined(_WIN32)
53 0 : void *result = NULL;
54 0 : return (posix_memalign(&result, alignment, size)) ? NULL : result;
55 : #else
56 : return _aligned_malloc(size, alignment);
57 : #endif
58 : }
59 :
60 0 : static void s_default_free(struct aws_allocator *allocator, void *ptr) {
61 0 : (void)allocator;
62 0 : #if !defined(_WIN32)
63 0 : free(ptr);
64 : #else
65 : _aligned_free(ptr);
66 : #endif
67 : }
68 :
69 0 : static void *s_default_realloc(struct aws_allocator *allocator, void *ptr, size_t oldsize, size_t newsize) {
70 0 : (void)allocator;
71 0 : (void)oldsize;
72 0 : #if !defined(_WIN32)
73 0 : if (newsize == 0 || !ptr) {
74 0 : free(ptr);
75 0 : return NULL;
76 0 : }
77 0 :
78 0 : if (newsize <= oldsize) {
79 0 : return ptr;
80 0 : }
81 0 :
82 0 : /* newsize is > oldsize, need more memory */
83 0 : void *new_mem = s_default_malloc(allocator, newsize);
84 0 : if (new_mem) {
85 0 : memcpy(new_mem, ptr, oldsize);
86 0 : s_default_free(allocator, ptr);
87 0 : }
88 0 : return new_mem;
89 : #else
90 : const size_t alignment = sizeof(void *) * (newsize > PAGE_SIZE ? 8 : 2);
91 : return _aligned_realloc(ptr, newsize, alignment);
92 : #endif
93 : }
94 :
95 0 : static void *s_default_calloc(struct aws_allocator *allocator, size_t num, size_t size) {
96 0 : void *mem = s_default_malloc(allocator, num * size);
97 0 : if (mem) {
98 0 : memset(mem, 0, num * size);
99 0 : }
100 0 : return mem;
101 0 : }
102 :
103 : static struct aws_allocator default_allocator = {
104 : .mem_acquire = s_default_malloc,
105 : .mem_release = s_default_free,
106 : .mem_realloc = s_default_realloc,
107 : .mem_calloc = s_default_calloc,
108 : };
109 :
110 0 : struct aws_allocator *aws_default_allocator(void) {
111 0 : return &default_allocator;
112 0 : }
113 :
114 616115 : void *aws_mem_acquire(struct aws_allocator *allocator, size_t size) {
115 616115 : AWS_FATAL_PRECONDITION(allocator != NULL);
116 616115 : AWS_FATAL_PRECONDITION(allocator->mem_acquire != NULL);
117 616115 : /* Protect against https://wiki.sei.cmu.edu/confluence/display/c/MEM04-C.+Beware+of+zero-length+allocations */
118 616115 : AWS_FATAL_PRECONDITION(size != 0);
119 616115 :
120 616115 : void *mem = allocator->mem_acquire(allocator, size);
121 616115 : if (!mem) {
122 0 : aws_raise_error(AWS_ERROR_OOM);
123 0 : }
124 616115 : return mem;
125 616115 : }
126 :
127 148439 : void *aws_mem_calloc(struct aws_allocator *allocator, size_t num, size_t size) {
128 148439 : AWS_FATAL_PRECONDITION(allocator != NULL);
129 148439 : AWS_FATAL_PRECONDITION(allocator->mem_calloc || allocator->mem_acquire);
130 148439 : /* Protect against https://wiki.sei.cmu.edu/confluence/display/c/MEM04-C.+Beware+of+zero-length+allocations */
131 148439 : AWS_FATAL_PRECONDITION(num != 0 && size != 0);
132 148439 :
133 148439 : /* Defensive check: never use calloc with size * num that would overflow
134 148439 : * https://wiki.sei.cmu.edu/confluence/display/c/MEM07-C.+Ensure+that+the+arguments+to+calloc%28%29%2C+when+multiplied%2C+do+not+wrap
135 148439 : */
136 148439 : size_t required_bytes;
137 148439 : if (aws_mul_size_checked(num, size, &required_bytes)) {
138 0 : return NULL;
139 0 : }
140 148439 :
141 148439 : /* If there is a defined calloc, use it */
142 148439 : if (allocator->mem_calloc) {
143 0 : void *mem = allocator->mem_calloc(allocator, num, size);
144 0 : if (!mem) {
145 0 : aws_raise_error(AWS_ERROR_OOM);
146 0 : }
147 0 : return mem;
148 0 : }
149 148439 :
150 148439 : /* Otherwise, emulate calloc */
151 148439 : void *mem = allocator->mem_acquire(allocator, required_bytes);
152 148439 : if (!mem) {
153 0 : aws_raise_error(AWS_ERROR_OOM);
154 0 : return NULL;
155 0 : }
156 148439 : memset(mem, 0, required_bytes);
157 148439 : AWS_POSTCONDITION(mem != NULL);
158 148439 : return mem;
159 148439 : }
160 :
161 0 : #define AWS_ALIGN_ROUND_UP(value, alignment) (((value) + ((alignment)-1)) & ~((alignment)-1))
162 :
163 0 : void *aws_mem_acquire_many(struct aws_allocator *allocator, size_t count, ...) {
164 0 :
165 0 : enum { S_ALIGNMENT = sizeof(intmax_t) };
166 0 :
167 0 : va_list args_size;
168 0 : va_start(args_size, count);
169 0 : va_list args_allocs;
170 0 : va_copy(args_allocs, args_size);
171 0 :
172 0 : size_t total_size = 0;
173 0 : for (size_t i = 0; i < count; ++i) {
174 0 :
175 0 : /* Ignore the pointer argument for now */
176 0 : va_arg(args_size, void **);
177 0 :
178 0 : size_t alloc_size = va_arg(args_size, size_t);
179 0 : total_size += AWS_ALIGN_ROUND_UP(alloc_size, S_ALIGNMENT);
180 0 : }
181 0 : va_end(args_size);
182 0 :
183 0 : void *allocation = NULL;
184 0 :
185 0 : if (total_size > 0) {
186 0 :
187 0 : allocation = aws_mem_acquire(allocator, total_size);
188 0 : if (!allocation) {
189 0 : aws_raise_error(AWS_ERROR_OOM);
190 0 : goto cleanup;
191 0 : }
192 0 :
193 0 : uint8_t *current_ptr = allocation;
194 0 :
195 0 : for (size_t i = 0; i < count; ++i) {
196 0 :
197 0 : void **out_ptr = va_arg(args_allocs, void **);
198 0 :
199 0 : size_t alloc_size = va_arg(args_allocs, size_t);
200 0 : alloc_size = AWS_ALIGN_ROUND_UP(alloc_size, S_ALIGNMENT);
201 0 :
202 0 : *out_ptr = current_ptr;
203 0 : current_ptr += alloc_size;
204 0 : }
205 0 : }
206 0 :
207 0 : cleanup:
208 0 : va_end(args_allocs);
209 0 : return allocation;
210 0 : }
211 :
212 : #undef AWS_ALIGN_ROUND_UP
213 :
214 687040 : void aws_mem_release(struct aws_allocator *allocator, void *ptr) {
215 687040 : AWS_FATAL_PRECONDITION(allocator != NULL);
216 687040 : AWS_FATAL_PRECONDITION(allocator->mem_release != NULL);
217 687040 :
218 687040 : if (ptr != NULL) {
219 687040 : allocator->mem_release(allocator, ptr);
220 687040 : }
221 687040 : }
222 :
223 123436 : int aws_mem_realloc(struct aws_allocator *allocator, void **ptr, size_t oldsize, size_t newsize) {
224 123436 : AWS_FATAL_PRECONDITION(allocator != NULL);
225 123436 : AWS_FATAL_PRECONDITION(allocator->mem_realloc || allocator->mem_acquire);
226 123436 : AWS_FATAL_PRECONDITION(allocator->mem_release);
227 123436 :
228 123436 : /* Protect against https://wiki.sei.cmu.edu/confluence/display/c/MEM04-C.+Beware+of+zero-length+allocations */
229 123436 : if (newsize == 0) {
230 0 : aws_mem_release(allocator, *ptr);
231 0 : *ptr = NULL;
232 0 : return AWS_OP_SUCCESS;
233 0 : }
234 123436 :
235 123436 : if (allocator->mem_realloc) {
236 0 : void *newptr = allocator->mem_realloc(allocator, *ptr, oldsize, newsize);
237 0 : if (!newptr) {
238 0 : return aws_raise_error(AWS_ERROR_OOM);
239 0 : }
240 0 : *ptr = newptr;
241 0 : return AWS_OP_SUCCESS;
242 0 : }
243 123436 :
244 123436 : /* Since the allocator doesn't support realloc, we'll need to emulate it (inefficiently). */
245 123436 : if (oldsize >= newsize) {
246 0 : return AWS_OP_SUCCESS;
247 0 : }
248 123436 :
249 123436 : void *newptr = allocator->mem_acquire(allocator, newsize);
250 123436 : if (!newptr) {
251 0 : return aws_raise_error(AWS_ERROR_OOM);
252 0 : }
253 123436 :
254 123436 : memcpy(newptr, *ptr, oldsize);
255 123436 : memset((uint8_t *)newptr + oldsize, 0, newsize - oldsize);
256 123436 :
257 123436 : aws_mem_release(allocator, *ptr);
258 123436 :
259 123436 : *ptr = newptr;
260 123436 :
261 123436 : return AWS_OP_SUCCESS;
262 123436 : }
263 :
264 : /* Wraps a CFAllocator around aws_allocator. For Mac only. */
265 : #ifdef __MACH__
266 :
267 : static CFStringRef s_cf_allocator_description = CFSTR("CFAllocator wrapping aws_allocator.");
268 :
269 : /* note we don't have a standard specification stating sizeof(size_t) == sizeof(void *) so we have some extra casts */
270 : static void *s_cf_allocator_allocate(CFIndex alloc_size, CFOptionFlags hint, void *info) {
271 : (void)hint;
272 :
273 : struct aws_allocator *allocator = info;
274 :
275 : void *mem = aws_mem_acquire(allocator, (size_t)alloc_size + sizeof(size_t));
276 :
277 : if (!mem) {
278 : return NULL;
279 : }
280 :
281 : size_t allocation_size = (size_t)alloc_size + sizeof(size_t);
282 : memcpy(mem, &allocation_size, sizeof(size_t));
283 : return (void *)((uint8_t *)mem + sizeof(size_t));
284 : }
285 :
286 : static void s_cf_allocator_deallocate(void *ptr, void *info) {
287 : struct aws_allocator *allocator = info;
288 :
289 : void *original_allocation = (uint8_t *)ptr - sizeof(size_t);
290 :
291 : aws_mem_release(allocator, original_allocation);
292 : }
293 :
294 : static void *s_cf_allocator_reallocate(void *ptr, CFIndex new_size, CFOptionFlags hint, void *info) {
295 : (void)hint;
296 :
297 : struct aws_allocator *allocator = info;
298 : AWS_ASSERT(allocator->mem_realloc);
299 :
300 : void *original_allocation = (uint8_t *)ptr - sizeof(size_t);
301 : size_t original_size = 0;
302 : memcpy(&original_size, original_allocation, sizeof(size_t));
303 :
304 : if (aws_mem_realloc(allocator, &original_allocation, original_size, (size_t)new_size)) {
305 : return NULL;
306 : }
307 :
308 : size_t new_allocation_size = (size_t)new_size;
309 : memcpy(original_allocation, &new_allocation_size, sizeof(size_t));
310 :
311 : return (void *)((uint8_t *)original_allocation + sizeof(size_t));
312 : }
313 :
314 : static CFStringRef s_cf_allocator_copy_description(const void *info) {
315 : (void)info;
316 :
317 : return s_cf_allocator_description;
318 : }
319 :
320 : static CFIndex s_cf_allocator_preferred_size(CFIndex size, CFOptionFlags hint, void *info) {
321 : (void)hint;
322 : (void)info;
323 :
324 : return size + sizeof(size_t);
325 : }
326 :
327 : CFAllocatorRef aws_wrapped_cf_allocator_new(struct aws_allocator *allocator) {
328 : CFAllocatorRef cf_allocator = NULL;
329 :
330 : CFAllocatorReallocateCallBack reallocate_callback = NULL;
331 :
332 : if (allocator->mem_realloc) {
333 : reallocate_callback = s_cf_allocator_reallocate;
334 : }
335 :
336 : CFAllocatorContext context = {
337 : .allocate = s_cf_allocator_allocate,
338 : .copyDescription = s_cf_allocator_copy_description,
339 : .deallocate = s_cf_allocator_deallocate,
340 : .reallocate = reallocate_callback,
341 : .info = allocator,
342 : .preferredSize = s_cf_allocator_preferred_size,
343 : .release = NULL,
344 : .retain = NULL,
345 : .version = 0,
346 : };
347 :
348 : cf_allocator = CFAllocatorCreate(NULL, &context);
349 :
350 : if (!cf_allocator) {
351 : aws_raise_error(AWS_ERROR_OOM);
352 : }
353 :
354 : return cf_allocator;
355 : }
356 :
357 : void aws_wrapped_cf_allocator_destroy(CFAllocatorRef allocator) {
358 : CFRelease(allocator);
359 : }
360 :
361 : #endif /*__MACH__ */
|