xref: /illumos-gate/usr/src/lib/libumem/common/malloc.c (revision 7257d1b4d25bfac0c802847390e98a464fd787ac)
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 *
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 *
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 *
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 *
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
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
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 *
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