Mercurial > dovecot > core-2.2
view src/lib/process-title.c @ 19595:d993ed368ee0
lib: Don't crash if getenv() is called after process_title_deinit() with Linux/OSX PROCTITLE_HACK
Right now the only known issue is GNUTLS's lib_deinit():
e = getenv("GNUTLS_NO_EXPLICIT_INIT");
But we likely never want that to be set. Alternative ways to solve this are
more annoying than our clearing the environment. Libraries probably shouldn't
rely on it much anyway, because some parts of Dovecot clear the whole
environment while running.
author | Timo Sirainen <timo.sirainen@dovecot.fi> |
---|---|
date | Tue, 19 Jan 2016 20:45:27 +0200 |
parents | 0f22db71df7a |
children | 2e2563132d5f |
line wrap: on
line source
/* Copyright (c) 2002-2016 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "env-util.h" #include "process-title.h" #include <unistd.h> /* FreeBSD */ static char *process_name = NULL; #ifdef HAVE_SETPROCTITLE # undef PROCTITLE_HACK #endif #ifdef PROCTITLE_HACK #define PROCTITLE_CLEAR_CHAR 0xab static char *process_title; static size_t process_title_len, process_title_clean_pos; static void *argv_memblock, *environ_memblock; static void proctitle_hack_init(char *argv[], char *env[]) { char *last; unsigned int i; bool clear_env; i_assert(argv[0] != NULL); /* find the last argv or environment string. it should always be the last string in environ, but don't rely on it. this is what openssh does, so hopefully it's safe enough. */ last = argv[0] + strlen(argv[0]) + 1; for (i = 1; argv[i] != NULL; i++) { if (argv[i] == last) last = argv[i] + strlen(argv[i]) + 1; } if (env[0] == NULL) clear_env = FALSE; else { clear_env = last == env[0]; for (i = 0; env[i] != NULL; i++) { if (env[i] == last) last = env[i] + strlen(env[i]) + 1; } } process_title = argv[0]; process_title_len = last - argv[0]; /* if there are problems with this approach, try to make sure we notice it */ if (clear_env) { memset(env[0], PROCTITLE_CLEAR_CHAR, last - env[0]); process_title_clean_pos = env[0] - process_title; } else { process_title_clean_pos = 0; } } static char **argv_dup(char *old_argv[], void **memblock_r) { /* @UNSAFE */ void *memblock, *memblock_end; char **new_argv; unsigned int i, count; size_t len, memblock_len = 0; for (count = 0; old_argv[count] != NULL; count++) memblock_len += strlen(old_argv[count]) + 1; memblock_len += sizeof(char *) * (count + 1); memblock = malloc(memblock_len); if (memblock == NULL) i_fatal_status(FATAL_OUTOFMEM, "malloc() failed: %m"); *memblock_r = memblock; memblock_end = PTR_OFFSET(memblock, memblock_len); new_argv = memblock; memblock = PTR_OFFSET(memblock, sizeof(char *) * (count + 1)); for (i = 0; i < count; i++) { new_argv[i] = memblock; len = strlen(old_argv[i]) + 1; memcpy(memblock, old_argv[i], len); memblock = PTR_OFFSET(memblock, len); } i_assert(memblock == memblock_end); new_argv[i] = NULL; return new_argv; } static void proctitle_hack_set(const char *title) { size_t len = strlen(title); /* OS X wants two NULs */ if (len >= process_title_len-1) len = process_title_len - 2; memcpy(process_title, title, len); process_title[len++] = '\0'; process_title[len++] = '\0'; if (len < process_title_clean_pos) { memset(process_title + len, PROCTITLE_CLEAR_CHAR, process_title_clean_pos - len); process_title_clean_pos = len; } else if (process_title_clean_pos != 0) { process_title_clean_pos = len; } } #endif void process_title_init(char **argv[]) { #ifdef PROCTITLE_HACK char ***environ_p = env_get_environ_p(); char **orig_argv = *argv; char **orig_environ = *environ_p; *argv = argv_dup(orig_argv, &argv_memblock); *environ_p = argv_dup(orig_environ, &environ_memblock); proctitle_hack_init(orig_argv, orig_environ); #endif process_name = (*argv)[0]; } void process_title_set(const char *title ATTR_UNUSED) { i_assert(process_name != NULL); #ifdef HAVE_SETPROCTITLE if (title == NULL) setproctitle(NULL); else setproctitle("%s", title); #elif defined(PROCTITLE_HACK) T_BEGIN { proctitle_hack_set(t_strconcat(process_name, " ", title, NULL)); } T_END; #endif } void process_title_deinit(void) { #ifdef PROCTITLE_HACK char ***environ_p = env_get_environ_p(); free(argv_memblock); free(environ_memblock); /* Environment is no longer usable. Make sure we won't crash in case some library's deinit function still calls getenv(). This code was mainly added because of GNUTLS where we don't really care about the getenv() call. Alternatively we could remove the free() calls above, but that would annoy memory leak checking tools. Also we could attempt to restore the environ_p to its original state, but that's a bit complicated. */ *environ_p = NULL; #endif }