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