root/trunk/malloc.c

Revision 24, 8.5 kB (checked in by richdawe, 8 years ago)

Add --enable-malloc-replacement option, for using libumem as a malloc replacement

  • Property svn:eol-style set to native
Line 
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26
27 #pragma ident   "@(#)malloc.c   1.5     05/06/08 SMI"
28
29 #include "config.h"
30 #include <unistd.h>
31
32 #include <errno.h>
33
34 #include <string.h>
35
36 #include <sys/sysmacros.h>
37
38 #include "umem_base.h"
39
40 #include "misc.h"
41
42 /*
43  * malloc_data_t is an 8-byte structure which is located "before" the pointer
44  * returned from {m,c,re}alloc and memalign.  The first four bytes give
45  * information about the buffer, and the second four bytes are a status byte.
46  *
47  * See umem_impl.h for the various magic numbers used, and the size
48  * encode/decode macros.
49  *
50  * The 'size' of the buffer includes the tags.  That is, we encode the
51  * argument to umem_alloc(), not the argument to malloc().
52  */
53
54 typedef struct malloc_data {
55         uint32_t malloc_size;
56         uint32_t malloc_stat; /* = UMEM_MALLOC_ENCODE(state, malloc_size) */
57 } malloc_data_t;
58
59 void *
60 malloc(size_t size_arg)
61 {
62 #ifdef _LP64
63         uint32_t high_size = 0;
64 #endif
65         size_t size;
66
67         malloc_data_t *ret;
68         size = size_arg + sizeof (malloc_data_t);
69
70 #ifdef _LP64
71         if (size > UMEM_SECOND_ALIGN) {
72                 size += sizeof (malloc_data_t);
73                 high_size = (size >> 32);
74         }
75 #endif
76         if (size < size_arg) {
77                 errno = ENOMEM;                 /* overflow */
78                 return (NULL);
79         }
80         ret = (malloc_data_t *)_umem_alloc(size, UMEM_DEFAULT);
81         if (ret == NULL) {
82                 if (size <= UMEM_MAXBUF)
83                         errno = EAGAIN;
84                 else
85                         errno = ENOMEM;
86                 return (NULL);
87 #ifdef _LP64
88         } else if (high_size > 0) {
89                 uint32_t low_size = (uint32_t)size;
90
91                 /*
92                  * uses different magic numbers to make it harder to
93                  * undetectably corrupt
94                  */
95                 ret->malloc_size = high_size;
96                 ret->malloc_stat = UMEM_MALLOC_ENCODE(MALLOC_MAGIC, high_size);
97                 ret++;
98
99                 ret->malloc_size = low_size;
100                 ret->malloc_stat = UMEM_MALLOC_ENCODE(MALLOC_OVERSIZE_MAGIC,
101                     low_size);
102                 ret++;
103         } else if (size > UMEM_SECOND_ALIGN) {
104                 uint32_t low_size = (uint32_t)size;
105
106                 ret++; /* leave the first 8 bytes alone */
107
108                 ret->malloc_size = low_size;
109                 ret->malloc_stat = UMEM_MALLOC_ENCODE(MALLOC_SECOND_MAGIC,
110                     low_size);
111                 ret++;
112 #endif
113         } else {
114                 ret->malloc_size = size;
115                 ret->malloc_stat = UMEM_MALLOC_ENCODE(MALLOC_MAGIC, size);
116                 ret++;
117         }
118         return ((void *)ret);
119 }
120
121 void *
122 calloc(size_t nelem, size_t elsize)
123 {
124         size_t size = nelem * elsize;
125         void *retval;
126
127         if (nelem > 0 && elsize > 0 && size/nelem != elsize) {
128                 errno = ENOMEM;                         /* overflow */
129                 return (NULL);
130         }
131
132         retval = malloc(size);
133         if (retval == NULL)
134                 return (NULL);
135
136         (void) memset(retval, 0, size);
137         return (retval);
138 }
139
140 /*
141  * memalign uses vmem_xalloc to do its work.
142  *
143  * in 64-bit, the memaligned buffer always has two tags.  This simplifies the
144  * code.
145  */
146
147 void *
148 memalign(size_t align, size_t size_arg)
149 {
150         size_t size;
151         uintptr_t phase;
152
153         void *buf;
154         malloc_data_t *ret;
155
156         size_t overhead;
157
158         if (size_arg == 0 || align == 0 || (align & (align - 1)) != 0) {
159                 errno = EINVAL;
160                 return (NULL);
161         }
162
163         /*
164          * if malloc provides the required alignment, use it.
165          */
166         if (align <= UMEM_ALIGN ||
167             (align <= UMEM_SECOND_ALIGN && size_arg >= UMEM_SECOND_ALIGN))
168                 return (malloc(size_arg));
169
170 #ifdef _LP64
171         overhead = 2 * sizeof (malloc_data_t);
172 #else
173         overhead = sizeof (malloc_data_t);
174 #endif
175
176         ASSERT(overhead <= align);
177
178         size = size_arg + overhead;
179         phase = align - overhead;
180
181         if (umem_memalign_arena == NULL && umem_init() == 0) {
182                 errno = ENOMEM;
183                 return (NULL);
184         }
185
186         if (size < size_arg) {
187                 errno = ENOMEM;                 /* overflow */
188                 return (NULL);
189         }
190
191         buf = vmem_xalloc(umem_memalign_arena, size, align, phase,
192             0, NULL, NULL, VM_NOSLEEP);
193
194         if (buf == NULL) {
195                 if ((size_arg + align) <= UMEM_MAXBUF)
196                         errno = EAGAIN;
197                 else
198                         errno = ENOMEM;
199
200                 return (NULL);
201         }
202
203         ret = (malloc_data_t *)buf;
204         {
205                 uint32_t low_size = (uint32_t)size;
206
207 #ifdef _LP64
208                 uint32_t high_size = (uint32_t)(size >> 32);
209
210                 ret->malloc_size = high_size;
211                 ret->malloc_stat = UMEM_MALLOC_ENCODE(MEMALIGN_MAGIC,
212                     high_size);
213                 ret++;
214 #endif
215
216                 ret->malloc_size = low_size;
217                 ret->malloc_stat = UMEM_MALLOC_ENCODE(MEMALIGN_MAGIC, low_size);
218                 ret++;
219         }
220
221         ASSERT(P2PHASE((uintptr_t)ret, align) == 0);
222         ASSERT((void *)((uintptr_t)ret - overhead) == buf);
223
224         return ((void *)ret);
225 }
226
227 void *
228 valloc(size_t size)
229 {
230         return (memalign(pagesize, size));
231 }
232
233 /*
234  * process_free:
235  *
236  * Pulls information out of a buffer pointer, and optionally free it.
237  * This is used by free() and realloc() to process buffers.
238  *
239  * On failure, calls umem_err_recoverable() with an appropriate message
240  * On success, returns the data size through *data_size_arg, if (!is_free).
241  *
242  * Preserves errno, since free()'s semantics require it.
243  */
244
245 static int
246 process_free(void *buf_arg,
247     int do_free,                /* free the buffer, or just get its size? */
248     size_t *data_size_arg)      /* output: bytes of data in buf_arg */
249 {
250         malloc_data_t *buf;
251
252         void *base;
253         size_t size;
254         size_t data_size;
255
256         const char *message;
257         int old_errno = errno;
258
259         buf = (malloc_data_t *)buf_arg;
260
261         buf--;
262         size = buf->malloc_size;
263
264         switch (UMEM_MALLOC_DECODE(buf->malloc_stat, size)) {
265
266         case MALLOC_MAGIC:
267                 base = (void *)buf;
268                 data_size = size - sizeof (malloc_data_t);
269
270                 if (do_free)
271                         buf->malloc_stat = UMEM_FREE_PATTERN_32;
272
273                 goto process_malloc;
274
275 #ifdef _LP64
276         case MALLOC_SECOND_MAGIC:
277                 base = (void *)(buf - 1);
278                 data_size = size - 2 * sizeof (malloc_data_t);
279
280                 if (do_free)
281                         buf->malloc_stat = UMEM_FREE_PATTERN_32;
282
283                 goto process_malloc;
284
285         case MALLOC_OVERSIZE_MAGIC: {
286                 size_t high_size;
287
288                 buf--;
289                 high_size = buf->malloc_size;
290
291                 if (UMEM_MALLOC_DECODE(buf->malloc_stat, high_size) !=
292                     MALLOC_MAGIC) {
293                         message = "invalid or corrupted buffer";
294                         break;
295                 }
296
297                 size += high_size << 32;
298
299                 base = (void *)buf;
300                 data_size = size - 2 * sizeof (malloc_data_t);
301
302                 if (do_free) {
303                         buf->malloc_stat = UMEM_FREE_PATTERN_32;
304                         (buf + 1)->malloc_stat = UMEM_FREE_PATTERN_32;
305                 }
306
307                 goto process_malloc;
308         }
309 #endif
310
311         case MEMALIGN_MAGIC: {
312                 size_t overhead = sizeof (malloc_data_t);
313
314 #ifdef _LP64
315                 size_t high_size;
316
317                 overhead += sizeof (malloc_data_t);
318
319                 buf--;
320                 high_size = buf->malloc_size;
321
322                 if (UMEM_MALLOC_DECODE(buf->malloc_stat, high_size) !=
323                     MEMALIGN_MAGIC) {
324                         message = "invalid or corrupted buffer";
325                         break;
326                 }
327                 size += high_size << 32;
328
329                 /*
330                  * destroy the main tag's malloc_stat
331                  */
332                 if (do_free)
333                         (buf + 1)->malloc_stat = UMEM_FREE_PATTERN_32;
334 #endif
335
336                 base = (void *)buf;
337                 data_size = size - overhead;
338
339                 if (do_free)
340                         buf->malloc_stat = UMEM_FREE_PATTERN_32;
341
342                 goto process_memalign;
343         }
344         default:
345                 if (buf->malloc_stat == UMEM_FREE_PATTERN_32)
346                         message = "double-free or invalid buffer";
347                 else
348                         message = "invalid or corrupted buffer";
349                 break;
350         }
351
352         umem_err_recoverable("%s(%p): %s\n",
353             do_free? "free" : "realloc", buf_arg, message);
354
355         errno = old_errno;
356         return (0);
357
358 process_malloc:
359         if (do_free)
360                 _umem_free(base, size);
361         else
362                 *data_size_arg = data_size;
363
364         errno = old_errno;
365         return (1);
366
367 process_memalign:
368         if (do_free)
369                 vmem_xfree(umem_memalign_arena, base, size);
370         else
371                 *data_size_arg = data_size;
372
373         errno = old_errno;
374         return (1);
375 }
376
377 void
378 free(void *buf)
379 {
380         if (buf == NULL)
381                 return;
382
383         /*
384          * Process buf, freeing it if it is not corrupt.
385          */
386         (void) process_free(buf, 1, NULL);
387 }
388
389 void *
390 realloc(void *buf_arg, size_t newsize)
391 {
392         size_t oldsize;
393         void *buf;
394
395         if (buf_arg == NULL)
396                 return (malloc(newsize));
397
398         /*
399          * get the old data size without freeing the buffer
400          */
401         if (process_free(buf_arg, 0, &oldsize) == 0) {
402                 errno = EINVAL;
403                 return (NULL);
404         }
405
406         if (newsize == oldsize)         /* size didn't change */
407                 return (buf_arg);
408
409         buf = malloc(newsize);
410         if (buf == NULL)
411                 return (NULL);
412
413         (void) memcpy(buf, buf_arg, MIN(newsize, oldsize));
414         free(buf_arg);
415         return (buf);
416 }
417
418 void __attribute__((constructor))
419 __malloc_umem_init (void)
420 {
421   umem_startup(NULL, 0, 0, NULL, NULL);
422 }
Note: See TracBrowser for help on using the browser.