root/trunk/malloc.c

Revision 27, 8.5 kB (checked in by dhodell, 8 years ago)

Insert bits to allow successful compilation (and apparent success in
running) on FreeBSD.

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