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