8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / lib / libumem / common / malloc.c
blob3d19e5b320270012bc1c1af9906e80747c12a5f5
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #include <unistd.h>
28 #include <errno.h>
29 #include <string.h>
30 #include <sys/sysmacros.h>
31 #include "umem_base.h"
32 #include "misc.h"
35 * malloc_data_t is an 8-byte structure which is located "before" the pointer
36 * returned from {m,c,re}alloc and memalign. The first four bytes give
37 * information about the buffer, and the second four bytes are a status byte.
39 * See umem_impl.h for the various magic numbers used, and the size
40 * encode/decode macros.
42 * The 'size' of the buffer includes the tags. That is, we encode the
43 * argument to umem_alloc(), not the argument to malloc().
46 typedef struct malloc_data {
47 uint32_t malloc_size;
48 uint32_t malloc_stat; /* = UMEM_MALLOC_ENCODE(state, malloc_size) */
49 } malloc_data_t;
52 * Because we do not support ptcumem on non-x86 today, we have to create these
53 * weak aliases.
55 #ifndef _x86
56 #pragma weak malloc = umem_malloc
57 #pragma weak free = umem_malloc_free
58 #endif /* !_x86 */
60 void *
61 umem_malloc(size_t size_arg)
63 #ifdef _LP64
64 uint32_t high_size = 0;
65 #endif
66 size_t size;
68 malloc_data_t *ret;
69 size = size_arg + sizeof (malloc_data_t);
71 #ifdef _LP64
72 if (size > UMEM_SECOND_ALIGN) {
73 size += sizeof (malloc_data_t);
74 high_size = (size >> 32);
76 #endif
77 if (size < size_arg) {
78 errno = ENOMEM; /* overflow */
79 return (NULL);
81 ret = (malloc_data_t *)_umem_alloc(size, UMEM_DEFAULT);
82 if (ret == NULL) {
83 if (size <= UMEM_MAXBUF)
84 errno = EAGAIN;
85 else
86 errno = ENOMEM;
87 return (NULL);
88 #ifdef _LP64
89 } else if (high_size > 0) {
90 uint32_t low_size = (uint32_t)size;
93 * uses different magic numbers to make it harder to
94 * undetectably corrupt
96 ret->malloc_size = high_size;
97 ret->malloc_stat = UMEM_MALLOC_ENCODE(MALLOC_MAGIC, high_size);
98 ret++;
100 ret->malloc_size = low_size;
101 ret->malloc_stat = UMEM_MALLOC_ENCODE(MALLOC_OVERSIZE_MAGIC,
102 low_size);
103 ret++;
104 } else if (size > UMEM_SECOND_ALIGN) {
105 uint32_t low_size = (uint32_t)size;
107 ret++; /* leave the first 8 bytes alone */
109 ret->malloc_size = low_size;
110 ret->malloc_stat = UMEM_MALLOC_ENCODE(MALLOC_SECOND_MAGIC,
111 low_size);
112 ret++;
113 #endif
114 } else {
115 ret->malloc_size = size;
116 ret->malloc_stat = UMEM_MALLOC_ENCODE(MALLOC_MAGIC, size);
117 ret++;
119 return ((void *)ret);
122 void *
123 calloc(size_t nelem, size_t elsize)
125 size_t size = nelem * elsize;
126 void *retval;
128 if (nelem > 0 && elsize > 0 && size/nelem != elsize) {
129 errno = ENOMEM; /* overflow */
130 return (NULL);
133 retval = malloc(size);
134 if (retval == NULL)
135 return (NULL);
137 (void) memset(retval, 0, size);
138 return (retval);
142 * memalign uses vmem_xalloc to do its work.
144 * in 64-bit, the memaligned buffer always has two tags. This simplifies the
145 * code.
148 void *
149 memalign(size_t align, size_t size_arg)
151 size_t size;
152 uintptr_t phase;
154 void *buf;
155 malloc_data_t *ret;
157 size_t overhead;
159 if (size_arg == 0 || align == 0 || (align & (align - 1)) != 0) {
160 errno = EINVAL;
161 return (NULL);
165 * if malloc provides the required alignment, use it.
167 if (align <= UMEM_ALIGN ||
168 (align <= UMEM_SECOND_ALIGN && size_arg >= UMEM_SECOND_ALIGN))
169 return (malloc(size_arg));
171 #ifdef _LP64
172 overhead = 2 * sizeof (malloc_data_t);
173 #else
174 overhead = sizeof (malloc_data_t);
175 #endif
177 ASSERT(overhead <= align);
179 size = size_arg + overhead;
180 phase = align - overhead;
182 if (umem_memalign_arena == NULL && umem_init() == 0) {
183 errno = ENOMEM;
184 return (NULL);
187 if (size < size_arg) {
188 errno = ENOMEM; /* overflow */
189 return (NULL);
192 buf = vmem_xalloc(umem_memalign_arena, size, align, phase,
193 0, NULL, NULL, VM_NOSLEEP);
195 if (buf == NULL) {
196 if ((size_arg + align) <= UMEM_MAXBUF)
197 errno = EAGAIN;
198 else
199 errno = ENOMEM;
201 return (NULL);
204 ret = (malloc_data_t *)buf;
206 uint32_t low_size = (uint32_t)size;
208 #ifdef _LP64
209 uint32_t high_size = (uint32_t)(size >> 32);
211 ret->malloc_size = high_size;
212 ret->malloc_stat = UMEM_MALLOC_ENCODE(MEMALIGN_MAGIC,
213 high_size);
214 ret++;
215 #endif
217 ret->malloc_size = low_size;
218 ret->malloc_stat = UMEM_MALLOC_ENCODE(MEMALIGN_MAGIC, low_size);
219 ret++;
222 ASSERT(P2PHASE((uintptr_t)ret, align) == 0);
223 ASSERT((void *)((uintptr_t)ret - overhead) == buf);
225 return ((void *)ret);
228 void *
229 valloc(size_t size)
231 return (memalign(pagesize, size));
235 * process_free:
237 * Pulls information out of a buffer pointer, and optionally free it.
238 * This is used by free() and realloc() to process buffers.
240 * On failure, calls umem_err_recoverable() with an appropriate message
241 * On success, returns the data size through *data_size_arg, if (!is_free).
243 * Preserves errno, since free()'s semantics require it.
246 static int
247 process_free(void *buf_arg,
248 int do_free, /* free the buffer, or just get its size? */
249 size_t *data_size_arg) /* output: bytes of data in buf_arg */
251 malloc_data_t *buf;
253 void *base;
254 size_t size;
255 size_t data_size;
257 const char *message;
258 int old_errno = errno;
260 buf = (malloc_data_t *)buf_arg;
262 buf--;
263 size = buf->malloc_size;
265 switch (UMEM_MALLOC_DECODE(buf->malloc_stat, size)) {
267 case MALLOC_MAGIC:
268 base = (void *)buf;
269 data_size = size - sizeof (malloc_data_t);
271 if (do_free)
272 buf->malloc_stat = UMEM_FREE_PATTERN_32;
274 goto process_malloc;
276 #ifdef _LP64
277 case MALLOC_SECOND_MAGIC:
278 base = (void *)(buf - 1);
279 data_size = size - 2 * sizeof (malloc_data_t);
281 if (do_free)
282 buf->malloc_stat = UMEM_FREE_PATTERN_32;
284 goto process_malloc;
286 case MALLOC_OVERSIZE_MAGIC: {
287 size_t high_size;
289 buf--;
290 high_size = buf->malloc_size;
292 if (UMEM_MALLOC_DECODE(buf->malloc_stat, high_size) !=
293 MALLOC_MAGIC) {
294 message = "invalid or corrupted buffer";
295 break;
298 size += high_size << 32;
300 base = (void *)buf;
301 data_size = size - 2 * sizeof (malloc_data_t);
303 if (do_free) {
304 buf->malloc_stat = UMEM_FREE_PATTERN_32;
305 (buf + 1)->malloc_stat = UMEM_FREE_PATTERN_32;
308 goto process_malloc;
310 #endif
312 case MEMALIGN_MAGIC: {
313 size_t overhead = sizeof (malloc_data_t);
315 #ifdef _LP64
316 size_t high_size;
318 overhead += sizeof (malloc_data_t);
320 buf--;
321 high_size = buf->malloc_size;
323 if (UMEM_MALLOC_DECODE(buf->malloc_stat, high_size) !=
324 MEMALIGN_MAGIC) {
325 message = "invalid or corrupted buffer";
326 break;
328 size += high_size << 32;
331 * destroy the main tag's malloc_stat
333 if (do_free)
334 (buf + 1)->malloc_stat = UMEM_FREE_PATTERN_32;
335 #endif
337 base = (void *)buf;
338 data_size = size - overhead;
340 if (do_free)
341 buf->malloc_stat = UMEM_FREE_PATTERN_32;
343 goto process_memalign;
345 default:
346 if (buf->malloc_stat == UMEM_FREE_PATTERN_32)
347 message = "double-free or invalid buffer";
348 else
349 message = "invalid or corrupted buffer";
350 break;
353 umem_err_recoverable("%s(%p): %s\n",
354 do_free? "free" : "realloc", buf_arg, message);
356 errno = old_errno;
357 return (0);
359 process_malloc:
360 if (do_free)
361 _umem_free(base, size);
362 else
363 *data_size_arg = data_size;
365 errno = old_errno;
366 return (1);
368 process_memalign:
369 if (do_free)
370 vmem_xfree(umem_memalign_arena, base, size);
371 else
372 *data_size_arg = data_size;
374 errno = old_errno;
375 return (1);
378 void
379 umem_malloc_free(void *buf)
381 if (buf == NULL)
382 return;
385 * Process buf, freeing it if it is not corrupt.
387 (void) process_free(buf, 1, NULL);
390 void *
391 realloc(void *buf_arg, size_t newsize)
393 size_t oldsize;
394 void *buf;
396 if (buf_arg == NULL)
397 return (malloc(newsize));
399 if (newsize == 0) {
400 free(buf_arg);
401 return (NULL);
405 * get the old data size without freeing the buffer
407 if (process_free(buf_arg, 0, &oldsize) == 0) {
408 errno = EINVAL;
409 return (NULL);
412 if (newsize == oldsize) /* size didn't change */
413 return (buf_arg);
415 buf = malloc(newsize);
416 if (buf == NULL)
417 return (NULL);
419 (void) memcpy(buf, buf_arg, MIN(newsize, oldsize));
420 free(buf_arg);
421 return (buf);