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