[systemd-devel] [PATCH 1/2 V2] util: add stack_size_guess() and alloca_maybe_safe()

harald at redhat.com harald at redhat.com
Mon Apr 15 09:41:51 PDT 2013


From: Harald Hoyer <harald at 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



More information about the systemd-devel mailing list