From: Harald Hoyer <har...@redhat.com> stack_size_guess() guesses the remaining stack size in bytes.
alloca_maybe_safe() returns NULL, if the allocation would exceed the remaining stack. --- V2: added missing src/shared/test-stack.c CODING_STYLE | 7 +++++++ Makefile.am | 7 +++++++ configure.ac | 35 ++++++++++++++++++++++++++++++++ src/shared/test-stack.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++ src/shared/util.c | 47 +++++++++++++++++++++++++++++++++++++++++++ src/shared/util.h | 8 ++++++++ 6 files changed, 157 insertions(+) create mode 100644 src/shared/test-stack.c diff --git a/CODING_STYLE b/CODING_STYLE index 4b84479..2da76de 100644 --- a/CODING_STYLE +++ b/CODING_STYLE @@ -47,6 +47,13 @@ string can have. Or in other words, if you use "char buf[256]" then you are likely doing something wrong! +- Instead of using alloca() consider using alloca_maybe_safe(), which + returns a NULL pointer, if you are short of stack memory. + +- To guess the stack memory left, you can use stack_size_guess(), which + tries to guess the memory left on the stack. Better do not use all of + the memory, but leave some page_size() bytes left. + - Stay uniform. For example, always use "usec_t" for time values. Don't usec mix msec, and usec and whatnot. diff --git a/Makefile.am b/Makefile.am index 5a632a9..5723094 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2629,6 +2629,12 @@ test_journal_send_LDADD = \ libsystemd-journal-internal.la \ libsystemd-id128-internal.la +test_stack_SOURCES = \ + src/shared/test-stack.c + +test_stack_LDADD = \ + libsystemd-shared.la + test_journal_syslog_SOURCES = \ src/journal/test-journal-syslog.c @@ -2845,6 +2851,7 @@ noinst_tests += \ test-journal-stream \ test-journal-verify \ test-mmap-cache \ + test-stack \ test-catalog pkginclude_HEADERS += \ diff --git a/configure.ac b/configure.ac index 33b0ca9..ee784ba 100644 --- a/configure.ac +++ b/configure.ac @@ -778,6 +778,41 @@ AM_CONDITIONAL(ENABLE_MANPAGES, [test "x$have_manpages" = "xyes"]) # ------------------------------------------------------------------------------ +AC_CACHE_CHECK([stack direction for C alloca], + [ac_cv_c_stack_direction], +[AC_RUN_IFELSE([AC_LANG_SOURCE( +[AC_INCLUDES_DEFAULT +int +find_stack_direction (int *addr, int depth) +{ + int dir, dummy = 0; + if (! addr) + addr = &dummy; + *addr = addr < &dummy ? 1 : addr == &dummy ? 0 : -1; + dir = depth ? find_stack_direction (addr, depth - 1) : 0; + return dir + dummy; +} + +int +main (int argc, char **argv) +{ + return find_stack_direction (0, argc + !argv + 20) < 0; +}])], + [ac_cv_c_stack_direction=1], + [ac_cv_c_stack_direction=-1], + [ac_cv_c_stack_direction=0])]) +AH_VERBATIM([STACK_DIRECTION], +[/* If using the C implementation of alloca, define if you know the + direction of stack growth for your system; otherwise it will be + automatically deduced at runtime. + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => direction of growth unknown */ +@%:@undef STACK_DIRECTION])dnl +AC_DEFINE_UNQUOTED(STACK_DIRECTION, $ac_cv_c_stack_direction) + +# ------------------------------------------------------------------------------ + # Location of the init scripts as mandated by LSB SYSTEM_SYSVINIT_PATH=/etc/init.d SYSTEM_SYSVRCND_PATH=/etc/rc.d diff --git a/src/shared/test-stack.c b/src/shared/test-stack.c new file mode 100644 index 0000000..87b9102 --- /dev/null +++ b/src/shared/test-stack.c @@ -0,0 +1,53 @@ +#include <stdio.h> +#include <unistd.h> +#include <sys/resource.h> +#include <locale.h> +#include <langinfo.h> + +#include "macro.h" +#include "util.h" + +#define BYTES 128 + +static void test(void) +{ + char huge[random() % BYTES + 20]; + rlim_t ss; + unsigned long len; + ss = stack_size_guess(); + + snprintf(huge, BYTES, "Stack size: %ld\n", (unsigned long)ss); + huge[BYTES-1]=0; + len = strlen(huge); + + if (ss > (BYTES + page_size())) + test(); + else { + write(2, huge, len); + fflush(stderr); + } +} + +int main(int argc, char *argv[]) +{ + int i; + struct rlimit rlim; + int r; + + r = getrlimit(RLIMIT_STACK, &rlim); + if (r) { + perror("getrlimit"); + return 1; + } + + rlim.rlim_cur = sysconf(_SC_PAGESIZE) * 10; + r = setrlimit(RLIMIT_STACK, &rlim); + if (r) { + perror("setrlimit"); + return 1; + } + srandom(time(NULL)); + for (i = 0; i < 100; i++) + test(); + return 0; +} diff --git a/src/shared/util.c b/src/shared/util.c index 5d6995d..7f9f22d 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -104,6 +104,53 @@ size_t page_size(void) { return pgsz; } +#if (STACK_DIRECTION == 0) +rlim_t stack_size_guess(void) { + return RLIM_INFINITY; +} +#else +rlim_t stack_size_guess(void) +{ + rlim_t ep = (rlim_t) &ep; + PROTECT_ERRNO; + int r; + rlim_t stack_size; + struct rlimit rlim; + rlim_t sp = 0; + + /* guard against manipulation of program_invocation_name or environ */ +#if (STACK_DIRECTION < 0) + sp = MAX(PAGE_ALIGN((rlim_t) program_invocation_name), PAGE_ALIGN((rlim_t) environ)); +#else + sp = MIN(PAGE_ALIGN((rlim_t) program_invocation_name), PAGE_ALIGN((rlim_t) environ)); +#endif + + /* s.th. is wrong, bail out */ +#if (STACK_DIRECTION < 0) + if (ep > sp) + return RLIM_INFINITY; +#else + if (ep < sp) + return RLIM_INFINITY; +#endif + + r = getrlimit(RLIMIT_STACK, &rlim); + + if (r < 0 || rlim.rlim_cur == RLIM_INFINITY) + return RLIM_INFINITY; + + /* save one page_size() for emergency usage */ + rlim.rlim_cur -= page_size(); + +#if (STACK_DIRECTION < 0) + stack_size = sp - ep; +#else + stack_size = ep - sp; +#endif + return (rlim.rlim_cur > stack_size) ? rlim.rlim_cur - stack_size : 0; +} +#endif + bool streq_ptr(const char *a, const char *b) { /* Like streq(), but tries to make sense of NULL pointers */ diff --git a/src/shared/util.h b/src/shared/util.h index b33fdb5..6fc32af 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -68,6 +68,8 @@ union dirent_storage { size_t page_size(void); #define PAGE_ALIGN(l) ALIGN_TO((l), page_size()) +rlim_t stack_size_guess(void); + #define streq(a,b) (strcmp((a),(b)) == 0) #define strneq(a, b, n) (strncmp((a), (b), (n)) == 0) #define strcaseeq(a,b) (strcasecmp((a),(b)) == 0) @@ -85,6 +87,12 @@ bool streq_ptr(const char *a, const char *b); #define malloc0(n) (calloc((n), 1)) +#define alloca_maybe_safe(a) \ + __extension__ ({ \ + typeof(a) _a = (a); \ + stack_size_guess() > _a ? alloca(_a) : NULL; \ + }) + static inline const char* yes_no(bool b) { return b ? "yes" : "no"; } -- 1.8.2 _______________________________________________ systemd-devel mailing list systemd-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/systemd-devel