1 /* 2 * ***************************************************************************** 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 * 6 * Copyright (c) 2018-2025 Gavin D. Howard and contributors. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions are met: 10 * 11 * * Redistributions of source code must retain the above copyright notice, this 12 * list of conditions and the following disclaimer. 13 * 14 * * Redistributions in binary form must reproduce the above copyright notice, 15 * this list of conditions and the following disclaimer in the documentation 16 * and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGE. 29 * 30 * ***************************************************************************** 31 * 32 * Code common to all of bc and dc. 33 * 34 */ 35 36 #include <assert.h> 37 #include <ctype.h> 38 #include <errno.h> 39 #include <stdarg.h> 40 #include <string.h> 41 42 #include <signal.h> 43 44 #include <setjmp.h> 45 46 #ifndef _WIN32 47 48 #include <unistd.h> 49 #include <sys/types.h> 50 #include <unistd.h> 51 52 #else // _WIN32 53 54 #define WIN32_LEAN_AND_MEAN 55 #include <windows.h> 56 #include <io.h> 57 58 #endif // _WIN32 59 60 #include <status.h> 61 #include <vector.h> 62 #include <args.h> 63 #include <vm.h> 64 #include <read.h> 65 #include <bc.h> 66 #if BC_ENABLE_LIBRARY 67 #include <library.h> 68 #endif // BC_ENABLE_LIBRARY 69 #if BC_ENABLE_OSSFUZZ 70 #include <ossfuzz.h> 71 #endif // BC_ENABLE_OSSFUZZ 72 73 #if !BC_ENABLE_LIBRARY 74 75 // The actual globals. 76 char output_bufs[BC_VM_BUF_SIZE]; 77 BcVm vm_data; 78 BcVm* vm = &vm_data; 79 80 #endif // !BC_ENABLE_LIBRARY 81 82 #if BC_DEBUG_CODE 83 BC_NORETURN void 84 bc_vm_jmp(const char* f) 85 { 86 #else // BC_DEBUG_CODE 87 BC_NORETURN void 88 bc_vm_jmp(void) 89 { 90 #endif 91 92 #if BC_ENABLE_LIBRARY 93 BcVm* vm = bcl_getspecific(); 94 #endif // BC_ENABLE_LIBRARY 95 96 assert(BC_SIG_EXC(vm)); 97 98 BC_SIG_MAYLOCK; 99 100 #if BC_DEBUG_CODE 101 bc_file_puts(&vm->ferr, bc_flush_none, "Longjmp: "); 102 bc_file_puts(&vm->ferr, bc_flush_none, f); 103 bc_file_putchar(&vm->ferr, bc_flush_none, '\n'); 104 bc_file_flush(&vm->ferr, bc_flush_none); 105 #endif // BC_DEBUG_CODE 106 107 #if BC_DEBUG 108 assert(vm->jmp_bufs.len - (size_t) vm->sig_pop); 109 #endif // BC_DEBUG 110 111 if (vm->jmp_bufs.len == 0) abort(); 112 if (vm->sig_pop) bc_vec_pop(&vm->jmp_bufs); 113 else vm->sig_pop = 1; 114 115 siglongjmp(*((sigjmp_buf*) bc_vec_top(&vm->jmp_bufs)), 1); 116 } 117 118 #if !BC_ENABLE_LIBRARY 119 120 /** 121 * Handles signals. This is the signal handler. 122 * @param sig The signal to handle. 123 */ 124 static void 125 bc_vm_sig(int sig) 126 { 127 // There is already a signal in flight if this is true. 128 if (vm->status == (sig_atomic_t) BC_STATUS_QUIT || vm->sig != 0) 129 { 130 if (!BC_I || sig != SIGINT) vm->status = BC_STATUS_QUIT; 131 return; 132 } 133 134 // We always want to set this because a stack trace can be printed if we do. 135 vm->sig = sig; 136 137 // Only reset under these conditions; otherwise, quit. 138 if (sig == SIGINT && BC_SIGINT && BC_I) 139 { 140 int err = errno; 141 142 #if BC_ENABLE_EDITLINE 143 // Editline needs this, for some unknown reason. 144 if (write(STDOUT_FILENO, "^C", 2) != (ssize_t) 2) 145 { 146 vm->status = BC_STATUS_ERROR_FATAL; 147 } 148 #endif // BC_ENABLE_EDITLINE 149 150 // Write the message. 151 if (write(STDOUT_FILENO, vm->sigmsg, vm->siglen) != 152 (ssize_t) vm->siglen) 153 { 154 vm->status = BC_STATUS_ERROR_FATAL; 155 } 156 157 errno = err; 158 } 159 else 160 { 161 #if BC_ENABLE_EDITLINE 162 if (write(STDOUT_FILENO, "^C", 2) != (ssize_t) 2) 163 { 164 vm->status = BC_STATUS_ERROR_FATAL; 165 return; 166 } 167 #endif // BC_ENABLE_EDITLINE 168 169 vm->status = BC_STATUS_QUIT; 170 } 171 172 #if BC_ENABLE_LINE_LIB 173 // Readline and Editline need this to actually handle sigints correctly. 174 if (sig == SIGINT && bc_history_inlinelib) 175 { 176 bc_history_inlinelib = 0; 177 siglongjmp(bc_history_jmpbuf, 1); 178 } 179 #endif // BC_ENABLE_LINE_LIB 180 181 assert(vm->jmp_bufs.len); 182 183 // Only jump if signals are not locked. The jump will happen by whoever 184 // unlocks signals. 185 if (!vm->sig_lock) BC_JMP; 186 } 187 188 /** 189 * Sets up signal handling. 190 */ 191 static void 192 bc_vm_sigaction(void) 193 { 194 #ifndef _WIN32 195 196 struct sigaction sa; 197 198 sigemptyset(&sa.sa_mask); 199 sa.sa_flags = SA_NODEFER; 200 201 // This mess is to silence a warning on Clang with regards to glibc's 202 // sigaction handler, which activates the warning here. 203 #if BC_CLANG 204 #pragma clang diagnostic push 205 #pragma clang diagnostic ignored "-Wdisabled-macro-expansion" 206 #endif // BC_CLANG 207 sa.sa_handler = bc_vm_sig; 208 #if BC_CLANG 209 #pragma clang diagnostic pop 210 #endif // BC_CLANG 211 212 sigaction(SIGTERM, &sa, NULL); 213 sigaction(SIGQUIT, &sa, NULL); 214 sigaction(SIGINT, &sa, NULL); 215 216 #if BC_ENABLE_HISTORY 217 if (BC_TTY) sigaction(SIGHUP, &sa, NULL); 218 #endif // BC_ENABLE_HISTORY 219 220 #else // _WIN32 221 222 signal(SIGTERM, bc_vm_sig); 223 signal(SIGINT, bc_vm_sig); 224 225 #endif // _WIN32 226 } 227 228 void 229 bc_vm_info(const char* const help) 230 { 231 BC_SIG_ASSERT_LOCKED; 232 233 // Print the banner. 234 bc_file_printf(&vm->fout, "%s %s\n%s", vm->name, BC_VERSION, bc_copyright); 235 236 // Print the help. 237 if (help != NULL) 238 { 239 bc_file_putchar(&vm->fout, bc_flush_none, '\n'); 240 241 #if BC_ENABLED 242 if (BC_IS_BC) 243 { 244 const char* const banner = BC_DEFAULT_BANNER ? "to" : "to not"; 245 const char* const sigint = BC_DEFAULT_SIGINT_RESET ? "to reset" : 246 "to exit"; 247 const char* const tty = BC_DEFAULT_TTY_MODE ? "enabled" : 248 "disabled"; 249 const char* const prompt = BC_DEFAULT_PROMPT ? "enabled" : 250 "disabled"; 251 const char* const expr = BC_DEFAULT_EXPR_EXIT ? "to exit" : 252 "to not exit"; 253 const char* const clamp = BC_DEFAULT_DIGIT_CLAMP ? "to clamp" : 254 "to not clamp"; 255 256 bc_file_printf(&vm->fout, help, vm->name, vm->name, BC_VERSION, 257 BC_BUILD_TYPE, banner, sigint, tty, prompt, expr, 258 clamp); 259 } 260 #endif // BC_ENABLED 261 262 #if DC_ENABLED 263 if (BC_IS_DC) 264 { 265 const char* const sigint = DC_DEFAULT_SIGINT_RESET ? "to reset" : 266 "to exit"; 267 const char* const tty = DC_DEFAULT_TTY_MODE ? "enabled" : 268 "disabled"; 269 const char* const prompt = DC_DEFAULT_PROMPT ? "enabled" : 270 "disabled"; 271 const char* const expr = DC_DEFAULT_EXPR_EXIT ? "to exit" : 272 "to not exit"; 273 const char* const clamp = DC_DEFAULT_DIGIT_CLAMP ? "to clamp" : 274 "to not clamp"; 275 276 bc_file_printf(&vm->fout, help, vm->name, vm->name, BC_VERSION, 277 BC_BUILD_TYPE, sigint, tty, prompt, expr, clamp); 278 } 279 #endif // DC_ENABLED 280 } 281 282 // Flush. 283 bc_file_flush(&vm->fout, bc_flush_none); 284 } 285 #endif // !BC_ENABLE_LIBRARY 286 287 #if !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK 288 BC_NORETURN 289 #endif // !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK 290 void 291 bc_vm_fatalError(BcErr e) 292 { 293 bc_err(e); 294 #if !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK 295 BC_UNREACHABLE 296 #if !BC_CLANG 297 abort(); 298 #endif // !BC_CLANG 299 #endif // !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK 300 } 301 302 #if BC_ENABLE_LIBRARY 303 BC_NORETURN void 304 bc_vm_handleError(BcErr e) 305 { 306 #if BC_ENABLE_LIBRARY 307 BcVm* vm = bcl_getspecific(); 308 #endif // BC_ENABLE_LIBRARY 309 310 assert(e < BC_ERR_NELEMS); 311 assert(!vm->sig_pop); 312 313 BC_SIG_LOCK; 314 315 // If we have a normal error... 316 if (e <= BC_ERR_MATH_DIVIDE_BY_ZERO) 317 { 318 // Set the error. 319 vm->err = (BclError) (e - BC_ERR_MATH_NEGATIVE + 320 BCL_ERROR_MATH_NEGATIVE); 321 } 322 // Abort if we should. 323 else if (vm->abrt) abort(); 324 else if (e == BC_ERR_FATAL_ALLOC_ERR) vm->err = BCL_ERROR_FATAL_ALLOC_ERR; 325 else vm->err = BCL_ERROR_FATAL_UNKNOWN_ERR; 326 327 BC_JMP; 328 } 329 #else // BC_ENABLE_LIBRARY 330 #if BC_DEBUG 331 void 332 bc_vm_handleError(BcErr e, const char* file, int fline, size_t line, ...) 333 #else // BC_DEBUG 334 void 335 bc_vm_handleError(BcErr e, size_t line, ...) 336 #endif // BC_DEBUG 337 { 338 BcStatus s; 339 BcStatus fout_s; 340 va_list args; 341 uchar id = bc_err_ids[e]; 342 const char* err_type = vm->err_ids[id]; 343 sig_atomic_t lock; 344 345 assert(e < BC_ERR_NELEMS); 346 assert(!vm->sig_pop); 347 348 #if BC_ENABLED 349 // Figure out if the POSIX error should be an error, a warning, or nothing. 350 if (!BC_S && e >= BC_ERR_POSIX_START) 351 { 352 if (BC_W) 353 { 354 // Make sure to not return an error. 355 id = UCHAR_MAX; 356 err_type = vm->err_ids[BC_ERR_IDX_WARN]; 357 } 358 else return; 359 } 360 #endif // BC_ENABLED 361 362 BC_SIG_TRYLOCK(lock); 363 364 // Make sure all of stdout is written first. 365 fout_s = bc_file_flushErr(&vm->fout, bc_flush_err); 366 367 // XXX: Keep the status for later. 368 369 // Print the error message. 370 va_start(args, line); 371 bc_file_putchar(&vm->ferr, bc_flush_none, '\n'); 372 bc_file_puts(&vm->ferr, bc_flush_none, err_type); 373 bc_file_putchar(&vm->ferr, bc_flush_none, ' '); 374 bc_file_vprintf(&vm->ferr, vm->err_msgs[e], args); 375 va_end(args); 376 377 // Print the extra information if we have it. 378 if (BC_NO_ERR(vm->file != NULL)) 379 { 380 // This is the condition for parsing vs runtime. 381 // If line is not 0, it is parsing. 382 if (line) 383 { 384 bc_file_puts(&vm->ferr, bc_flush_none, "\n "); 385 bc_file_puts(&vm->ferr, bc_flush_none, vm->file); 386 bc_file_printf(&vm->ferr, ":%zu\n", line); 387 } 388 else 389 { 390 // Print a stack trace. 391 bc_file_putchar(&vm->ferr, bc_flush_none, '\n'); 392 bc_program_printStackTrace(&vm->prog); 393 } 394 } 395 else 396 { 397 bc_file_putchar(&vm->ferr, bc_flush_none, '\n'); 398 } 399 400 #if BC_DEBUG 401 bc_file_printf(&vm->ferr, "\n %s:%d\n", file, fline); 402 #endif // BC_DEBUG 403 404 bc_file_puts(&vm->ferr, bc_flush_none, "\n"); 405 406 // If flushing to stdout failed, try to print *that* error, as long as that 407 // was not the error already. 408 if (fout_s == BC_STATUS_ERROR_FATAL && e != BC_ERR_FATAL_IO_ERR) 409 { 410 bc_file_putchar(&vm->ferr, bc_flush_none, '\n'); 411 bc_file_puts(&vm->ferr, bc_flush_none, 412 vm->err_ids[bc_err_ids[BC_ERR_FATAL_IO_ERR]]); 413 bc_file_putchar(&vm->ferr, bc_flush_none, ' '); 414 bc_file_puts(&vm->ferr, bc_flush_none, 415 vm->err_msgs[BC_ERR_FATAL_IO_ERR]); 416 } 417 418 s = bc_file_flushErr(&vm->ferr, bc_flush_err); 419 420 #if !BC_ENABLE_MEMCHECK 421 422 // Because this function is called by a BC_NORETURN function when fatal 423 // errors happen, we need to make sure to exit on fatal errors. This will 424 // be faster anyway. This function *cannot jump when a fatal error occurs!* 425 if (BC_ERR(id == BC_ERR_IDX_FATAL || fout_s == BC_STATUS_ERROR_FATAL || 426 s == BC_STATUS_ERROR_FATAL)) 427 { 428 exit((int) BC_STATUS_ERROR_FATAL); 429 } 430 431 #else // !BC_ENABLE_MEMCHECK 432 if (BC_ERR(fout_s == BC_STATUS_ERROR_FATAL)) 433 { 434 vm->status = (sig_atomic_t) fout_s; 435 } 436 else if (BC_ERR(s == BC_STATUS_ERROR_FATAL)) 437 { 438 vm->status = (sig_atomic_t) s; 439 } 440 else 441 #endif // !BC_ENABLE_MEMCHECK 442 { 443 vm->status = (sig_atomic_t) (uchar) (id + 1); 444 } 445 446 // Only jump if there is an error. 447 if (BC_ERR(vm->status)) BC_JMP; 448 449 BC_SIG_TRYUNLOCK(lock); 450 } 451 452 char* 453 bc_vm_getenv(const char* var) 454 { 455 char* ret; 456 457 #ifndef _WIN32 458 ret = getenv(var); 459 #else // _WIN32 460 _dupenv_s(&ret, NULL, var); 461 #endif // _WIN32 462 463 return ret; 464 } 465 466 void 467 bc_vm_getenvFree(char* val) 468 { 469 BC_UNUSED(val); 470 #ifdef _WIN32 471 free(val); 472 #endif // _WIN32 473 } 474 475 /** 476 * Sets a flag from an environment variable and the default. 477 * @param var The environment variable. 478 * @param def The default. 479 * @param flag The flag to set. 480 */ 481 static void 482 bc_vm_setenvFlag(const char* const var, int def, uint16_t flag) 483 { 484 // Get the value. 485 char* val = bc_vm_getenv(var); 486 487 // If there is no value... 488 if (val == NULL) 489 { 490 // Set the default. 491 if (def) vm->flags |= flag; 492 else vm->flags &= ~(flag); 493 } 494 // Parse the value. 495 else if (strtoul(val, NULL, 0)) vm->flags |= flag; 496 else vm->flags &= ~(flag); 497 498 bc_vm_getenvFree(val); 499 } 500 501 /** 502 * Parses the arguments in {B,D]C_ENV_ARGS. 503 * @param env_args_name The environment variable to use. 504 * @param scale A pointer to return the scale that the arguments set, 505 * if any. 506 * @param ibase A pointer to return the ibase that the arguments set, 507 * if any. 508 * @param obase A pointer to return the obase that the arguments set, 509 * if any. 510 */ 511 static void 512 bc_vm_envArgs(const char* const env_args_name, BcBigDig* scale, BcBigDig* ibase, 513 BcBigDig* obase) 514 { 515 char *env_args = bc_vm_getenv(env_args_name), *buf, *start; 516 char instr = '\0'; 517 518 BC_SIG_ASSERT_LOCKED; 519 520 if (env_args == NULL) return; 521 522 // Windows already allocates, so we don't need to. 523 #ifndef _WIN32 524 start = buf = vm->env_args_buffer = bc_vm_strdup(env_args); 525 #else // _WIN32 526 start = buf = vm->env_args_buffer = env_args; 527 #endif // _WIN32 528 529 assert(buf != NULL); 530 531 // Create two buffers for parsing. These need to stay throughout the entire 532 // execution of bc, unfortunately, because of filenames that might be in 533 // there. 534 bc_vec_init(&vm->env_args, sizeof(char*), BC_DTOR_NONE); 535 bc_vec_push(&vm->env_args, &env_args_name); 536 537 // While we haven't reached the end of the args... 538 while (*buf) 539 { 540 // If we don't have whitespace... 541 if (!isspace(*buf)) 542 { 543 // If we have the start of a string... 544 if (*buf == '"' || *buf == '\'') 545 { 546 // Set stuff appropriately. 547 instr = *buf; 548 buf += 1; 549 550 // Check for the empty string. 551 if (*buf == instr) 552 { 553 instr = '\0'; 554 buf += 1; 555 continue; 556 } 557 } 558 559 // Push the pointer to the args buffer. 560 bc_vec_push(&vm->env_args, &buf); 561 562 // Parse the string. 563 while (*buf && 564 ((!instr && !isspace(*buf)) || (instr && *buf != instr))) 565 { 566 buf += 1; 567 } 568 569 // If we did find the end of the string... 570 if (*buf) 571 { 572 if (instr) instr = '\0'; 573 574 // Reset stuff. 575 *buf = '\0'; 576 buf += 1; 577 start = buf; 578 } 579 else if (instr) bc_error(BC_ERR_FATAL_OPTION, 0, start); 580 } 581 // If we have whitespace, eat it. 582 else buf += 1; 583 } 584 585 // Make sure to push a NULL pointer at the end. 586 buf = NULL; 587 bc_vec_push(&vm->env_args, &buf); 588 589 // Parse the arguments. 590 bc_args((int) vm->env_args.len - 1, bc_vec_item(&vm->env_args, 0), false, 591 scale, ibase, obase); 592 } 593 594 /** 595 * Gets the {B,D}C_LINE_LENGTH. 596 * @param var The environment variable to pull it from. 597 * @return The line length. 598 */ 599 static size_t 600 bc_vm_envLen(const char* var) 601 { 602 char* lenv = bc_vm_getenv(var); 603 size_t i, len = BC_NUM_PRINT_WIDTH; 604 int num; 605 606 // Return the default with none. 607 if (lenv == NULL) return len; 608 609 len = strlen(lenv); 610 611 // Figure out if it's a number. 612 for (num = 1, i = 0; num && i < len; ++i) 613 { 614 num = isdigit(lenv[i]); 615 } 616 617 // If it is a number... 618 if (num) 619 { 620 // Parse it and clamp it if needed. 621 len = (size_t) strtol(lenv, NULL, 10); 622 if (len != 0) 623 { 624 len -= 1; 625 if (len < 2 || len >= UINT16_MAX) len = BC_NUM_PRINT_WIDTH; 626 } 627 } 628 // Set the default. 629 else len = BC_NUM_PRINT_WIDTH; 630 631 bc_vm_getenvFree(lenv); 632 633 return len; 634 } 635 #endif // BC_ENABLE_LIBRARY 636 637 void 638 bc_vm_shutdown(void) 639 { 640 BC_SIG_ASSERT_LOCKED; 641 642 #if BC_ENABLE_NLS 643 if (vm->catalog != BC_VM_INVALID_CATALOG) catclose(vm->catalog); 644 #endif // BC_ENABLE_NLS 645 646 #if !BC_ENABLE_LIBRARY 647 #if BC_ENABLE_HISTORY 648 // This must always run to ensure that the terminal is back to normal, i.e., 649 // has raw mode disabled. But we should only do it if we did not have a bad 650 // terminal because history was not initialized if it is a bad terminal. 651 if (BC_TTY && !vm->history.badTerm) bc_history_free(&vm->history); 652 #endif // BC_ENABLE_HISTORY 653 #endif // !BC_ENABLE_LIBRARY 654 655 #if BC_DEBUG || BC_ENABLE_MEMCHECK 656 #if !BC_ENABLE_LIBRARY 657 bc_vec_free(&vm->env_args); 658 free(vm->env_args_buffer); 659 bc_vec_free(&vm->files); 660 bc_vec_free(&vm->exprs); 661 662 if (BC_PARSE_IS_INITED(&vm->read_prs, &vm->prog)) 663 { 664 bc_vec_free(&vm->read_buf); 665 bc_parse_free(&vm->read_prs); 666 } 667 668 bc_parse_free(&vm->prs); 669 bc_program_free(&vm->prog); 670 671 bc_slabvec_free(&vm->slabs); 672 #endif // !BC_ENABLE_LIBRARY 673 674 bc_vm_freeTemps(); 675 #endif // BC_DEBUG || BC_ENABLE_MEMCHECK 676 677 #if !BC_ENABLE_LIBRARY 678 // We always want to flush. 679 bc_file_free(&vm->fout); 680 bc_file_free(&vm->ferr); 681 #endif // !BC_ENABLE_LIBRARY 682 } 683 684 void 685 bc_vm_addTemp(BcDig* num) 686 { 687 #if BC_ENABLE_LIBRARY 688 BcVm* vm = bcl_getspecific(); 689 #endif // BC_ENABLE_LIBRARY 690 691 BC_SIG_ASSERT_LOCKED; 692 693 // If we don't have room, just free. 694 if (vm->temps_len == BC_VM_MAX_TEMPS) free(num); 695 else 696 { 697 // Add to the buffer and length. 698 vm->temps_buf[vm->temps_len] = num; 699 vm->temps_len += 1; 700 } 701 } 702 703 BcDig* 704 bc_vm_takeTemp(void) 705 { 706 #if BC_ENABLE_LIBRARY 707 BcVm* vm = bcl_getspecific(); 708 #endif // BC_ENABLE_LIBRARY 709 710 BC_SIG_ASSERT_LOCKED; 711 712 if (!vm->temps_len) return NULL; 713 714 vm->temps_len -= 1; 715 716 return vm->temps_buf[vm->temps_len]; 717 } 718 719 BcDig* 720 bc_vm_getTemp(void) 721 { 722 #if BC_ENABLE_LIBRARY 723 BcVm* vm = bcl_getspecific(); 724 #endif // BC_ENABLE_LIBRARY 725 726 BC_SIG_ASSERT_LOCKED; 727 728 if (!vm->temps_len) return NULL; 729 730 return vm->temps_buf[vm->temps_len - 1]; 731 } 732 733 void 734 bc_vm_freeTemps(void) 735 { 736 size_t i; 737 #if BC_ENABLE_LIBRARY 738 BcVm* vm = bcl_getspecific(); 739 #endif // BC_ENABLE_LIBRARY 740 741 BC_SIG_ASSERT_LOCKED; 742 743 if (!vm->temps_len) return; 744 745 // Free them all... 746 for (i = 0; i < vm->temps_len; ++i) 747 { 748 free(vm->temps_buf[i]); 749 } 750 751 vm->temps_len = 0; 752 } 753 754 #if !BC_ENABLE_LIBRARY 755 756 size_t 757 bc_vm_numDigits(size_t val) 758 { 759 size_t digits = 0; 760 761 do 762 { 763 digits += 1; 764 val /= 10; 765 } 766 while (val != 0); 767 768 return digits; 769 } 770 771 #endif // !BC_ENABLE_LIBRARY 772 773 inline size_t 774 bc_vm_arraySize(size_t n, size_t size) 775 { 776 size_t res = n * size; 777 778 if (BC_ERR(BC_VM_MUL_OVERFLOW(n, size, res))) 779 { 780 bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR); 781 } 782 783 return res; 784 } 785 786 inline size_t 787 bc_vm_growSize(size_t a, size_t b) 788 { 789 size_t res = a + b; 790 791 if (BC_ERR(res >= SIZE_MAX || res < a)) 792 { 793 bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR); 794 } 795 796 return res; 797 } 798 799 void* 800 bc_vm_malloc(size_t n) 801 { 802 void* ptr; 803 804 BC_SIG_ASSERT_LOCKED; 805 806 ptr = malloc(n); 807 808 if (BC_ERR(ptr == NULL)) 809 { 810 bc_vm_freeTemps(); 811 812 ptr = malloc(n); 813 814 if (BC_ERR(ptr == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR); 815 } 816 817 return ptr; 818 } 819 820 void* 821 bc_vm_realloc(void* ptr, size_t n) 822 { 823 void* temp; 824 825 BC_SIG_ASSERT_LOCKED; 826 827 temp = realloc(ptr, n); 828 829 if (BC_ERR(temp == NULL)) 830 { 831 bc_vm_freeTemps(); 832 833 temp = realloc(ptr, n); 834 835 if (BC_ERR(temp == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR); 836 } 837 838 return temp; 839 } 840 841 char* 842 bc_vm_strdup(const char* str) 843 { 844 char* s; 845 846 BC_SIG_ASSERT_LOCKED; 847 848 s = strdup(str); 849 850 if (BC_ERR(s == NULL)) 851 { 852 bc_vm_freeTemps(); 853 854 s = strdup(str); 855 856 if (BC_ERR(s == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR); 857 } 858 859 return s; 860 } 861 862 #if !BC_ENABLE_LIBRARY 863 void 864 bc_vm_printf(const char* fmt, ...) 865 { 866 va_list args; 867 #if BC_ENABLE_LIBRARY 868 BcVm* vm = bcl_getspecific(); 869 #else // BC_ENABLE_LIBRARY 870 sig_atomic_t lock; 871 #endif // BC_ENABLE_LIBRARY 872 873 BC_SIG_TRYLOCK(lock); 874 875 va_start(args, fmt); 876 bc_file_vprintf(&vm->fout, fmt, args); 877 va_end(args); 878 879 vm->nchars = 0; 880 881 BC_SIG_TRYUNLOCK(lock); 882 } 883 #endif // !BC_ENABLE_LIBRARY 884 885 void 886 bc_vm_putchar(int c, BcFlushType type) 887 { 888 #if BC_ENABLE_LIBRARY 889 BcVm* vm = bcl_getspecific(); 890 bc_vec_pushByte(&vm->out, (uchar) c); 891 #else // BC_ENABLE_LIBRARY 892 bc_file_putchar(&vm->fout, type, (uchar) c); 893 vm->nchars = (c == '\n' ? 0 : vm->nchars + 1); 894 #endif // BC_ENABLE_LIBRARY 895 } 896 897 #if !BC_ENABLE_LIBRARY 898 899 #ifdef __OpenBSD__ 900 901 /** 902 * Aborts with a message. This should never be called because I have carefully 903 * made sure that the calls to pledge() and unveil() are correct, but it's here 904 * just in case. 905 * @param msg The message to print. 906 */ 907 BC_NORETURN static void 908 bc_abortm(const char* msg) 909 { 910 bc_file_puts(&vm->ferr, bc_flush_none, msg); 911 bc_file_puts(&vm->ferr, bc_flush_none, "; this is a bug"); 912 bc_file_flush(&vm->ferr, bc_flush_none); 913 abort(); 914 } 915 916 void 917 bc_pledge(const char* promises, const char* execpromises) 918 { 919 int r = pledge(promises, execpromises); 920 if (r) bc_abortm("pledge() failed"); 921 } 922 923 #if BC_ENABLE_EXTRA_MATH 924 925 /** 926 * A convenience and portability function for OpenBSD's unveil(). 927 * @param path The path. 928 * @param permissions The permissions for the path. 929 */ 930 static void 931 bc_unveil(const char* path, const char* permissions) 932 { 933 int r = unveil(path, permissions); 934 if (r) bc_abortm("unveil() failed"); 935 } 936 937 #endif // BC_ENABLE_EXTRA_MATH 938 939 #else // __OpenBSD__ 940 941 void 942 bc_pledge(const char* promises, const char* execpromises) 943 { 944 BC_UNUSED(promises); 945 BC_UNUSED(execpromises); 946 } 947 948 #if BC_ENABLE_EXTRA_MATH 949 static void 950 bc_unveil(const char* path, const char* permissions) 951 { 952 BC_UNUSED(path); 953 BC_UNUSED(permissions); 954 } 955 #endif // BC_ENABLE_EXTRA_MATH 956 957 #endif // __OpenBSD__ 958 959 /** 960 * Cleans unneeded variables, arrays, functions, strings, and constants when 961 * done executing a line of stdin. This is to prevent memory usage growing 962 * without bound. This is an idea from busybox. 963 */ 964 static void 965 bc_vm_clean(void) 966 { 967 BcVec* fns = &vm->prog.fns; 968 BcFunc* f = bc_vec_item(fns, BC_PROG_MAIN); 969 BcInstPtr* ip = bc_vec_item(&vm->prog.stack, 0); 970 bool good = ((vm->status && vm->status != BC_STATUS_QUIT) || vm->sig != 0); 971 972 BC_SIG_ASSERT_LOCKED; 973 974 // If all is good, go ahead and reset. 975 if (good) bc_program_reset(&vm->prog); 976 977 #if BC_ENABLED 978 // bc has this extra condition. If it not satisfied, it is in the middle of 979 // a parse. 980 if (good && BC_IS_BC) good = !BC_PARSE_NO_EXEC(&vm->prs); 981 #endif // BC_ENABLED 982 983 #if DC_ENABLED 984 // For dc, it is safe only when all of the results on the results stack are 985 // safe, which means that they are temporaries or other things that don't 986 // need strings or constants. 987 if (BC_IS_DC) 988 { 989 size_t i; 990 991 good = true; 992 993 for (i = 0; good && i < vm->prog.results.len; ++i) 994 { 995 BcResult* r = (BcResult*) bc_vec_item(&vm->prog.results, i); 996 good = BC_VM_SAFE_RESULT(r); 997 } 998 } 999 #endif // DC_ENABLED 1000 1001 // If this condition is true, we can get rid of strings, 1002 // constants, and code. 1003 if (good && vm->prog.stack.len == 1 && ip->idx == f->code.len) 1004 { 1005 // XXX: Nothing can be popped in dc. Deal with it. 1006 1007 #if BC_ENABLED 1008 if (BC_IS_BC) 1009 { 1010 // XXX: you cannot delete strings, functions, or constants in bc. 1011 // Deal with it. 1012 bc_vec_popAll(&f->labels); 1013 } 1014 #endif // BC_ENABLED 1015 1016 bc_vec_popAll(&f->code); 1017 1018 ip->idx = 0; 1019 } 1020 } 1021 1022 /** 1023 * Process a bunch of text. 1024 * @param text The text to process. 1025 * @param mode The mode to process in. 1026 */ 1027 static void 1028 bc_vm_process(const char* text, BcMode mode) 1029 { 1030 // Set up the parser. 1031 bc_parse_text(&vm->prs, text, mode); 1032 1033 while (vm->prs.l.t != BC_LEX_EOF) 1034 { 1035 // Parsing requires a signal lock. We also don't parse everything; we 1036 // want to execute as soon as possible for *everything*. 1037 BC_SIG_LOCK; 1038 vm->parse(&vm->prs); 1039 BC_SIG_UNLOCK; 1040 1041 // Execute if possible. 1042 if (BC_IS_DC || !BC_PARSE_NO_EXEC(&vm->prs)) bc_program_exec(&vm->prog); 1043 1044 assert(BC_IS_DC || vm->prog.results.len == 0); 1045 1046 // Flush in interactive mode. 1047 if (BC_I) bc_file_flush(&vm->fout, bc_flush_save); 1048 } 1049 } 1050 1051 #if BC_ENABLED 1052 1053 /** 1054 * Ends a series of if statements. This is to ensure that full parses happen 1055 * when a file finishes or stdin has no more data. Without this, bc thinks that 1056 * it cannot parse any further. But if we reach the end of a file or stdin has 1057 * no more data, we know we can add an empty else clause. 1058 */ 1059 static void 1060 bc_vm_endif(void) 1061 { 1062 bc_parse_endif(&vm->prs); 1063 bc_program_exec(&vm->prog); 1064 } 1065 1066 #endif // BC_ENABLED 1067 1068 /** 1069 * Processes a file. 1070 * @param file The filename. 1071 */ 1072 static void 1073 bc_vm_file(const char* file) 1074 { 1075 char* data = NULL; 1076 #if BC_ENABLE_LIBRARY 1077 BcVm* vm = bcl_getspecific(); 1078 #endif // BC_ENABLE_LIBRARY 1079 1080 assert(!vm->sig_pop); 1081 1082 vm->mode = BC_MODE_FILE; 1083 1084 // Set up the lexer. 1085 bc_lex_file(&vm->prs.l, file); 1086 1087 BC_SIG_LOCK; 1088 1089 // Read the file. 1090 data = bc_read_file(file); 1091 1092 assert(data != NULL); 1093 1094 BC_SETJMP_LOCKED(vm, err); 1095 1096 BC_SIG_UNLOCK; 1097 1098 // Process it. 1099 bc_vm_process(data, BC_MODE_FILE); 1100 1101 #if BC_ENABLED 1102 // Make sure to end any open if statements. 1103 if (BC_IS_BC) bc_vm_endif(); 1104 #endif // BC_ENABLED 1105 1106 err: 1107 1108 BC_SIG_MAYLOCK; 1109 1110 // Cleanup. 1111 free(data); 1112 bc_vm_clean(); 1113 1114 // bc_program_reset(), called by bc_vm_clean(), resets the status. 1115 // We want it to clear the sig_pop variable in case it was set. 1116 if (vm->status == (sig_atomic_t) BC_STATUS_SUCCESS) BC_LONGJMP_STOP; 1117 1118 BC_LONGJMP_CONT(vm); 1119 } 1120 1121 #if !BC_ENABLE_OSSFUZZ 1122 1123 bool 1124 bc_vm_readLine(bool clear) 1125 { 1126 BcStatus s; 1127 bool good; 1128 1129 BC_SIG_ASSERT_NOT_LOCKED; 1130 1131 // Clear the buffer if desired. 1132 if (clear) bc_vec_empty(&vm->buffer); 1133 1134 // Empty the line buffer. 1135 bc_vec_empty(&vm->line_buf); 1136 1137 if (vm->eof) return false; 1138 1139 do 1140 { 1141 // bc_read_line() must always return either BC_STATUS_SUCCESS or 1142 // BC_STATUS_EOF. Everything else, it and whatever it calls, must jump 1143 // out instead. 1144 s = bc_read_line(&vm->line_buf, ">>> "); 1145 vm->eof = (s == BC_STATUS_EOF); 1146 } 1147 while (s == BC_STATUS_SUCCESS && !vm->eof && vm->line_buf.len < 1); 1148 1149 good = (vm->line_buf.len > 1); 1150 1151 // Concat if we found something. 1152 if (good) bc_vec_concat(&vm->buffer, vm->line_buf.v); 1153 1154 return good; 1155 } 1156 1157 /** 1158 * Processes text from stdin. 1159 */ 1160 static void 1161 bc_vm_stdin(void) 1162 { 1163 bool clear; 1164 1165 #if BC_ENABLE_LIBRARY 1166 BcVm* vm = bcl_getspecific(); 1167 #endif // BC_ENABLE_LIBRARY 1168 1169 clear = true; 1170 vm->mode = BC_MODE_STDIN; 1171 1172 // Set up the lexer. 1173 bc_lex_file(&vm->prs.l, bc_program_stdin_name); 1174 1175 // These are global so that the lexers can access them, but they are 1176 // allocated and freed in this function because they should only be used for 1177 // stdin and expressions (they are used in bc_vm_exprs() as well). So they 1178 // are tied to this function, really. Well, this and bc_vm_readLine(). These 1179 // are the reasons that we have vm->is_stdin to tell the lexers if we are 1180 // reading from stdin. Well, both lexers care. And the reason they care is 1181 // so that if a comment or a string goes across multiple lines, the lexer 1182 // can request more data from stdin until the comment or string is ended. 1183 BC_SIG_LOCK; 1184 bc_vec_init(&vm->buffer, sizeof(uchar), BC_DTOR_NONE); 1185 bc_vec_init(&vm->line_buf, sizeof(uchar), BC_DTOR_NONE); 1186 BC_SETJMP_LOCKED(vm, err); 1187 BC_SIG_UNLOCK; 1188 1189 // This label exists because errors can cause jumps to end up at the err label 1190 // below. If that happens, and the error should be cleared and execution 1191 // continue, then we need to jump back. 1192 restart: 1193 1194 // While we still read data from stdin. 1195 while (bc_vm_readLine(clear)) 1196 { 1197 size_t len = vm->buffer.len - 1; 1198 const char* str = vm->buffer.v; 1199 1200 // We don't want to clear the buffer when the line ends with a backslash 1201 // because a backslash newline is special in bc. 1202 clear = (len < 2 || str[len - 2] != '\\' || str[len - 1] != '\n'); 1203 if (!clear) continue; 1204 1205 // Process the data. 1206 bc_vm_process(vm->buffer.v, BC_MODE_STDIN); 1207 1208 if (vm->eof) break; 1209 else 1210 { 1211 BC_SIG_LOCK; 1212 bc_vm_clean(); 1213 BC_SIG_UNLOCK; 1214 } 1215 } 1216 1217 #if BC_ENABLED 1218 // End the if statements. 1219 if (BC_IS_BC) bc_vm_endif(); 1220 #endif // BC_ENABLED 1221 1222 err: 1223 1224 BC_SIG_MAYLOCK; 1225 1226 // Cleanup. 1227 bc_vm_clean(); 1228 1229 #if !BC_ENABLE_MEMCHECK 1230 assert(vm->status != BC_STATUS_ERROR_FATAL); 1231 1232 vm->status = vm->status == BC_STATUS_QUIT || !BC_I ? vm->status : 1233 BC_STATUS_SUCCESS; 1234 #else // !BC_ENABLE_MEMCHECK 1235 vm->status = vm->status == BC_STATUS_ERROR_FATAL || 1236 vm->status == BC_STATUS_QUIT || !BC_I ? 1237 vm->status : 1238 BC_STATUS_SUCCESS; 1239 #endif // !BC_ENABLE_MEMCHECK 1240 1241 if (!vm->status && !vm->eof) 1242 { 1243 bc_vec_empty(&vm->buffer); 1244 BC_LONGJMP_STOP; 1245 BC_SIG_UNLOCK; 1246 goto restart; 1247 } 1248 1249 #if BC_DEBUG 1250 // Since these are tied to this function, free them here. We only free in 1251 // debug mode because stdin is always the last thing read. 1252 bc_vec_free(&vm->line_buf); 1253 bc_vec_free(&vm->buffer); 1254 #endif // BC_DEBUG 1255 1256 BC_LONGJMP_CONT(vm); 1257 } 1258 1259 #endif // BC_ENABLE_OSSFUZZ 1260 1261 bool 1262 bc_vm_readBuf(bool clear) 1263 { 1264 size_t len = vm->exprs.len - 1; 1265 bool more; 1266 1267 BC_SIG_ASSERT_NOT_LOCKED; 1268 1269 // Clear the buffer if desired. 1270 if (clear) bc_vec_empty(&vm->buffer); 1271 1272 // We want to pop the nul byte off because that's what bc_read_buf() 1273 // expects. 1274 bc_vec_pop(&vm->buffer); 1275 1276 // Read one line of expressions. 1277 more = bc_read_buf(&vm->buffer, vm->exprs.v, &len); 1278 bc_vec_pushByte(&vm->buffer, '\0'); 1279 1280 return more; 1281 } 1282 1283 static void 1284 bc_vm_exprs(void) 1285 { 1286 bool clear; 1287 1288 #if BC_ENABLE_LIBRARY 1289 BcVm* vm = bcl_getspecific(); 1290 #endif // BC_ENABLE_LIBRARY 1291 1292 clear = true; 1293 vm->mode = BC_MODE_EXPRS; 1294 1295 // Prepare the lexer. 1296 bc_lex_file(&vm->prs.l, bc_program_exprs_name); 1297 1298 // We initialize this so that the lexer can access it in the case that it 1299 // needs more data for expressions, such as for a multiline string or 1300 // comment. See the comment on the allocation of vm->buffer above in 1301 // bc_vm_stdin() for more information. 1302 BC_SIG_LOCK; 1303 bc_vec_init(&vm->buffer, sizeof(uchar), BC_DTOR_NONE); 1304 BC_SETJMP_LOCKED(vm, err); 1305 BC_SIG_UNLOCK; 1306 1307 while (bc_vm_readBuf(clear)) 1308 { 1309 size_t len = vm->buffer.len - 1; 1310 const char* str = vm->buffer.v; 1311 1312 // We don't want to clear the buffer when the line ends with a backslash 1313 // because a backslash newline is special in bc. 1314 clear = (len < 2 || str[len - 2] != '\\' || str[len - 1] != '\n'); 1315 if (!clear) continue; 1316 1317 // Process the data. 1318 bc_vm_process(vm->buffer.v, BC_MODE_EXPRS); 1319 } 1320 1321 // If we were not supposed to clear, then we should process everything. This 1322 // makes sure that errors get reported. 1323 if (!clear) bc_vm_process(vm->buffer.v, BC_MODE_EXPRS); 1324 1325 err: 1326 1327 BC_SIG_MAYLOCK; 1328 1329 // Cleanup. 1330 bc_vm_clean(); 1331 1332 // bc_program_reset(), called by bc_vm_clean(), resets the status. 1333 // We want it to clear the sig_pop variable in case it was set. 1334 if (vm->status == (sig_atomic_t) BC_STATUS_SUCCESS) BC_LONGJMP_STOP; 1335 1336 // Since this is tied to this function, free it here. We always free it here 1337 // because bc_vm_stdin() may or may not use it later. 1338 bc_vec_free(&vm->buffer); 1339 1340 BC_LONGJMP_CONT(vm); 1341 } 1342 1343 #if BC_ENABLED 1344 1345 /** 1346 * Loads a math library. 1347 * @param name The name of the library. 1348 * @param text The text of the source code. 1349 */ 1350 static void 1351 bc_vm_load(const char* name, const char* text) 1352 { 1353 bc_lex_file(&vm->prs.l, name); 1354 bc_parse_text(&vm->prs, text, BC_MODE_FILE); 1355 1356 BC_SIG_LOCK; 1357 1358 while (vm->prs.l.t != BC_LEX_EOF) 1359 { 1360 vm->parse(&vm->prs); 1361 } 1362 1363 BC_SIG_UNLOCK; 1364 } 1365 1366 #endif // BC_ENABLED 1367 1368 /** 1369 * Loads the default error messages. 1370 */ 1371 static void 1372 bc_vm_defaultMsgs(void) 1373 { 1374 size_t i; 1375 1376 // Load the error categories. 1377 for (i = 0; i < BC_ERR_IDX_NELEMS + BC_ENABLED; ++i) 1378 { 1379 vm->err_ids[i] = bc_errs[i]; 1380 } 1381 1382 // Load the error messages. 1383 for (i = 0; i < BC_ERR_NELEMS; ++i) 1384 { 1385 vm->err_msgs[i] = bc_err_msgs[i]; 1386 } 1387 } 1388 1389 /** 1390 * Loads the error messages for the locale. If NLS is disabled, this just loads 1391 * the default messages. 1392 */ 1393 static void 1394 bc_vm_gettext(void) 1395 { 1396 #if BC_ENABLE_NLS 1397 uchar id = 0; 1398 int set, msg = 1; 1399 size_t i; 1400 1401 // If no locale, load the defaults. 1402 if (vm->locale == NULL) 1403 { 1404 vm->catalog = BC_VM_INVALID_CATALOG; 1405 bc_vm_defaultMsgs(); 1406 return; 1407 } 1408 1409 vm->catalog = catopen(BC_MAINEXEC, NL_CAT_LOCALE); 1410 1411 // If no catalog, load the defaults. 1412 if (vm->catalog == BC_VM_INVALID_CATALOG) 1413 { 1414 bc_vm_defaultMsgs(); 1415 return; 1416 } 1417 1418 // Load the error categories. 1419 for (set = 1; msg <= BC_ERR_IDX_NELEMS + BC_ENABLED; ++msg) 1420 { 1421 vm->err_ids[msg - 1] = catgets(vm->catalog, set, msg, bc_errs[msg - 1]); 1422 } 1423 1424 i = 0; 1425 id = bc_err_ids[i]; 1426 1427 // Load the error messages. In order to understand this loop, you must know 1428 // the order of messages and categories in the enum and in the locale files. 1429 for (set = id + 2, msg = 1; i < BC_ERR_NELEMS; ++i, ++msg) 1430 { 1431 if (id != bc_err_ids[i]) 1432 { 1433 msg = 1; 1434 id = bc_err_ids[i]; 1435 set = id + 2; 1436 } 1437 1438 vm->err_msgs[i] = catgets(vm->catalog, set, msg, bc_err_msgs[i]); 1439 } 1440 #else // BC_ENABLE_NLS 1441 bc_vm_defaultMsgs(); 1442 #endif // BC_ENABLE_NLS 1443 } 1444 1445 /** 1446 * Starts execution. Really, this is a function of historical accident; it could 1447 * probably be combined with bc_vm_boot(), but I don't care enough. Really, this 1448 * function starts when execution of bc or dc source code starts. 1449 */ 1450 static void 1451 bc_vm_exec(void) 1452 { 1453 size_t i; 1454 #if DC_ENABLED 1455 bool has_file = false; 1456 #endif // DC_ENABLED 1457 1458 #if BC_ENABLED 1459 // Load the math libraries. 1460 if (BC_IS_BC && (vm->flags & BC_FLAG_L)) 1461 { 1462 // Can't allow redefinitions in the builtin library. 1463 vm->no_redefine = true; 1464 1465 bc_vm_load(bc_lib_name, bc_lib); 1466 1467 #if BC_ENABLE_EXTRA_MATH 1468 if (!BC_IS_POSIX) bc_vm_load(bc_lib2_name, bc_lib2); 1469 #endif // BC_ENABLE_EXTRA_MATH 1470 1471 // Make sure to clear this. 1472 vm->no_redefine = false; 1473 1474 // Execute to ensure that all is hunky dory. Without this, scale can be 1475 // set improperly. 1476 bc_program_exec(&vm->prog); 1477 } 1478 #endif // BC_ENABLED 1479 1480 assert(!BC_ENABLE_OSSFUZZ || BC_EXPR_EXIT == 0); 1481 1482 // If there are expressions to execute... 1483 if (vm->exprs.len) 1484 { 1485 // Process the expressions. 1486 bc_vm_exprs(); 1487 1488 // Sometimes, executing expressions means we need to quit. 1489 if (vm->status != BC_STATUS_SUCCESS || 1490 (!vm->no_exprs && vm->exit_exprs && BC_EXPR_EXIT)) 1491 { 1492 return; 1493 } 1494 } 1495 1496 // Process files. 1497 for (i = 0; i < vm->files.len; ++i) 1498 { 1499 char* path = *((char**) bc_vec_item(&vm->files, i)); 1500 if (!strcmp(path, "")) continue; 1501 #if DC_ENABLED 1502 has_file = true; 1503 #endif // DC_ENABLED 1504 bc_vm_file(path); 1505 1506 if (vm->status != BC_STATUS_SUCCESS) return; 1507 } 1508 1509 #if BC_ENABLE_EXTRA_MATH 1510 // These are needed for the pseudo-random number generator. 1511 bc_unveil("/dev/urandom", "r"); 1512 bc_unveil("/dev/random", "r"); 1513 bc_unveil(NULL, NULL); 1514 #endif // BC_ENABLE_EXTRA_MATH 1515 1516 #if BC_ENABLE_HISTORY 1517 1518 // We need to keep tty if history is enabled, and we need to keep rpath for 1519 // the times when we read from /dev/urandom. 1520 if (BC_TTY && !vm->history.badTerm) bc_pledge(bc_pledge_end_history, NULL); 1521 else 1522 #endif // BC_ENABLE_HISTORY 1523 { 1524 bc_pledge(bc_pledge_end, NULL); 1525 } 1526 1527 #if BC_ENABLE_AFL 1528 // This is the thing that makes fuzzing with AFL++ so fast. If you move this 1529 // back, you won't cause any problems, but fuzzing will slow down. If you 1530 // move this forward, you won't fuzz anything because you will be skipping 1531 // the reading from stdin. 1532 __AFL_INIT(); 1533 #endif // BC_ENABLE_AFL 1534 1535 #if BC_ENABLE_OSSFUZZ 1536 1537 if (BC_VM_RUN_STDIN(has_file)) 1538 { 1539 // XXX: Yes, this is a hack to run the fuzzer for OSS-Fuzz, but it 1540 // works. 1541 bc_vm_load("<stdin>", (const char*) bc_fuzzer_data); 1542 } 1543 1544 #else // BC_ENABLE_OSSFUZZ 1545 1546 // Execute from stdin. bc always does. 1547 if (BC_VM_RUN_STDIN(has_file)) bc_vm_stdin(); 1548 1549 #endif // BC_ENABLE_OSSFUZZ 1550 } 1551 1552 BcStatus 1553 bc_vm_boot(int argc, const char* argv[]) 1554 { 1555 int ttyin, ttyout, ttyerr; 1556 bool tty; 1557 const char* const env_len = BC_VM_LINE_LENGTH_STR; 1558 const char* const env_args = BC_VM_ENV_ARGS_STR; 1559 const char* const env_exit = BC_VM_EXPR_EXIT_STR; 1560 const char* const env_clamp = BC_VM_DIGIT_CLAMP_STR; 1561 int env_exit_def = BC_VM_EXPR_EXIT_DEF; 1562 int env_clamp_def = BC_VM_DIGIT_CLAMP_DEF; 1563 BcBigDig scale = BC_NUM_BIGDIG_MAX; 1564 BcBigDig env_scale = BC_NUM_BIGDIG_MAX; 1565 BcBigDig ibase = BC_NUM_BIGDIG_MAX; 1566 BcBigDig env_ibase = BC_NUM_BIGDIG_MAX; 1567 BcBigDig obase = BC_NUM_BIGDIG_MAX; 1568 BcBigDig env_obase = BC_NUM_BIGDIG_MAX; 1569 1570 // We need to know which of stdin, stdout, and stderr are tty's. 1571 ttyin = isatty(STDIN_FILENO); 1572 ttyout = isatty(STDOUT_FILENO); 1573 ttyerr = isatty(STDERR_FILENO); 1574 tty = (ttyin != 0 && ttyout != 0 && ttyerr != 0); 1575 1576 vm->flags |= ttyin ? BC_FLAG_TTYIN : 0; 1577 vm->flags |= tty ? BC_FLAG_TTY : 0; 1578 vm->flags |= ttyin && ttyout ? BC_FLAG_I : 0; 1579 1580 // Set up signals. 1581 bc_vm_sigaction(); 1582 1583 // Initialize some vm stuff. This is separate to make things easier for the 1584 // library. 1585 bc_vm_init(); 1586 1587 // Explicitly set this in case NULL isn't all zeroes. 1588 vm->file = NULL; 1589 1590 // Set the error messages. 1591 bc_vm_gettext(); 1592 1593 #if BC_ENABLE_LINE_LIB 1594 1595 // Initialize the output file buffers. 1596 bc_file_init(&vm->ferr, stderr, true); 1597 bc_file_init(&vm->fout, stdout, false); 1598 1599 // Set the input buffer. 1600 vm->buf = output_bufs; 1601 1602 #else // BC_ENABLE_LINE_LIB 1603 1604 // Initialize the output file buffers. They each take portions of the global 1605 // buffer. stdout gets more because it will probably have more data. 1606 bc_file_init(&vm->ferr, STDERR_FILENO, output_bufs + BC_VM_STDOUT_BUF_SIZE, 1607 BC_VM_STDERR_BUF_SIZE, true); 1608 bc_file_init(&vm->fout, STDOUT_FILENO, output_bufs, BC_VM_STDOUT_BUF_SIZE, 1609 false); 1610 1611 // Set the input buffer to the rest of the global buffer. 1612 vm->buf = output_bufs + BC_VM_STDOUT_BUF_SIZE + BC_VM_STDERR_BUF_SIZE; 1613 #endif // BC_ENABLE_LINE_LIB 1614 1615 // Set the line length by environment variable. 1616 vm->line_len = (uint16_t) bc_vm_envLen(env_len); 1617 1618 bc_vm_setenvFlag(env_exit, env_exit_def, BC_FLAG_EXPR_EXIT); 1619 bc_vm_setenvFlag(env_clamp, env_clamp_def, BC_FLAG_DIGIT_CLAMP); 1620 1621 // Clear the files and expressions vectors, just in case. This marks them as 1622 // *not* allocated. 1623 bc_vec_clear(&vm->files); 1624 bc_vec_clear(&vm->exprs); 1625 1626 #if !BC_ENABLE_LIBRARY 1627 1628 // Initialize the slab vector. 1629 bc_slabvec_init(&vm->slabs); 1630 1631 #endif // !BC_ENABLE_LIBRARY 1632 1633 // Initialize the program and main parser. These have to be in this order 1634 // because the program has to be initialized first, since a pointer to it is 1635 // passed to the parser. 1636 bc_program_init(&vm->prog); 1637 bc_parse_init(&vm->prs, &vm->prog, BC_PROG_MAIN); 1638 1639 // Set defaults. 1640 vm->flags |= BC_TTY ? BC_FLAG_P | BC_FLAG_R : 0; 1641 vm->flags |= BC_I ? BC_FLAG_Q : 0; 1642 1643 #if BC_ENABLED 1644 if (BC_IS_BC) 1645 { 1646 // bc checks this environment variable to see if it should run in 1647 // standard mode. 1648 char* var = bc_vm_getenv("POSIXLY_CORRECT"); 1649 1650 vm->flags |= BC_FLAG_S * (var != NULL); 1651 bc_vm_getenvFree(var); 1652 1653 // Set whether we print the banner or not. 1654 if (BC_I) bc_vm_setenvFlag("BC_BANNER", BC_DEFAULT_BANNER, BC_FLAG_Q); 1655 } 1656 #endif // BC_ENABLED 1657 1658 // Are we in TTY mode? 1659 if (BC_TTY) 1660 { 1661 const char* const env_tty = BC_VM_TTY_MODE_STR; 1662 int env_tty_def = BC_VM_TTY_MODE_DEF; 1663 const char* const env_prompt = BC_VM_PROMPT_STR; 1664 int env_prompt_def = BC_VM_PROMPT_DEF; 1665 1666 // Set flags for TTY mode and prompt. 1667 bc_vm_setenvFlag(env_tty, env_tty_def, BC_FLAG_TTY); 1668 bc_vm_setenvFlag(env_prompt, tty ? env_prompt_def : 0, BC_FLAG_P); 1669 1670 #if BC_ENABLE_HISTORY 1671 // If TTY mode is used, activate history. 1672 if (BC_TTY) bc_history_init(&vm->history); 1673 #endif // BC_ENABLE_HISTORY 1674 } 1675 1676 // Process environment and command-line arguments. 1677 bc_vm_envArgs(env_args, &env_scale, &env_ibase, &env_obase); 1678 bc_args(argc, argv, true, &scale, &ibase, &obase); 1679 1680 // This section is here because we don't want the math library to stomp on 1681 // the user's given value for scale. And we don't want ibase affecting how 1682 // the scale is interpreted. Also, it's sectioned off just for this comment. 1683 { 1684 BC_SIG_UNLOCK; 1685 1686 scale = scale == BC_NUM_BIGDIG_MAX ? env_scale : scale; 1687 #if BC_ENABLED 1688 // Assign the library value only if it is used and no value was set. 1689 scale = scale == BC_NUM_BIGDIG_MAX && BC_L ? 20 : scale; 1690 #endif // BC_ENABLED 1691 obase = obase == BC_NUM_BIGDIG_MAX ? env_obase : obase; 1692 ibase = ibase == BC_NUM_BIGDIG_MAX ? env_ibase : ibase; 1693 1694 if (scale != BC_NUM_BIGDIG_MAX) 1695 { 1696 bc_program_assignBuiltin(&vm->prog, true, false, scale); 1697 } 1698 1699 if (obase != BC_NUM_BIGDIG_MAX) 1700 { 1701 bc_program_assignBuiltin(&vm->prog, false, true, obase); 1702 } 1703 1704 // This is last to avoid it affecting the value of the others. 1705 if (ibase != BC_NUM_BIGDIG_MAX) 1706 { 1707 bc_program_assignBuiltin(&vm->prog, false, false, ibase); 1708 } 1709 1710 BC_SIG_LOCK; 1711 } 1712 1713 // If we are in interactive mode... 1714 if (BC_I) 1715 { 1716 const char* const env_sigint = BC_VM_SIGINT_RESET_STR; 1717 int env_sigint_def = BC_VM_SIGINT_RESET_DEF; 1718 1719 // Set whether we reset on SIGINT or not. 1720 bc_vm_setenvFlag(env_sigint, env_sigint_def, BC_FLAG_SIGINT); 1721 } 1722 1723 #if BC_ENABLED 1724 // Disable global stacks in POSIX mode. 1725 if (BC_IS_POSIX) vm->flags &= ~(BC_FLAG_G); 1726 1727 // Print the banner if allowed. We have to be in bc, in interactive mode, 1728 // and not be quieted by command-line option or environment variable. 1729 if (BC_IS_BC && BC_I && (vm->flags & BC_FLAG_Q)) 1730 { 1731 bc_vm_info(NULL); 1732 bc_file_putchar(&vm->fout, bc_flush_none, '\n'); 1733 bc_file_flush(&vm->fout, bc_flush_none); 1734 } 1735 #endif // BC_ENABLED 1736 1737 BC_SIG_UNLOCK; 1738 1739 // Start executing. 1740 bc_vm_exec(); 1741 1742 BC_SIG_LOCK; 1743 1744 // Exit. 1745 return (BcStatus) vm->status; 1746 } 1747 #endif // !BC_ENABLE_LIBRARY 1748 1749 void 1750 bc_vm_init(void) 1751 { 1752 #if BC_ENABLE_LIBRARY 1753 BcVm* vm = bcl_getspecific(); 1754 #endif // BC_ENABLE_LIBRARY 1755 1756 BC_SIG_ASSERT_LOCKED; 1757 1758 #if !BC_ENABLE_LIBRARY 1759 // Set up the constant zero. 1760 bc_num_setup(&vm->zero, vm->zero_num, BC_VM_ONE_CAP); 1761 #endif // !BC_ENABLE_LIBRARY 1762 1763 // Set up more constant BcNum's. 1764 bc_num_setup(&vm->one, vm->one_num, BC_VM_ONE_CAP); 1765 bc_num_one(&vm->one); 1766 1767 // Set up more constant BcNum's. 1768 // NOLINTNEXTLINE 1769 memcpy(vm->max_num, bc_num_bigdigMax, 1770 bc_num_bigdigMax_size * sizeof(BcDig)); 1771 // NOLINTNEXTLINE 1772 memcpy(vm->max2_num, bc_num_bigdigMax2, 1773 bc_num_bigdigMax2_size * sizeof(BcDig)); 1774 bc_num_setup(&vm->max, vm->max_num, BC_NUM_BIGDIG_LOG10); 1775 bc_num_setup(&vm->max2, vm->max2_num, BC_NUM_BIGDIG_LOG10); 1776 vm->max.len = bc_num_bigdigMax_size; 1777 vm->max2.len = bc_num_bigdigMax2_size; 1778 1779 // Set up the maxes for the globals. 1780 vm->maxes[BC_PROG_GLOBALS_IBASE] = BC_NUM_MAX_POSIX_IBASE; 1781 vm->maxes[BC_PROG_GLOBALS_OBASE] = BC_MAX_OBASE; 1782 vm->maxes[BC_PROG_GLOBALS_SCALE] = BC_MAX_SCALE; 1783 1784 #if BC_ENABLE_EXTRA_MATH 1785 vm->maxes[BC_PROG_MAX_RAND] = ((BcRand) 0) - 1; 1786 #endif // BC_ENABLE_EXTRA_MATH 1787 1788 #if BC_ENABLED 1789 #if !BC_ENABLE_LIBRARY 1790 // bc has a higher max ibase when it's not in POSIX mode. 1791 if (BC_IS_BC && !BC_IS_POSIX) 1792 #endif // !BC_ENABLE_LIBRARY 1793 { 1794 vm->maxes[BC_PROG_GLOBALS_IBASE] = BC_NUM_MAX_IBASE; 1795 } 1796 #endif // BC_ENABLED 1797 } 1798 1799 #if BC_ENABLE_LIBRARY 1800 void 1801 bc_vm_atexit(void) 1802 { 1803 #if BC_DEBUG 1804 #if BC_ENABLE_LIBRARY 1805 BcVm* vm = bcl_getspecific(); 1806 #endif // BC_ENABLE_LIBRARY 1807 #endif // BC_DEBUG 1808 1809 bc_vm_shutdown(); 1810 1811 #if BC_DEBUG 1812 bc_vec_free(&vm->jmp_bufs); 1813 #endif // BC_DEBUG 1814 } 1815 #else // BC_ENABLE_LIBRARY 1816 BcStatus 1817 bc_vm_atexit(BcStatus status) 1818 { 1819 // Set the status correctly. 1820 BcStatus s = BC_STATUS_IS_ERROR(status) ? status : BC_STATUS_SUCCESS; 1821 1822 bc_vm_shutdown(); 1823 1824 #if BC_DEBUG 1825 bc_vec_free(&vm->jmp_bufs); 1826 #endif // BC_DEBUG 1827 1828 return s; 1829 } 1830 #endif // BC_ENABLE_LIBRARY 1831