xref: /illumos-gate/usr/src/lib/libumem/common/malloc.c (revision ca9327a6de44d69ddab3668cc1e143ce781387a3)
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 
52 typedef struct malloc_data {
53 	uint32_t malloc_size;
54 	uint32_t malloc_stat; /* = UMEM_MALLOC_ENCODE(state, malloc_size) */
55 } malloc_data_t;
56 
57 void *
58 malloc(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 
119 void *
120 calloc(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 
145 void *
146 memalign(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 
225 void *
226 valloc(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 
243 static int
244 process_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 
356 process_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 
365 process_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 
375 void
376 free(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 
387 void *
388 realloc(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