[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