diff options
Diffstat (limited to 'src/alloca/needstack.c')
-rw-r--r-- | src/alloca/needstack.c | 154 |
1 files changed, 154 insertions, 0 deletions
diff --git a/src/alloca/needstack.c b/src/alloca/needstack.c new file mode 100644 index 0000000..b75eb02 --- /dev/null +++ b/src/alloca/needstack.c @@ -0,0 +1,154 @@ +/** + * slibc — Yet another C library + * Copyright © 2015 Mattias Andrée (maandree@member.fsf.org) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include <slibc/internals.h> +#include <alloca.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <signal.h> + + + +/** + * Unless the stack can grow with `n` bytes + * without overflowing, the program shall + * either be killed by the kernel (SIGSEGV) + * or this function shall indicate that the + * stack cannot be grown. + * + * @param n The number of bytes the stack must be able to grow. + * @return 0 if the stack can grow, 1 otherwise. + */ +int stack_will_overflow(intptr_t n) +{ + void* prev_brk; + + /* TODO This function shall block calls to brk and sbrk. */ + + prev_brk = sbrk(n); + if (prev_brk == (void*)-1) + return 1; + if (brk(prev_brk)) + return 1; /* Should not happen. */ + + { + /* TODO This function shall block calls to signal.h functions. */ + + sigset_t old_mask; + sigset_t new_mask; + void* old_sigsegv_handler; + + old_sigsegv_handler = signal(SIGSEGV, SIG_DFL); + if (old_sigsegv_handler == SIG_ERR) + return 1; + + if (sigprocmask(SIG_UNBLOCK, &new_mask, &old_mask)) + return 1; + + (void) alloca((size_t)n); + + if (sigprocmask(SIG_SETMASK, &old_mask, NULL)) + return 1; + + if (signal(SIGSEGV, Sold_sigsegv_handler) == SIG_ERR) + return 1; + } + + return 0; +} + + +/** + * Check for execution stack overflow. If the stack of + * overflow, or will overflow, the program shall abort. + * + * It is possible that the process is killed by SIGSEGV + * instead of aborting. + * + * This is a Plan 9 from Bell Labs extension. + * + * @etymology I (need) addition (stack) space. + * + * @param n The number of bytes the stack will grow by. + * 0 can be used to check for a current stack + * overflow. Must be non-negative. + * + * @since Always. + */ +void (needstack)(int n) +{ + int tty; + + if (stack_will_overflow((intptr_t)n) == 0) + return; + + tty = isatty(STDERR_FILENO); + fprintf(stderr, + _("%(\033[00;01m%)%s%(\033[00m%): " + "%(\033[31m%)%s%(\033[00m%)" + "%(: %(\033[31m%)hinted stack growth: %i bytes%(\033[00m%)%)\n"), + tty, program_invocation_name, tty, + tty, (n ? _("imminent stack overflow detected") : _("stack overflow detected")), tty, + n != 0, tty, n, tty); + + fflush(NULL); /* Flush all streams. */ + abort(); +} + + +/** + * This function is called if the `needstack` macro is called. + * It enhances the `needstack` function with detail on where + * the program failed. + * + * You should not use this function directly. + * + * @param n The number of bytes the stack will grow by. 0 can be used + * to check for a current stack overflow. Must be non-negative. + * @param file The filename of the source cose whence the check was made. + * @param line The line in the source code whence the check was made. + * @param func The function in the source code whence the check was made, + * `NULL` if unknown (C99 is required.) + */ +void __needstack(int n, const char* file, int line, const char* func) +{ + int tty; + + if (stack_will_overflow((intptr_t)n) == 0) + return; + + tty = isatty(STDERR_FILENO); + fprintf(stderr, + _("%(\033[00;01m%)%s%(\033[00m%): " + "%(\033[31m%)%s%(\033[00m%) " + "at line %(\033[33m%)%i%(\033[00m%) " + "of file %(\033[35m%)%s%(\033[00m%)" + "%(, function %(\033[1;34m%)%s%(\033[00m%)%)" + "%(: %(\033[31m%)hinted stack growth: %i bytes%(\033[00m%)%)\n"), + tty, program_invocation_name, tty, + tty, (n ? _("imminent stack overflow detected") : _("stack overflow detected")), tty, + tty, line, tty, + tty, file, tty, + func != NULL, tty, func, tty, + n != 0, tty, n, tty); + + fflush(NULL); /* Flush all streams. */ + abort(); +} + |