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 * 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 * 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 * 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 * 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 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 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 * 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