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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 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 31 #include <errno.h> 32 33 #include <string.h> 34 35 #include <sys/sysmacros.h> 36 37 #include "umem_base.h" 38 39 #include "misc.h" 40 41 /* 42 * malloc_data_t is an 8-byte structure which is located "before" the pointer 43 * returned from {m,c,re}alloc and memalign. The first four bytes give 44 * information about the buffer, and the second four bytes are a status byte. 45 * 46 * See umem_impl.h for the various magic numbers used, and the size 47 * encode/decode macros. 48 * 49 * The 'size' of the buffer includes the tags. That is, we encode the 50 * argument to umem_alloc(), not the argument to malloc(). 51 */ 52 53 typedef struct malloc_data { 54 uint32_t malloc_size; 55 uint32_t malloc_stat; /* = UMEM_MALLOC_ENCODE(state, malloc_size) */ 56 } malloc_data_t; 57 58 void * 59 malloc(size_t size_arg) 60 { 61 #ifdef _LP64 62 uint32_t high_size = 0; 63 #endif 64 size_t size; 65 66 malloc_data_t *ret; 67 size = size_arg + sizeof (malloc_data_t); 68 69 #ifdef _LP64 70 if (size > UMEM_SECOND_ALIGN) { 71 size += sizeof (malloc_data_t); 72 high_size = (size >> 32); 73 } 74 #endif 75 if (size < size_arg) { 76 errno = ENOMEM; /* overflow */ 77 return (NULL); 78 } 79 ret = (malloc_data_t *)_umem_alloc(size, UMEM_DEFAULT); 80 if (ret == NULL) { 81 if (size <= UMEM_MAXBUF) 82 errno = EAGAIN; 83 else 84 errno = ENOMEM; 85 return (NULL); 86 #ifdef _LP64 87 } else if (high_size > 0) { 88 uint32_t low_size = (uint32_t)size; 89 90 /* 91 * uses different magic numbers to make it harder to 92 * undetectably corrupt 93 */ 94 ret->malloc_size = high_size; 95 ret->malloc_stat = UMEM_MALLOC_ENCODE(MALLOC_MAGIC, high_size); 96 ret++; 97 98 ret->malloc_size = low_size; 99 ret->malloc_stat = UMEM_MALLOC_ENCODE(MALLOC_OVERSIZE_MAGIC, 100 low_size); 101 ret++; 102 } else if (size > UMEM_SECOND_ALIGN) { 103 uint32_t low_size = (uint32_t)size; 104 105 ret++; /* leave the first 8 bytes alone */ 106 107 ret->malloc_size = low_size; 108 ret->malloc_stat = UMEM_MALLOC_ENCODE(MALLOC_SECOND_MAGIC, 109 low_size); 110 ret++; 111 #endif 112 } else { 113 ret->malloc_size = size; 114 ret->malloc_stat = UMEM_MALLOC_ENCODE(MALLOC_MAGIC, size); 115 ret++; 116 } 117 return ((void *)ret); 118 } 119 120 void * 121 calloc(size_t nelem, size_t elsize) 122 { 123 size_t size = nelem * elsize; 124 void *retval; 125 126 if (nelem > 0 && elsize > 0 && size/nelem != elsize) { 127 errno = ENOMEM; /* overflow */ 128 return (NULL); 129 } 130 131 retval = malloc(size); 132 if (retval == NULL) 133 return (NULL); 134 135 (void) memset(retval, 0, size); 136 return (retval); 137 } 138 139 /* 140 * memalign uses vmem_xalloc to do its work. 141 * 142 * in 64-bit, the memaligned buffer always has two tags. This simplifies the 143 * code. 144 */ 145 146 void * 147 memalign(size_t align, size_t size_arg) 148 { 149 size_t size; 150 uintptr_t phase; 151 152 void *buf; 153 malloc_data_t *ret; 154 155 size_t overhead; 156 157 if (size_arg == 0 || align == 0 || (align & (align - 1)) != 0) { 158 errno = EINVAL; 159 return (NULL); 160 } 161 162 /* 163 * if malloc provides the required alignment, use it. 164 */ 165 if (align <= UMEM_ALIGN || 166 (align <= UMEM_SECOND_ALIGN && size_arg >= UMEM_SECOND_ALIGN)) 167 return (malloc(size_arg)); 168 169 #ifdef _LP64 170 overhead = 2 * sizeof (malloc_data_t); 171 #else 172 overhead = sizeof (malloc_data_t); 173 #endif 174 175 ASSERT(overhead <= align); 176 177 size = size_arg + overhead; 178 phase = align - overhead; 179 180 if (umem_memalign_arena == NULL && umem_init() == 0) { 181 errno = ENOMEM; 182 return (NULL); 183 } 184 185 if (size < size_arg) { 186 errno = ENOMEM; /* overflow */ 187 return (NULL); 188 } 189 190 buf = vmem_xalloc(umem_memalign_arena, size, align, phase, 191 0, NULL, NULL, VM_NOSLEEP); 192 193 if (buf == NULL) { 194 if ((size_arg + align) <= UMEM_MAXBUF) 195 errno = EAGAIN; 196 else 197 errno = ENOMEM; 198 199 return (NULL); 200 } 201 202 ret = (malloc_data_t *)buf; 203 { 204 uint32_t low_size = (uint32_t)size; 205 206 #ifdef _LP64 207 uint32_t high_size = (uint32_t)(size >> 32); 208 209 ret->malloc_size = high_size; 210 ret->malloc_stat = UMEM_MALLOC_ENCODE(MEMALIGN_MAGIC, 211 high_size); 212 ret++; 213 #endif 214 215 ret->malloc_size = low_size; 216 ret->malloc_stat = UMEM_MALLOC_ENCODE(MEMALIGN_MAGIC, low_size); 217 ret++; 218 } 219 220 ASSERT(P2PHASE((uintptr_t)ret, align) == 0); 221 ASSERT((void *)((uintptr_t)ret - overhead) == buf); 222 223 return ((void *)ret); 224 } 225 226 void * 227 valloc(size_t size) 228 { 229 return (memalign(pagesize, size)); 230 } 231 232 /* 233 * process_free: 234 * 235 * Pulls information out of a buffer pointer, and optionally free it. 236 * This is used by free() and realloc() to process buffers. 237 * 238 * On failure, calls umem_err_recoverable() with an appropriate message 239 * On success, returns the data size through *data_size_arg, if (!is_free). 240 * 241 * Preserves errno, since free()'s semantics require it. 242 */ 243 244 static int 245 process_free(void *buf_arg, 246 int do_free, /* free the buffer, or just get its size? */ 247 size_t *data_size_arg) /* output: bytes of data in buf_arg */ 248 { 249 malloc_data_t *buf; 250 251 void *base; 252 size_t size; 253 size_t data_size; 254 255 const char *message; 256 int old_errno = errno; 257 258 buf = (malloc_data_t *)buf_arg; 259 260 buf--; 261 size = buf->malloc_size; 262 263 switch (UMEM_MALLOC_DECODE(buf->malloc_stat, size)) { 264 265 case MALLOC_MAGIC: 266 base = (void *)buf; 267 data_size = size - sizeof (malloc_data_t); 268 269 if (do_free) 270 buf->malloc_stat = UMEM_FREE_PATTERN_32; 271 272 goto process_malloc; 273 274 #ifdef _LP64 275 case MALLOC_SECOND_MAGIC: 276 base = (void *)(buf - 1); 277 data_size = size - 2 * sizeof (malloc_data_t); 278 279 if (do_free) 280 buf->malloc_stat = UMEM_FREE_PATTERN_32; 281 282 goto process_malloc; 283 284 case MALLOC_OVERSIZE_MAGIC: { 285 size_t high_size; 286 287 buf--; 288 high_size = buf->malloc_size; 289 290 if (UMEM_MALLOC_DECODE(buf->malloc_stat, high_size) != 291 MALLOC_MAGIC) { 292 message = "invalid or corrupted buffer"; 293 break; 294 } 295 296 size += high_size << 32; 297 298 base = (void *)buf; 299 data_size = size - 2 * sizeof (malloc_data_t); 300 301 if (do_free) { 302 buf->malloc_stat = UMEM_FREE_PATTERN_32; 303 (buf + 1)->malloc_stat = UMEM_FREE_PATTERN_32; 304 } 305 306 goto process_malloc; 307 } 308 #endif 309 310 case MEMALIGN_MAGIC: { 311 size_t overhead = sizeof (malloc_data_t); 312 313 #ifdef _LP64 314 size_t high_size; 315 316 overhead += sizeof (malloc_data_t); 317 318 buf--; 319 high_size = buf->malloc_size; 320 321 if (UMEM_MALLOC_DECODE(buf->malloc_stat, high_size) != 322 MEMALIGN_MAGIC) { 323 message = "invalid or corrupted buffer"; 324 break; 325 } 326 size += high_size << 32; 327 328 /* 329 * destroy the main tag's malloc_stat 330 */ 331 if (do_free) 332 (buf + 1)->malloc_stat = UMEM_FREE_PATTERN_32; 333 #endif 334 335 base = (void *)buf; 336 data_size = size - overhead; 337 338 if (do_free) 339 buf->malloc_stat = UMEM_FREE_PATTERN_32; 340 341 goto process_memalign; 342 } 343 default: 344 if (buf->malloc_stat == UMEM_FREE_PATTERN_32) 345 message = "double-free or invalid buffer"; 346 else 347 message = "invalid or corrupted buffer"; 348 break; 349 } 350 351 umem_err_recoverable("%s(%p): %s\n", 352 do_free? "free" : "realloc", buf_arg, message); 353 354 errno = old_errno; 355 return (0); 356 357 process_malloc: 358 if (do_free) 359 _umem_free(base, size); 360 else 361 *data_size_arg = data_size; 362 363 errno = old_errno; 364 return (1); 365 366 process_memalign: 367 if (do_free) 368 vmem_xfree(umem_memalign_arena, base, size); 369 else 370 *data_size_arg = data_size; 371 372 errno = old_errno; 373 return (1); 374 } 375 376 void 377 free(void *buf) 378 { 379 if (buf == NULL) 380 return; 381 382 /* 383 * Process buf, freeing it if it is not corrupt. 384 */ 385 (void) process_free(buf, 1, NULL); 386 } 387 388 void * 389 realloc(void *buf_arg, size_t newsize) 390 { 391 size_t oldsize; 392 void *buf; 393 394 if (buf_arg == NULL) 395 return (malloc(newsize)); 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