1 /* 2 * Buffer-based memory allocator 3 * 4 * Copyright The Mbed TLS Contributors 5 * SPDX-License-Identifier: Apache-2.0 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); you may 8 * not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 15 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 */ 19 20 #include "common.h" 21 22 #if defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C) 23 #include "mbedtls/memory_buffer_alloc.h" 24 25 /* No need for the header guard as MBEDTLS_MEMORY_BUFFER_ALLOC_C 26 is dependent upon MBEDTLS_PLATFORM_C */ 27 #include "mbedtls/platform.h" 28 #include "mbedtls/platform_util.h" 29 30 #include <string.h> 31 32 #if defined(MBEDTLS_MEMORY_BACKTRACE) 33 #include <execinfo.h> 34 #endif 35 36 #if defined(MBEDTLS_THREADING_C) 37 #include "mbedtls/threading.h" 38 #endif 39 40 #define MAGIC1 0xFF00AA55 41 #define MAGIC2 0xEE119966 42 #define MAX_BT 20 43 44 typedef struct _memory_header memory_header; 45 struct _memory_header { 46 size_t magic1; 47 size_t size; 48 size_t alloc; 49 memory_header *prev; 50 memory_header *next; 51 memory_header *prev_free; 52 memory_header *next_free; 53 #if defined(MBEDTLS_MEMORY_BACKTRACE) 54 char **trace; 55 size_t trace_count; 56 #endif 57 size_t magic2; 58 }; 59 60 typedef struct { 61 unsigned char *buf; 62 size_t len; 63 memory_header *first; 64 memory_header *first_free; 65 int verify; 66 #if defined(MBEDTLS_MEMORY_DEBUG) 67 size_t alloc_count; 68 size_t free_count; 69 size_t total_used; 70 size_t maximum_used; 71 size_t header_count; 72 size_t maximum_header_count; 73 #endif 74 #if defined(MBEDTLS_THREADING_C) 75 mbedtls_threading_mutex_t mutex; 76 #endif 77 } 78 buffer_alloc_ctx; 79 80 static buffer_alloc_ctx heap; 81 82 #if defined(MBEDTLS_MEMORY_DEBUG) 83 static void debug_header(memory_header *hdr) 84 { 85 #if defined(MBEDTLS_MEMORY_BACKTRACE) 86 size_t i; 87 #endif 88 89 mbedtls_fprintf(stderr, "HDR: PTR(%10zu), PREV(%10zu), NEXT(%10zu), " 90 "ALLOC(%zu), SIZE(%10zu)\n", 91 (size_t) hdr, (size_t) hdr->prev, (size_t) hdr->next, 92 hdr->alloc, hdr->size); 93 mbedtls_fprintf(stderr, " FPREV(%10zu), FNEXT(%10zu)\n", 94 (size_t) hdr->prev_free, (size_t) hdr->next_free); 95 96 #if defined(MBEDTLS_MEMORY_BACKTRACE) 97 mbedtls_fprintf(stderr, "TRACE: \n"); 98 for (i = 0; i < hdr->trace_count; i++) { 99 mbedtls_fprintf(stderr, "%s\n", hdr->trace[i]); 100 } 101 mbedtls_fprintf(stderr, "\n"); 102 #endif 103 } 104 105 static void debug_chain(void) 106 { 107 memory_header *cur = heap.first; 108 109 mbedtls_fprintf(stderr, "\nBlock list\n"); 110 while (cur != NULL) { 111 debug_header(cur); 112 cur = cur->next; 113 } 114 115 mbedtls_fprintf(stderr, "Free list\n"); 116 cur = heap.first_free; 117 118 while (cur != NULL) { 119 debug_header(cur); 120 cur = cur->next_free; 121 } 122 } 123 #endif /* MBEDTLS_MEMORY_DEBUG */ 124 125 static int verify_header(memory_header *hdr) 126 { 127 if (hdr->magic1 != MAGIC1) { 128 #if defined(MBEDTLS_MEMORY_DEBUG) 129 mbedtls_fprintf(stderr, "FATAL: MAGIC1 mismatch\n"); 130 #endif 131 return 1; 132 } 133 134 if (hdr->magic2 != MAGIC2) { 135 #if defined(MBEDTLS_MEMORY_DEBUG) 136 mbedtls_fprintf(stderr, "FATAL: MAGIC2 mismatch\n"); 137 #endif 138 return 1; 139 } 140 141 if (hdr->alloc > 1) { 142 #if defined(MBEDTLS_MEMORY_DEBUG) 143 mbedtls_fprintf(stderr, "FATAL: alloc has illegal value\n"); 144 #endif 145 return 1; 146 } 147 148 if (hdr->prev != NULL && hdr->prev == hdr->next) { 149 #if defined(MBEDTLS_MEMORY_DEBUG) 150 mbedtls_fprintf(stderr, "FATAL: prev == next\n"); 151 #endif 152 return 1; 153 } 154 155 if (hdr->prev_free != NULL && hdr->prev_free == hdr->next_free) { 156 #if defined(MBEDTLS_MEMORY_DEBUG) 157 mbedtls_fprintf(stderr, "FATAL: prev_free == next_free\n"); 158 #endif 159 return 1; 160 } 161 162 return 0; 163 } 164 165 static int verify_chain(void) 166 { 167 memory_header *prv = heap.first, *cur; 168 169 if (prv == NULL || verify_header(prv) != 0) { 170 #if defined(MBEDTLS_MEMORY_DEBUG) 171 mbedtls_fprintf(stderr, "FATAL: verification of first header " 172 "failed\n"); 173 #endif 174 return 1; 175 } 176 177 if (heap.first->prev != NULL) { 178 #if defined(MBEDTLS_MEMORY_DEBUG) 179 mbedtls_fprintf(stderr, "FATAL: verification failed: " 180 "first->prev != NULL\n"); 181 #endif 182 return 1; 183 } 184 185 cur = heap.first->next; 186 187 while (cur != NULL) { 188 if (verify_header(cur) != 0) { 189 #if defined(MBEDTLS_MEMORY_DEBUG) 190 mbedtls_fprintf(stderr, "FATAL: verification of header " 191 "failed\n"); 192 #endif 193 return 1; 194 } 195 196 if (cur->prev != prv) { 197 #if defined(MBEDTLS_MEMORY_DEBUG) 198 mbedtls_fprintf(stderr, "FATAL: verification failed: " 199 "cur->prev != prv\n"); 200 #endif 201 return 1; 202 } 203 204 prv = cur; 205 cur = cur->next; 206 } 207 208 return 0; 209 } 210 211 static void *buffer_alloc_calloc(size_t n, size_t size) 212 { 213 memory_header *new, *cur = heap.first_free; 214 unsigned char *p; 215 void *ret; 216 size_t original_len, len; 217 #if defined(MBEDTLS_MEMORY_BACKTRACE) 218 void *trace_buffer[MAX_BT]; 219 size_t trace_cnt; 220 #endif 221 222 if (heap.buf == NULL || heap.first == NULL) { 223 return NULL; 224 } 225 226 original_len = len = n * size; 227 228 if (n == 0 || size == 0 || len / n != size) { 229 return NULL; 230 } else if (len > (size_t) -MBEDTLS_MEMORY_ALIGN_MULTIPLE) { 231 return NULL; 232 } 233 234 if (len % MBEDTLS_MEMORY_ALIGN_MULTIPLE) { 235 len -= len % MBEDTLS_MEMORY_ALIGN_MULTIPLE; 236 len += MBEDTLS_MEMORY_ALIGN_MULTIPLE; 237 } 238 239 // Find block that fits 240 // 241 while (cur != NULL) { 242 if (cur->size >= len) { 243 break; 244 } 245 246 cur = cur->next_free; 247 } 248 249 if (cur == NULL) { 250 return NULL; 251 } 252 253 if (cur->alloc != 0) { 254 #if defined(MBEDTLS_MEMORY_DEBUG) 255 mbedtls_fprintf(stderr, "FATAL: block in free_list but allocated " 256 "data\n"); 257 #endif 258 mbedtls_exit(1); 259 } 260 261 #if defined(MBEDTLS_MEMORY_DEBUG) 262 heap.alloc_count++; 263 #endif 264 265 // Found location, split block if > memory_header + 4 room left 266 // 267 if (cur->size - len < sizeof(memory_header) + 268 MBEDTLS_MEMORY_ALIGN_MULTIPLE) { 269 cur->alloc = 1; 270 271 // Remove from free_list 272 // 273 if (cur->prev_free != NULL) { 274 cur->prev_free->next_free = cur->next_free; 275 } else { 276 heap.first_free = cur->next_free; 277 } 278 279 if (cur->next_free != NULL) { 280 cur->next_free->prev_free = cur->prev_free; 281 } 282 283 cur->prev_free = NULL; 284 cur->next_free = NULL; 285 286 #if defined(MBEDTLS_MEMORY_DEBUG) 287 heap.total_used += cur->size; 288 if (heap.total_used > heap.maximum_used) { 289 heap.maximum_used = heap.total_used; 290 } 291 #endif 292 #if defined(MBEDTLS_MEMORY_BACKTRACE) 293 trace_cnt = backtrace(trace_buffer, MAX_BT); 294 cur->trace = backtrace_symbols(trace_buffer, trace_cnt); 295 cur->trace_count = trace_cnt; 296 #endif 297 298 if ((heap.verify & MBEDTLS_MEMORY_VERIFY_ALLOC) && verify_chain() != 0) { 299 mbedtls_exit(1); 300 } 301 302 ret = (unsigned char *) cur + sizeof(memory_header); 303 memset(ret, 0, original_len); 304 305 return ret; 306 } 307 308 p = ((unsigned char *) cur) + sizeof(memory_header) + len; 309 new = (memory_header *) p; 310 311 new->size = cur->size - len - sizeof(memory_header); 312 new->alloc = 0; 313 new->prev = cur; 314 new->next = cur->next; 315 #if defined(MBEDTLS_MEMORY_BACKTRACE) 316 new->trace = NULL; 317 new->trace_count = 0; 318 #endif 319 new->magic1 = MAGIC1; 320 new->magic2 = MAGIC2; 321 322 if (new->next != NULL) { 323 new->next->prev = new; 324 } 325 326 // Replace cur with new in free_list 327 // 328 new->prev_free = cur->prev_free; 329 new->next_free = cur->next_free; 330 if (new->prev_free != NULL) { 331 new->prev_free->next_free = new; 332 } else { 333 heap.first_free = new; 334 } 335 336 if (new->next_free != NULL) { 337 new->next_free->prev_free = new; 338 } 339 340 cur->alloc = 1; 341 cur->size = len; 342 cur->next = new; 343 cur->prev_free = NULL; 344 cur->next_free = NULL; 345 346 #if defined(MBEDTLS_MEMORY_DEBUG) 347 heap.header_count++; 348 if (heap.header_count > heap.maximum_header_count) { 349 heap.maximum_header_count = heap.header_count; 350 } 351 heap.total_used += cur->size; 352 if (heap.total_used > heap.maximum_used) { 353 heap.maximum_used = heap.total_used; 354 } 355 #endif 356 #if defined(MBEDTLS_MEMORY_BACKTRACE) 357 trace_cnt = backtrace(trace_buffer, MAX_BT); 358 cur->trace = backtrace_symbols(trace_buffer, trace_cnt); 359 cur->trace_count = trace_cnt; 360 #endif 361 362 if ((heap.verify & MBEDTLS_MEMORY_VERIFY_ALLOC) && verify_chain() != 0) { 363 mbedtls_exit(1); 364 } 365 366 ret = (unsigned char *) cur + sizeof(memory_header); 367 memset(ret, 0, original_len); 368 369 return ret; 370 } 371 372 static void buffer_alloc_free(void *ptr) 373 { 374 memory_header *hdr, *old = NULL; 375 unsigned char *p = (unsigned char *) ptr; 376 377 if (ptr == NULL || heap.buf == NULL || heap.first == NULL) { 378 return; 379 } 380 381 if (p < heap.buf || p >= heap.buf + heap.len) { 382 #if defined(MBEDTLS_MEMORY_DEBUG) 383 mbedtls_fprintf(stderr, "FATAL: mbedtls_free() outside of managed " 384 "space\n"); 385 #endif 386 mbedtls_exit(1); 387 } 388 389 p -= sizeof(memory_header); 390 hdr = (memory_header *) p; 391 392 if (verify_header(hdr) != 0) { 393 mbedtls_exit(1); 394 } 395 396 if (hdr->alloc != 1) { 397 #if defined(MBEDTLS_MEMORY_DEBUG) 398 mbedtls_fprintf(stderr, "FATAL: mbedtls_free() on unallocated " 399 "data\n"); 400 #endif 401 mbedtls_exit(1); 402 } 403 404 hdr->alloc = 0; 405 406 #if defined(MBEDTLS_MEMORY_DEBUG) 407 heap.free_count++; 408 heap.total_used -= hdr->size; 409 #endif 410 411 #if defined(MBEDTLS_MEMORY_BACKTRACE) 412 free(hdr->trace); 413 hdr->trace = NULL; 414 hdr->trace_count = 0; 415 #endif 416 417 // Regroup with block before 418 // 419 if (hdr->prev != NULL && hdr->prev->alloc == 0) { 420 #if defined(MBEDTLS_MEMORY_DEBUG) 421 heap.header_count--; 422 #endif 423 hdr->prev->size += sizeof(memory_header) + hdr->size; 424 hdr->prev->next = hdr->next; 425 old = hdr; 426 hdr = hdr->prev; 427 428 if (hdr->next != NULL) { 429 hdr->next->prev = hdr; 430 } 431 432 memset(old, 0, sizeof(memory_header)); 433 } 434 435 // Regroup with block after 436 // 437 if (hdr->next != NULL && hdr->next->alloc == 0) { 438 #if defined(MBEDTLS_MEMORY_DEBUG) 439 heap.header_count--; 440 #endif 441 hdr->size += sizeof(memory_header) + hdr->next->size; 442 old = hdr->next; 443 hdr->next = hdr->next->next; 444 445 if (hdr->prev_free != NULL || hdr->next_free != NULL) { 446 if (hdr->prev_free != NULL) { 447 hdr->prev_free->next_free = hdr->next_free; 448 } else { 449 heap.first_free = hdr->next_free; 450 } 451 452 if (hdr->next_free != NULL) { 453 hdr->next_free->prev_free = hdr->prev_free; 454 } 455 } 456 457 hdr->prev_free = old->prev_free; 458 hdr->next_free = old->next_free; 459 460 if (hdr->prev_free != NULL) { 461 hdr->prev_free->next_free = hdr; 462 } else { 463 heap.first_free = hdr; 464 } 465 466 if (hdr->next_free != NULL) { 467 hdr->next_free->prev_free = hdr; 468 } 469 470 if (hdr->next != NULL) { 471 hdr->next->prev = hdr; 472 } 473 474 memset(old, 0, sizeof(memory_header)); 475 } 476 477 // Prepend to free_list if we have not merged 478 // (Does not have to stay in same order as prev / next list) 479 // 480 if (old == NULL) { 481 hdr->next_free = heap.first_free; 482 if (heap.first_free != NULL) { 483 heap.first_free->prev_free = hdr; 484 } 485 heap.first_free = hdr; 486 } 487 488 if ((heap.verify & MBEDTLS_MEMORY_VERIFY_FREE) && verify_chain() != 0) { 489 mbedtls_exit(1); 490 } 491 } 492 493 void mbedtls_memory_buffer_set_verify(int verify) 494 { 495 heap.verify = verify; 496 } 497 498 int mbedtls_memory_buffer_alloc_verify(void) 499 { 500 return verify_chain(); 501 } 502 503 #if defined(MBEDTLS_MEMORY_DEBUG) 504 void mbedtls_memory_buffer_alloc_status(void) 505 { 506 mbedtls_fprintf(stderr, 507 "Current use: %zu blocks / %zu bytes, max: %zu blocks / " 508 "%zu bytes (total %zu bytes), alloc / free: %zu / %zu\n", 509 heap.header_count, heap.total_used, 510 heap.maximum_header_count, heap.maximum_used, 511 heap.maximum_header_count * sizeof(memory_header) 512 + heap.maximum_used, 513 heap.alloc_count, heap.free_count); 514 515 if (heap.first->next == NULL) { 516 mbedtls_fprintf(stderr, "All memory de-allocated in stack buffer\n"); 517 } else { 518 mbedtls_fprintf(stderr, "Memory currently allocated:\n"); 519 debug_chain(); 520 } 521 } 522 523 void mbedtls_memory_buffer_alloc_count_get(size_t *alloc_count, size_t *free_count) 524 { 525 *alloc_count = heap.alloc_count; 526 *free_count = heap.free_count; 527 } 528 529 void mbedtls_memory_buffer_alloc_max_get(size_t *max_used, size_t *max_blocks) 530 { 531 *max_used = heap.maximum_used; 532 *max_blocks = heap.maximum_header_count; 533 } 534 535 void mbedtls_memory_buffer_alloc_max_reset(void) 536 { 537 heap.maximum_used = 0; 538 heap.maximum_header_count = 0; 539 } 540 541 void mbedtls_memory_buffer_alloc_cur_get(size_t *cur_used, size_t *cur_blocks) 542 { 543 *cur_used = heap.total_used; 544 *cur_blocks = heap.header_count; 545 } 546 #endif /* MBEDTLS_MEMORY_DEBUG */ 547 548 #if defined(MBEDTLS_THREADING_C) 549 static void *buffer_alloc_calloc_mutexed(size_t n, size_t size) 550 { 551 void *buf; 552 if (mbedtls_mutex_lock(&heap.mutex) != 0) { 553 return NULL; 554 } 555 buf = buffer_alloc_calloc(n, size); 556 if (mbedtls_mutex_unlock(&heap.mutex)) { 557 return NULL; 558 } 559 return buf; 560 } 561 562 static void buffer_alloc_free_mutexed(void *ptr) 563 { 564 /* We have no good option here, but corrupting the heap seems 565 * worse than losing memory. */ 566 if (mbedtls_mutex_lock(&heap.mutex)) { 567 return; 568 } 569 buffer_alloc_free(ptr); 570 (void) mbedtls_mutex_unlock(&heap.mutex); 571 } 572 #endif /* MBEDTLS_THREADING_C */ 573 574 void mbedtls_memory_buffer_alloc_init(unsigned char *buf, size_t len) 575 { 576 memset(&heap, 0, sizeof(buffer_alloc_ctx)); 577 578 #if defined(MBEDTLS_THREADING_C) 579 mbedtls_mutex_init(&heap.mutex); 580 mbedtls_platform_set_calloc_free(buffer_alloc_calloc_mutexed, 581 buffer_alloc_free_mutexed); 582 #else 583 mbedtls_platform_set_calloc_free(buffer_alloc_calloc, buffer_alloc_free); 584 #endif 585 586 if (len < sizeof(memory_header) + MBEDTLS_MEMORY_ALIGN_MULTIPLE) { 587 return; 588 } else if ((size_t) buf % MBEDTLS_MEMORY_ALIGN_MULTIPLE) { 589 /* Adjust len first since buf is used in the computation */ 590 len -= MBEDTLS_MEMORY_ALIGN_MULTIPLE 591 - (size_t) buf % MBEDTLS_MEMORY_ALIGN_MULTIPLE; 592 buf += MBEDTLS_MEMORY_ALIGN_MULTIPLE 593 - (size_t) buf % MBEDTLS_MEMORY_ALIGN_MULTIPLE; 594 } 595 596 memset(buf, 0, len); 597 598 heap.buf = buf; 599 heap.len = len; 600 601 heap.first = (memory_header *) buf; 602 heap.first->size = len - sizeof(memory_header); 603 heap.first->magic1 = MAGIC1; 604 heap.first->magic2 = MAGIC2; 605 heap.first_free = heap.first; 606 } 607 608 void mbedtls_memory_buffer_alloc_free(void) 609 { 610 #if defined(MBEDTLS_THREADING_C) 611 mbedtls_mutex_free(&heap.mutex); 612 #endif 613 mbedtls_platform_zeroize(&heap, sizeof(buffer_alloc_ctx)); 614 } 615 616 #if defined(MBEDTLS_SELF_TEST) 617 static int check_pointer(void *p) 618 { 619 if (p == NULL) { 620 return -1; 621 } 622 623 if ((size_t) p % MBEDTLS_MEMORY_ALIGN_MULTIPLE != 0) { 624 return -1; 625 } 626 627 return 0; 628 } 629 630 static int check_all_free(void) 631 { 632 if ( 633 #if defined(MBEDTLS_MEMORY_DEBUG) 634 heap.total_used != 0 || 635 #endif 636 heap.first != heap.first_free || 637 (void *) heap.first != (void *) heap.buf) { 638 return -1; 639 } 640 641 return 0; 642 } 643 644 #define TEST_ASSERT(condition) \ 645 if (!(condition)) \ 646 { \ 647 if (verbose != 0) \ 648 mbedtls_printf("failed\n"); \ 649 \ 650 ret = 1; \ 651 goto cleanup; \ 652 } 653 654 int mbedtls_memory_buffer_alloc_self_test(int verbose) 655 { 656 unsigned char buf[1024]; 657 unsigned char *p, *q, *r, *end; 658 int ret = 0; 659 660 if (verbose != 0) { 661 mbedtls_printf(" MBA test #1 (basic alloc-free cycle): "); 662 } 663 664 mbedtls_memory_buffer_alloc_init(buf, sizeof(buf)); 665 666 p = mbedtls_calloc(1, 1); 667 q = mbedtls_calloc(1, 128); 668 r = mbedtls_calloc(1, 16); 669 670 TEST_ASSERT(check_pointer(p) == 0 && 671 check_pointer(q) == 0 && 672 check_pointer(r) == 0); 673 674 mbedtls_free(r); 675 mbedtls_free(q); 676 mbedtls_free(p); 677 678 TEST_ASSERT(check_all_free() == 0); 679 680 /* Memorize end to compare with the next test */ 681 end = heap.buf + heap.len; 682 683 mbedtls_memory_buffer_alloc_free(); 684 685 if (verbose != 0) { 686 mbedtls_printf("passed\n"); 687 } 688 689 if (verbose != 0) { 690 mbedtls_printf(" MBA test #2 (buf not aligned): "); 691 } 692 693 mbedtls_memory_buffer_alloc_init(buf + 1, sizeof(buf) - 1); 694 695 TEST_ASSERT(heap.buf + heap.len == end); 696 697 p = mbedtls_calloc(1, 1); 698 q = mbedtls_calloc(1, 128); 699 r = mbedtls_calloc(1, 16); 700 701 TEST_ASSERT(check_pointer(p) == 0 && 702 check_pointer(q) == 0 && 703 check_pointer(r) == 0); 704 705 mbedtls_free(r); 706 mbedtls_free(q); 707 mbedtls_free(p); 708 709 TEST_ASSERT(check_all_free() == 0); 710 711 mbedtls_memory_buffer_alloc_free(); 712 713 if (verbose != 0) { 714 mbedtls_printf("passed\n"); 715 } 716 717 if (verbose != 0) { 718 mbedtls_printf(" MBA test #3 (full): "); 719 } 720 721 mbedtls_memory_buffer_alloc_init(buf, sizeof(buf)); 722 723 p = mbedtls_calloc(1, sizeof(buf) - sizeof(memory_header)); 724 725 TEST_ASSERT(check_pointer(p) == 0); 726 TEST_ASSERT(mbedtls_calloc(1, 1) == NULL); 727 728 mbedtls_free(p); 729 730 p = mbedtls_calloc(1, sizeof(buf) - 2 * sizeof(memory_header) - 16); 731 q = mbedtls_calloc(1, 16); 732 733 TEST_ASSERT(check_pointer(p) == 0 && check_pointer(q) == 0); 734 TEST_ASSERT(mbedtls_calloc(1, 1) == NULL); 735 736 mbedtls_free(q); 737 738 TEST_ASSERT(mbedtls_calloc(1, 17) == NULL); 739 740 mbedtls_free(p); 741 742 TEST_ASSERT(check_all_free() == 0); 743 744 mbedtls_memory_buffer_alloc_free(); 745 746 if (verbose != 0) { 747 mbedtls_printf("passed\n"); 748 } 749 750 cleanup: 751 mbedtls_memory_buffer_alloc_free(); 752 753 return ret; 754 } 755 #endif /* MBEDTLS_SELF_TEST */ 756 757 #endif /* MBEDTLS_MEMORY_BUFFER_ALLOC_C */ 758