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