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