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
46typedef 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
60void *
61umem_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
122void *
123calloc(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
148void *
149memalign(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
228void *
229valloc(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
246static int
247process_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
359process_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
368process_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
378void
379umem_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
390void *
391realloc(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