From 3d46e3ab9b45d83f69e79a338a45f316e3f61dc0 Mon Sep 17 00:00:00 2001 From: Simon Ruderich Date: Thu, 13 Jun 2013 22:13:16 +0200 Subject: [PATCH 01/16] Add missing const and document where it can't be used. --- src/coloredstderr.c | 4 ++-- src/debug.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coloredstderr.c b/src/coloredstderr.c index 0d76b5a..3fe2053 100644 --- a/src/coloredstderr.c +++ b/src/coloredstderr.c @@ -507,14 +507,14 @@ int execve(char const *filename, char * const argv[], char * const env[]) { /* Count arguments. */ \ size_t count = 1; /* arg */ \ va_start(ap, arg); \ - while (va_arg(ap, char const *)) { \ + while (va_arg(ap, char *)) { \ count++; \ } \ va_end(ap); \ \ /* Copy varargs. */ \ char *args[count + 1 /* terminating NULL */]; \ - args[0] = (char *)arg; \ + args[0] = (char *)arg; /* there's no other way around the cast */ \ \ size_t i = 1; \ va_start(ap, arg); \ diff --git a/src/debug.h b/src/debug.h index 5e5cc6d..911a440 100644 --- a/src/debug.h +++ b/src/debug.h @@ -36,7 +36,7 @@ static void debug_write(int fd, int first_call, char const *format, va_list ap) DLSYM_FUNCTION(real_close, "close"); if (first_call) { - char nl = '\n'; + char const nl = '\n'; real_write(fd, &nl, 1); } real_write(fd, buffer, (size_t)written); -- 2.44.1 From 45d25526c0df734a5eeb926b0e733e19bfbdb8c2 Mon Sep 17 00:00:00 2001 From: Simon Ruderich Date: Thu, 13 Jun 2013 22:15:09 +0200 Subject: [PATCH 02/16] Minor documentation updates. --- src/trackfds.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/trackfds.h b/src/trackfds.h index 70751d7..56490ad 100644 --- a/src/trackfds.h +++ b/src/trackfds.h @@ -100,7 +100,7 @@ static void init_from_environment(void) { #ifdef DEBUG debug(" getenv(\"%s\"): \"%s\"\n", ENV_NAME_FDS, env); #endif - /* Environment is read-only. */ + /* Environment must be treated read-only. */ char env_copy[strlen(env) + 1]; strcpy(env_copy, env); @@ -132,6 +132,7 @@ static void init_from_environment(void) { break; } + /* Replace ',' to null-terminate number for atoi(). */ *x = 0; int fd = atoi(last); @@ -184,7 +185,7 @@ static char *update_environment_buffer_entry(char *x, int fd) { /* Write comma after number. */ x += length; *x++ = ','; - /* Make sure the string is always zero terminated. */ + /* Make sure the string is always null-terminated. */ *x = 0; return x; -- 2.44.1 From 1d1b6d77696e233235d558af9d66bc6edb184ca0 Mon Sep 17 00:00:00 2001 From: Simon Ruderich Date: Thu, 13 Jun 2013 22:15:49 +0200 Subject: [PATCH 03/16] Remove duplicate code in init_from_environment(). --- src/trackfds.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/trackfds.h b/src/trackfds.h index 56490ad..acf7ce3 100644 --- a/src/trackfds.h +++ b/src/trackfds.h @@ -124,8 +124,7 @@ static void init_from_environment(void) { } /* ',' at the beginning or double ',' - ignore. */ if (x == last) { - last = x + 1; - continue; + goto next; } if (i == count) { -- 2.44.1 From d5b84811d2f4bded83211984c839a096872608af Mon Sep 17 00:00:00 2001 From: Simon Ruderich Date: Thu, 13 Jun 2013 22:35:42 +0200 Subject: [PATCH 04/16] tests/lib.sh: Fix tests for dash. --- tests/lib.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/lib.sh b/tests/lib.sh index 897d50e..3427b56 100644 --- a/tests/lib.sh +++ b/tests/lib.sh @@ -105,7 +105,9 @@ run_test() { test_script() { testcase="$1" expected="$2" - shift; shift || true + # shift || true is not enough for dash. + test $# -ge 2 && shift + shift if test -z "$expected"; then expected="$testcase" @@ -118,7 +120,8 @@ test_script_subshell() { test_program() { testcase="$1" expected="$2" - shift; shift || true + test $# -ge 2 && shift + shift if test -z "$expected"; then expected="$testcase" -- 2.44.1 From 208757de8875b3719331024d246e9a7324094993 Mon Sep 17 00:00:00 2001 From: Simon Ruderich Date: Thu, 13 Jun 2013 22:43:43 +0200 Subject: [PATCH 05/16] .gitignore: Ignore generated test files. Forgotten in d8be625da928025e96417c1b62f3336251204cef and 59375c2b7949791d8cad8e5cebde2649bac83ec8. --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 6401ebd..989a240 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,8 @@ /tests/example_exec.o /tests/example_vfork /tests/example_vfork.o +/tests/test_environment.sh.log +/tests/test_environment.sh.trs /tests/test_error.sh.log /tests/test_error.sh.trs /tests/test_example.sh.log @@ -41,6 +43,8 @@ /tests/test_redirects.sh.trs /tests/test_simple.sh.log /tests/test_simple.sh.trs +/tests/test_symbols.sh.log +/tests/test_symbols.sh.trs /tests/test_vfork.sh.log /tests/test_vfork.sh.trs /tests/test-suite.log -- 2.44.1 From 04334418fcce94a1b528bd4c935d8126876bda04 Mon Sep 17 00:00:00 2001 From: Simon Ruderich Date: Fri, 14 Jun 2013 11:07:40 +0200 Subject: [PATCH 06/16] README: Add first part of installation instructions. --- README | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/README b/README index 6bedd74..5e9917a 100644 --- a/README +++ b/README @@ -31,6 +31,27 @@ DEPENDENCIES - dynamic linker/loader which supports 'LD_PRELOAD' (e.g. GNU/Linux's ld.so) +INSTALLATION +------------ + + ./configure && make && make check + +Then either install the library with `make install` or just copy it from +`src/.libs/` to wherever you want to install it: + + rm -f /destination/path/for/library/libcoloredstderr.so + cp -L src/.libs/libcoloredstderr.so /destination/path/for/library/ + +*Important:* If you install `libcoloredstderr.so` manually, make sure _not_ to +use plain `cp` to overwrite an existing `libcoloredstderr.so` file which is in +use! Doing so will crash all processes which were started with 'LD_PRELOAD' +set to this file. This is not a bug in coloredstderr, but a general problem. +`cp` truncates the file which causes the `mmap()` ed library to be in an +inconsistent state causing a segmentation fault when using any functions of +the library. Just remove the file first and then copy it. `make install` +handles the install in this way and is therefore not affected. + + USAGE ----- -- 2.44.1 From dcaadb5f7c530d348d936bf642ada9ef1ea855d1 Mon Sep 17 00:00:00 2001 From: Simon Ruderich Date: Fri, 14 Jun 2013 12:44:34 +0200 Subject: [PATCH 07/16] README: Explain how to modify pre/post variables. --- README | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README b/README index 5e9917a..34cff30 100644 --- a/README +++ b/README @@ -89,6 +89,21 @@ The following additional environment variables are available: are colored. +To set custom colors as pre/post strings you can use the `$''` feature of Bash +and Zsh: + + export COLORED_STDERR_PRE=$'\033[91m' # bright red + export COLORED_STDERR_POST=$'\033[0m' # default + +Or to be more compatible you can use the following which should work in any +Bourne shell: + + esc=`printf '\033'` + COLORED_STDERR_PRE="${esc}[91m" # red + COLORED_STDERR_POST="${esc}[0m" # default + export COLORED_STDERR_PRE COLORED_STDERR_POST + + DEBUG ----- -- 2.44.1 From 18d52b3b5a11b9625a4780bff85ed9dc49af93fc Mon Sep 17 00:00:00 2001 From: Simon Ruderich Date: Fri, 14 Jun 2013 13:40:06 +0200 Subject: [PATCH 08/16] Install libcoloredstderr.so read-only. Prevents accidental truncates of libcoloredstderr.so with `cp` which crash all programs which have loaded it. --- README | 5 +++++ src/Makefile.am | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/README b/README index 34cff30..298ce4f 100644 --- a/README +++ b/README @@ -51,6 +51,11 @@ inconsistent state causing a segmentation fault when using any functions of the library. Just remove the file first and then copy it. `make install` handles the install in this way and is therefore not affected. +As a simple safeguard, `make` builds and installs the `libcoloredstderr.so` +file non-writable to prevent accidental overwrites. Even if the overwrite is +forced with `cp -f`, the file is unlinked and recreated by `cp` because the +file is non-writable, preventing the problem. + USAGE ----- diff --git a/src/Makefile.am b/src/Makefile.am index 1526e1d..46f9289 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -6,3 +6,12 @@ libcoloredstderr_la_SOURCES = coloredstderr.c \ hookmacros.h \ ldpreload.h \ trackfds.h + +# Make sure the library is not writable. See README why this is important. Is +# not run with `make libcoloredstderr.la`, but this isn't common usage. +all-local: $(lib_LTLIBRARIES) + find .libs -type f -exec chmod ugo-w '{}' \; +# `install` uses normal permissions, "fix" them so `make install` also +# installs non-writable files. +install-exec-hook: + find $(DESTDIR)$(libdir) -type f -exec chmod ugo-w '{}' \; -- 2.44.1 From 6ae629bef0e8bd48bc6bb311a9ea12ee462091a2 Mon Sep 17 00:00:00 2001 From: Simon Ruderich Date: Fri, 14 Jun 2013 15:27:31 +0200 Subject: [PATCH 09/16] tests/Makefile.am: Enable parallel and colored test runs. Necessary for automake < 1.13. --- tests/Makefile.am | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/Makefile.am b/tests/Makefile.am index 2a3749f..80d0cdd 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,3 +1,6 @@ +# Default since automake 1.13, necessary for older versions. +AUTOMAKE_OPTIONS = color-tests parallel-tests + TESTS = test_environment.sh \ test_example.sh \ test_exec.sh \ -- 2.44.1 From 9507e0f60ddf054034e1673bd21709aedadee48e Mon Sep 17 00:00:00 2001 From: Simon Ruderich Date: Fri, 14 Jun 2013 15:33:28 +0200 Subject: [PATCH 10/16] configure.ac: Fix Vim's syntax coloring issue with `..'. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 09d90a0..a939d8a 100644 --- a/configure.ac +++ b/configure.ac @@ -50,7 +50,7 @@ AX_C___ATTRIBUTE__ AC_CHECK_MEMBER([struct _IO_FILE._fileno], [AC_DEFINE([HAVE_STRUCT__IO_FILE__FILENO], 1, [Define to 1 if `_fileno' is a member of `struct _IO_FILE'.])], - [],[[#include ]]) + [],[[#include ]]) dnl ' fix for vim syntax coloring AC_FUNC_FORK AC_CHECK_FUNCS([memmove setenv], -- 2.44.1 From 217e8c8bc5fa8c22221514a320d6edeb1c2a101f Mon Sep 17 00:00:00 2001 From: Simon Ruderich Date: Sat, 15 Jun 2013 00:54:46 +0200 Subject: [PATCH 11/16] Define real_*() static variables with macros. --- src/coloredstderr.c | 36 ++++++++++++++++++------------------ src/hookmacros.h | 14 ++++++++++++++ 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/src/coloredstderr.c b/src/coloredstderr.c index 3fe2053..34d3293 100644 --- a/src/coloredstderr.c +++ b/src/coloredstderr.c @@ -336,10 +336,8 @@ void error(int status, int errnum, char const *format, ...) { /* Hook functions which duplicate file descriptors to track them. */ -static int (*real_dup)(int); -static int (*real_dup2)(int, int); -static int (*real_dup3)(int, int, int); -int dup(int oldfd) { +/* int dup(int) */ +HOOK_FUNC_DEF1(int, dup, int, oldfd) { int newfd; DLSYM_FUNCTION(real_dup, "dup"); @@ -351,7 +349,8 @@ int dup(int oldfd) { return newfd; } -int dup2(int oldfd, int newfd) { +/* int dup2(int, int) */ +HOOK_FUNC_DEF2(int, dup2, int, oldfd, int, newfd) { DLSYM_FUNCTION(real_dup2, "dup2"); newfd = real_dup2(oldfd, newfd); @@ -361,7 +360,8 @@ int dup2(int oldfd, int newfd) { return newfd; } -int dup3(int oldfd, int newfd, int flags) { +/* int dup3(int, int, int) */ +HOOK_FUNC_DEF3(int, dup3, int, oldfd, int, newfd, int, flags) { DLSYM_FUNCTION(real_dup3, "dup3"); newfd = real_dup3(oldfd, newfd, flags); @@ -372,8 +372,8 @@ int dup3(int oldfd, int newfd, int flags) { return newfd; } -static int (*real_fcntl)(int, int, ...); -int fcntl(int fd, int cmd, ...) { +/* int fcntl(int, int, ...) */ +HOOK_FUNC_VAR_DEF2(int, fcntl, int, fd, int, cmd /*, ... */) { int result; va_list ap; @@ -404,8 +404,8 @@ int fcntl(int fd, int cmd, ...) { return result; } -static int (*real_close)(int); -int close(int fd) { +/* int close(int) */ +HOOK_FUNC_DEF1(int, close, int, fd) { DLSYM_FUNCTION(real_close, "close"); if (fd >= 0) { @@ -413,8 +413,8 @@ int close(int fd) { } return real_close(fd); } -static int (*real_fclose)(FILE *); -int fclose(FILE *fp) { +/* int fclose(FILE *) */ +HOOK_FUNC_DEF1(int, fclose, FILE *, fp) { int fd; DLSYM_FUNCTION(real_fclose, "fclose"); @@ -451,8 +451,8 @@ pid_t vfork(void) { * ENV_NAME_FDS. It's also faster to update the environment only when * necessary, right before the exec() to pass it to the new process. */ -static int (*real_execve)(char const *filename, char * const argv[], char * const env[]); -int execve(char const *filename, char * const argv[], char * const env[]) { +/* int execve(char const *, char * const [], char * const []) */ +HOOK_FUNC_DEF3(int, execve, char const *, filename, char * const *, argv, char * const *, env) { DLSYM_FUNCTION(real_execve, "execve"); int found = 0; @@ -552,16 +552,16 @@ int execle(char const *path, char const *arg, ... /*, char * const envp[] */) { return execve(path, args, envp); } -static int (*real_execv)(char const *path, char * const argv[]); -int execv(char const *path, char * const argv[]) { +/* int execv(char const *, char * const []) */ +HOOK_FUNC_DEF2(int, execv, char const *, path, char * const *, argv) { DLSYM_FUNCTION(real_execv, "execv"); update_environment(); return real_execv(path, argv); } -static int (*real_execvp)(char const *path, char * const argv[]); -int execvp(char const *file, char * const argv[]) { +/* int execvp(char const *, char * const []) */ +HOOK_FUNC_DEF2(int, execvp, char const *, file, char * const *, argv) { DLSYM_FUNCTION(real_execvp, "execvp"); update_environment(); diff --git a/src/hookmacros.h b/src/hookmacros.h index 42273e4..ebd5f7a 100644 --- a/src/hookmacros.h +++ b/src/hookmacros.h @@ -101,6 +101,20 @@ return result; +#define HOOK_FUNC_DEF1(type, name, type1, arg1) \ + static type (*real_ ## name)(type1); \ + type name(type1 arg1) +#define HOOK_FUNC_DEF2(type, name, type1, arg1, type2, arg2) \ + static type (*real_ ## name)(type1, type2); \ + type name(type1 arg1, type2 arg2) +#define HOOK_FUNC_DEF3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + static type (*real_ ## name)(type1, type2, type3); \ + type name(type1 arg1, type2 arg2, type3 arg3) + +#define HOOK_FUNC_VAR_DEF2(type, name, type1, arg1, type2, arg2) \ + static type (*real_ ## name)(type1, type2, ...); \ + type name(type1 arg1, type2 arg2, ...) + #define HOOK_VOID1(type, name, fd, type1, arg1) \ static type (*real_ ## name)(type1); \ type name(type1 arg1) { \ -- 2.44.1 From 0a909cac79e9d46ad6470b9deab9df9493f66a69 Mon Sep 17 00:00:00 2001 From: Simon Ruderich Date: Sat, 15 Jun 2013 01:58:36 +0200 Subject: [PATCH 12/16] Use locally defined functions in hooks where possible. Don't create a hook and call the "real" function when it's enough to call an existing hook for this function. --- src/hookmacros.h | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/hookmacros.h b/src/hookmacros.h index ebd5f7a..9ee05db 100644 --- a/src/hookmacros.h +++ b/src/hookmacros.h @@ -168,34 +168,28 @@ } #define HOOK_VAR_FILE1(type, name, file, func, type1, arg1) \ - static type (*real_ ## func)(type1, va_list); \ type name(type1 arg1, ...) { \ va_list ap; \ - _HOOK_PRE_FILE(type, func, file) \ va_start(ap, arg1); \ - result = real_ ## func(arg1, ap); \ + type result = func(arg1, ap); \ va_end(ap); \ - _HOOK_POST_FILE(file) \ + return result; \ } #define HOOK_VAR_FILE2(type, name, file, func, type1, arg1, type2, arg2) \ - static type (*real_ ## func)(type1, type2, va_list); \ type name(type1 arg1, type2 arg2, ...) { \ va_list ap; \ - _HOOK_PRE_FILE(type, func, file) \ va_start(ap, arg2); \ - result = real_ ## func(arg1, arg2, ap); \ + type result = func(arg1, arg2, ap); \ va_end(ap); \ - _HOOK_POST_FILE(file) \ + return result; \ } #define HOOK_VAR_FILE3(type, name, file, func, type1, arg1, type2, arg2, type3, arg3) \ - static type (*real_ ## func)(type1, type2, type3, va_list); \ type name(type1 arg1, type2 arg2, type3 arg3, ...) { \ va_list ap; \ - _HOOK_PRE_FILE(type, func, file) \ va_start(ap, arg3); \ - result = real_ ## func(arg1, arg2, arg3, ap); \ + type result = func(arg1, arg2, arg3, ap); \ va_end(ap); \ - _HOOK_POST_FILE(file) \ + return result; \ } #endif -- 2.44.1 From 1068648718dad86471451266fcdf4248bb79f3fe Mon Sep 17 00:00:00 2001 From: Simon Ruderich Date: Sat, 15 Jun 2013 02:21:27 +0200 Subject: [PATCH 13/16] Minor documentation update. --- configure.ac | 2 +- src/coloredstderr.c | 2 +- src/compiler.h | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index a939d8a..0a8f2a8 100644 --- a/configure.ac +++ b/configure.ac @@ -80,7 +80,7 @@ AC_ARG_ENABLE([debug], AC_DEFINE([WARNING], 1) fi]) -dnl Used in tests/Makefile.am to build the test only if error() is available. +dnl Used in tests/Makefile.am to build the test only if function is available. AM_CONDITIONAL([HAVE_ERROR_H],[test "x$ac_cv_header_error_h" = xyes]) AM_CONDITIONAL([HAVE_VFORK],[test "x$ac_cv_func_vfork_works" = xyes]) diff --git a/src/coloredstderr.c b/src/coloredstderr.c index 34d3293..eb9d92c 100644 --- a/src/coloredstderr.c +++ b/src/coloredstderr.c @@ -265,7 +265,7 @@ HOOK_FD2(int, __overflow, f->_fileno, _IO_FILE *, f, int, ch) HOOK_VOID1(void, perror, STDERR_FILENO, char const *, s) -/* error(3) */ +/* error(3), non-standard GNU extension */ #ifdef HAVE_ERROR_H static void error_vararg(int status, int errnum, char const *filename, unsigned int linenum, diff --git a/src/compiler.h b/src/compiler.h index 5ede8b2..85b58b1 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -20,10 +20,11 @@ #ifndef COMPILER_H #define COMPILER_H 1 -/* Prevent/force inlining. Used to improve performance. */ #ifdef HAVE___ATTRIBUTE__ +/* Prevent/force inlining. Used to improve performance. */ # define noinline __attribute__((noinline)) # define always_inline __attribute__((always_inline)) +/* Unused parameter. */ # define unused __attribute__((unused)) #else # define noinline -- 2.44.1 From 941370dda9f22ca42e1d5b3cd580d7e7091d40bc Mon Sep 17 00:00:00 2001 From: Simon Ruderich Date: Sat, 15 Jun 2013 02:39:52 +0200 Subject: [PATCH 14/16] Hook BSD err(), errx(), warn(), warnx(), etc. functions. err(), errx(), warn(), warnx(), verr(), verrx(), vwarn(), vwarnx() are hooked. --- .gitignore | 4 +++ configure.ac | 3 ++- src/coloredstderr.c | 30 ++++++++++++++++++++++ src/hookmacros.h | 32 ++++++++++++++++++++++++ tests/Makefile.am | 9 ++++++- tests/example.h | 38 ++++++++++++++++++++++++++++ tests/example_err.c | 51 ++++++++++++++++++++++++++++++++++++++ tests/example_err.expected | 11 ++++++++ tests/test_err.sh | 23 +++++++++++++++++ 9 files changed, 199 insertions(+), 2 deletions(-) create mode 100644 tests/example.h create mode 100644 tests/example_err.c create mode 100644 tests/example_err.expected create mode 100755 tests/test_err.sh diff --git a/.gitignore b/.gitignore index 989a240..a279991 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,8 @@ /tests/Makefile.in /tests/example /tests/example.o +/tests/example_err +/tests/example_err.o /tests/example_error /tests/example_error.o /tests/example_exec @@ -31,6 +33,8 @@ /tests/example_vfork.o /tests/test_environment.sh.log /tests/test_environment.sh.trs +/tests/test_err.sh.log +/tests/test_err.sh.trs /tests/test_error.sh.log /tests/test_error.sh.trs /tests/test_example.sh.log diff --git a/configure.ac b/configure.ac index 0a8f2a8..a96edb4 100644 --- a/configure.ac +++ b/configure.ac @@ -36,7 +36,7 @@ if test "x$GCC" = xyes; then CFLAGS="-Wall -Wextra -Wconversion $CFLAGS" fi -AC_CHECK_HEADERS([error.h]) +AC_CHECK_HEADERS([err.h error.h]) AC_CHECK_HEADERS([fcntl.h], [],[AC_MSG_ERROR([header is required])]) @@ -81,6 +81,7 @@ AC_ARG_ENABLE([debug], fi]) dnl Used in tests/Makefile.am to build the test only if function is available. +AM_CONDITIONAL([HAVE_ERR_H],[test "x$ac_cv_header_err_h" = xyes]) AM_CONDITIONAL([HAVE_ERROR_H],[test "x$ac_cv_header_error_h" = xyes]) AM_CONDITIONAL([HAVE_VFORK],[test "x$ac_cv_func_vfork_works" = xyes]) diff --git a/src/coloredstderr.c b/src/coloredstderr.c index eb9d92c..247bd06 100644 --- a/src/coloredstderr.c +++ b/src/coloredstderr.c @@ -38,6 +38,9 @@ #include #include +#ifdef HAVE_ERR_H +# include +#endif #ifdef HAVE_ERROR_H # include #endif @@ -265,6 +268,33 @@ HOOK_FD2(int, __overflow, f->_fileno, _IO_FILE *, f, int, ch) HOOK_VOID1(void, perror, STDERR_FILENO, char const *, s) +/* err(3), non standard BSD extension */ +#ifdef HAVE_ERR_H +HOOK_VAR_VOID2(void, err, STDERR_FILENO, verr, + int, eval, char const *, fmt) +HOOK_VAR_VOID2(void, errx, STDERR_FILENO, verrx, + int, eval, char const *, fmt) +HOOK_VAR_VOID1(void, warn, STDERR_FILENO, vwarn, + char const *, fmt) +HOOK_VAR_VOID1(void, warnx, STDERR_FILENO, vwarnx, + char const *, fmt) +HOOK_FUNC_SIMPLE3(void, verr, int, eval, const char *, fmt, va_list, args) { + /* Can't use verr() directly as it terminates the process which prevents + * the post string from being printed. */ + vwarn(fmt, args); + exit(eval); +} +HOOK_FUNC_SIMPLE3(void, verrx, int, eval, const char *, fmt, va_list, args) { + /* See verr(). */ + vwarnx(fmt, args); + exit(eval); +} +HOOK_VOID2(void, vwarn, STDERR_FILENO, + char const *, fmt, va_list, args) +HOOK_VOID2(void, vwarnx, STDERR_FILENO, + char const *, fmt, va_list, args) +#endif + /* error(3), non-standard GNU extension */ #ifdef HAVE_ERROR_H static void error_vararg(int status, int errnum, diff --git a/src/hookmacros.h b/src/hookmacros.h index 9ee05db..18a828e 100644 --- a/src/hookmacros.h +++ b/src/hookmacros.h @@ -115,6 +115,9 @@ static type (*real_ ## name)(type1, type2, ...); \ type name(type1 arg1, type2 arg2, ...) +#define HOOK_FUNC_SIMPLE3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + type name(type1 arg1, type2 arg2, type3 arg3) + #define HOOK_VOID1(type, name, fd, type1, arg1) \ static type (*real_ ## name)(type1); \ type name(type1 arg1) { \ @@ -122,6 +125,35 @@ real_ ## name(arg1); \ _HOOK_POST_FD_(fd) \ } +#define HOOK_VOID2(type, name, fd, type1, arg1, type2, arg2) \ + static type (*real_ ## name)(type1, type2); \ + type name(type1 arg1, type2 arg2) { \ + _HOOK_PRE_FD_(type, name, fd) \ + real_ ## name(arg1, arg2); \ + _HOOK_POST_FD_(fd) \ + } +#define HOOK_VOID3(type, name, fd, type1, arg1, type2, arg2, type3, arg3) \ + static type (*real_ ## name)(type1, type2, type3); \ + type name(type1 arg1, type2 arg2, type3 arg3) { \ + _HOOK_PRE_FD_(type, name, fd) \ + real_ ## name(arg1, arg2, arg3); \ + _HOOK_POST_FD_(fd) \ + } + +#define HOOK_VAR_VOID1(type, name, fd, func, type1, arg1) \ + type name(type1 arg1, ...) { \ + va_list ap; \ + va_start(ap, arg1); \ + func(arg1, ap); \ + va_end(ap); \ + } +#define HOOK_VAR_VOID2(type, name, fd, func, type1, arg1, type2, arg2) \ + type name(type1 arg1, type2 arg2, ...) { \ + va_list ap; \ + va_start(ap, arg2); \ + func(arg1, arg2, ap); \ + va_end(ap); \ + } #define HOOK_FD2(type, name, fd, type1, arg1, type2, arg2) \ static type (*real_ ## name)(type1, type2); \ diff --git a/tests/Makefile.am b/tests/Makefile.am index 80d0cdd..72e822a 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -12,6 +12,11 @@ check_PROGRAMS = example example_exec example_SOURCES = example.c example_exec_SOURCES = example_exec.c +if HAVE_ERR_H + TESTS += test_err.sh + check_PROGRAMS += example_err + example_error_SOURCES = example_err.c +endif if HAVE_ERROR_H TESTS += test_error.sh check_PROGRAMS += example_error @@ -24,9 +29,11 @@ if HAVE_VFORK endif dist_check_SCRIPTS = $(TESTS) lib.sh -dist_check_DATA = example.expected \ +dist_check_DATA = example.h \ + example.expected \ example_environment.expected \ example_environment_empty.expected \ + example_err.expected \ example_error.expected \ example_exec.expected \ example_noforce.sh \ diff --git a/tests/example.h b/tests/example.h new file mode 100644 index 0000000..facbc30 --- /dev/null +++ b/tests/example.h @@ -0,0 +1,38 @@ +/* + * Helper functions/macros for example files. + * + * Copyright (C) 2013 Simon Ruderich + * + * 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 . + */ + + +#define FORKED_TEST(pid) \ + pid = fork(); \ + if (pid == -1) { \ + perror("fork"); \ + exit(EXIT_FAILURE); \ + } else if (pid != 0) { \ + int status; \ + if (waitpid(pid, &status, 0) == -1) { \ + perror("waitpid"); \ + exit(EXIT_FAILURE); \ + } \ + if (WIFEXITED(status)) { \ + printf("exit code: %d\n", WEXITSTATUS(status)); \ + } else { \ + printf("child terminated!\n"); \ + } \ + fflush(stdout); \ + } else diff --git a/tests/example_err.c b/tests/example_err.c new file mode 100644 index 0000000..90072c4 --- /dev/null +++ b/tests/example_err.c @@ -0,0 +1,51 @@ +/* + * Test err(), verr(), ... Non-standard, BSD only. + * + * Copyright (C) 2013 Simon Ruderich + * + * 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 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "../src/compiler.h" +#include "example.h" + + +int main(int argc unused, char **argv unused) { + pid_t pid; + + FORKED_TEST(pid) { errno = ENOMEM; err(0, "error: %s", "message"); } + FORKED_TEST(pid) { errno = ENOMEM; err(1, "error: %s", "message"); } + + FORKED_TEST(pid) { errx(0, "error: %s", "message"); } + FORKED_TEST(pid) { errx(1, "error: %s", "message"); } + + errno = ENOMEM; + warn("warning: %s", "message"); + warnx("warning: %s", "message"); + + /* v*() functions are implicitly tested - the implementation uses them. */ + + printf("\n"); + return EXIT_SUCCESS; +} diff --git a/tests/example_err.expected b/tests/example_err.expected new file mode 100644 index 0000000..652cd81 --- /dev/null +++ b/tests/example_err.expected @@ -0,0 +1,11 @@ +>STDERR>example_err: error: message: Cannot allocate memory +STDERR>example_err: error: message: Cannot allocate memory +STDERR>example_err: error: message +STDERR>example_err: error: message +STDERR>example_err: warning: message: Cannot allocate memory +STDERR>example_err: warning: message +. + + +test "x$srcdir" = x && srcdir=. +. "$srcdir/lib.sh" + +test_program example_err +test_program_subshell example_err -- 2.44.1 From ca12529c8f3880c008212e97cf6b4b7d6981dec8 Mon Sep 17 00:00:00 2001 From: Simon Ruderich Date: Sat, 15 Jun 2013 03:23:53 +0200 Subject: [PATCH 15/16] hookmacros.h: Reduce duplication in macros. Also preparation to add attributes to function declarations. --- src/hookmacros.h | 47 ++++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/src/hookmacros.h b/src/hookmacros.h index 18a828e..8a05811 100644 --- a/src/hookmacros.h +++ b/src/hookmacros.h @@ -110,6 +110,9 @@ #define HOOK_FUNC_DEF3(type, name, type1, arg1, type2, arg2, type3, arg3) \ static type (*real_ ## name)(type1, type2, type3); \ type name(type1 arg1, type2 arg2, type3 arg3) +#define HOOK_FUNC_DEF4(type, name, type1, arg1, type2, arg2, type3, arg3, type4, arg4) \ + static type (*real_ ## name)(type1, type2, type3, type4); \ + type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4) #define HOOK_FUNC_VAR_DEF2(type, name, type1, arg1, type2, arg2) \ static type (*real_ ## name)(type1, type2, ...); \ @@ -118,37 +121,41 @@ #define HOOK_FUNC_SIMPLE3(type, name, type1, arg1, type2, arg2, type3, arg3) \ type name(type1 arg1, type2 arg2, type3 arg3) +#define HOOK_FUNC_VAR_SIMPLE1(type, name, type1, arg1) \ + type name(type1 arg1, ...) +#define HOOK_FUNC_VAR_SIMPLE2(type, name, type1, arg1, type2, arg2) \ + type name(type1 arg1, type2 arg2, ...) +#define HOOK_FUNC_VAR_SIMPLE3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + type name(type1 arg1, type2 arg2, type3 arg3, ...) + #define HOOK_VOID1(type, name, fd, type1, arg1) \ - static type (*real_ ## name)(type1); \ - type name(type1 arg1) { \ + HOOK_FUNC_DEF1(type, name, type1, arg1) { \ _HOOK_PRE_FD_(type, name, fd) \ real_ ## name(arg1); \ _HOOK_POST_FD_(fd) \ } #define HOOK_VOID2(type, name, fd, type1, arg1, type2, arg2) \ - static type (*real_ ## name)(type1, type2); \ - type name(type1 arg1, type2 arg2) { \ + HOOK_FUNC_DEF2(type, name, type1, arg1, type2, arg2) { \ _HOOK_PRE_FD_(type, name, fd) \ real_ ## name(arg1, arg2); \ _HOOK_POST_FD_(fd) \ } #define HOOK_VOID3(type, name, fd, type1, arg1, type2, arg2, type3, arg3) \ - static type (*real_ ## name)(type1, type2, type3); \ - type name(type1 arg1, type2 arg2, type3 arg3) { \ + HOOK_FUNC_DEF3(type, name, type1, arg1, type2, arg2, type3, arg3) { \ _HOOK_PRE_FD_(type, name, fd) \ real_ ## name(arg1, arg2, arg3); \ _HOOK_POST_FD_(fd) \ } #define HOOK_VAR_VOID1(type, name, fd, func, type1, arg1) \ - type name(type1 arg1, ...) { \ + HOOK_FUNC_VAR_SIMPLE1(type, name, type1, arg1) { \ va_list ap; \ va_start(ap, arg1); \ func(arg1, ap); \ va_end(ap); \ } #define HOOK_VAR_VOID2(type, name, fd, func, type1, arg1, type2, arg2) \ - type name(type1 arg1, type2 arg2, ...) { \ + HOOK_FUNC_VAR_SIMPLE2(type, name, type1, arg1, type2, arg2) { \ va_list ap; \ va_start(ap, arg2); \ func(arg1, arg2, ap); \ @@ -156,51 +163,45 @@ } #define HOOK_FD2(type, name, fd, type1, arg1, type2, arg2) \ - static type (*real_ ## name)(type1, type2); \ - type name(type1 arg1, type2 arg2) { \ + HOOK_FUNC_DEF2(type, name, type1, arg1, type2, arg2) { \ _HOOK_PRE_FD(type, name, fd) \ result = real_ ## name(arg1, arg2); \ _HOOK_POST_FD(fd) \ } #define HOOK_FD3(type, name, fd, type1, arg1, type2, arg2, type3, arg3) \ - static type (*real_ ## name)(type1, type2, type3); \ - type name(type1 arg1, type2 arg2, type3 arg3) { \ + HOOK_FUNC_DEF3(type, name, type1, arg1, type2, arg2, type3, arg3) { \ _HOOK_PRE_FD(type, name, fd) \ result = real_ ## name(arg1, arg2, arg3); \ _HOOK_POST_FD(fd) \ } #define HOOK_FILE1(type, name, file, type1, arg1) \ - static type (*real_ ## name)(type1); \ - type name(type1 arg1) { \ + HOOK_FUNC_DEF1(type, name, type1, arg1) { \ _HOOK_PRE_FILE(type, name, file) \ result = real_ ## name(arg1); \ _HOOK_POST_FILE(file) \ } #define HOOK_FILE2(type, name, file, type1, arg1, type2, arg2) \ - static type (*real_ ## name)(type1, type2); \ - type name(type1 arg1, type2 arg2) { \ + HOOK_FUNC_DEF2(type, name, type1, arg1, type2, arg2) { \ _HOOK_PRE_FILE(type, name, file) \ result = real_ ## name(arg1, arg2); \ _HOOK_POST_FILE(file) \ } #define HOOK_FILE3(type, name, file, type1, arg1, type2, arg2, type3, arg3) \ - static type (*real_ ## name)(type1, type2, type3); \ - type name(type1 arg1, type2 arg2, type3 arg3) { \ + HOOK_FUNC_DEF3(type, name, type1, arg1, type2, arg2, type3, arg3) { \ _HOOK_PRE_FILE(type, name, file) \ result = real_ ## name(arg1, arg2, arg3); \ _HOOK_POST_FILE(file) \ } #define HOOK_FILE4(type, name, file, type1, arg1, type2, arg2, type3, arg3, type4, arg4) \ - static type (*real_ ## name)(type1, type2, type3, type4); \ - type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + HOOK_FUNC_DEF4(type, name, type1, arg1, type2, arg2, type3, arg3, type4, arg4) { \ _HOOK_PRE_FILE(type, name, file) \ result = real_ ## name(arg1, arg2, arg3, arg4); \ _HOOK_POST_FILE(file) \ } #define HOOK_VAR_FILE1(type, name, file, func, type1, arg1) \ - type name(type1 arg1, ...) { \ + HOOK_FUNC_VAR_SIMPLE1(type, name, type1, arg1) { \ va_list ap; \ va_start(ap, arg1); \ type result = func(arg1, ap); \ @@ -208,7 +209,7 @@ return result; \ } #define HOOK_VAR_FILE2(type, name, file, func, type1, arg1, type2, arg2) \ - type name(type1 arg1, type2 arg2, ...) { \ + HOOK_FUNC_VAR_SIMPLE2(type, name, type1, arg1, type2, arg2) { \ va_list ap; \ va_start(ap, arg2); \ type result = func(arg1, arg2, ap); \ @@ -216,7 +217,7 @@ return result; \ } #define HOOK_VAR_FILE3(type, name, file, func, type1, arg1, type2, arg2, type3, arg3) \ - type name(type1 arg1, type2 arg2, type3 arg3, ...) { \ + HOOK_FUNC_VAR_SIMPLE3(type, name, type1, arg1, type2, arg2, type3, arg3) { \ va_list ap; \ va_start(ap, arg3); \ type result = func(arg1, arg2, arg3, ap); \ -- 2.44.1 From a58d1a9017a1a15a237f5b516fe2c44a0f01482e Mon Sep 17 00:00:00 2001 From: Simon Ruderich Date: Sat, 15 Jun 2013 03:36:50 +0200 Subject: [PATCH 16/16] Mark hooked functions as visibility(("protected")). --- src/compiler.h | 9 +++++++++ src/hookmacros.h | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/src/compiler.h b/src/compiler.h index 85b58b1..d1c12dc 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -26,10 +26,19 @@ # define always_inline __attribute__((always_inline)) /* Unused parameter. */ # define unused __attribute__((unused)) +/* Mark the function protected, which means it can't be overwritten by other + * modules (libraries), e.g. with LD_PRELOAD); otherwise same as the default + * visibility. This causes the compiler not use the PLT (and no relocations) + * for local calls from inside this module; but the symbols are still + * exported. This is faster and in this case prevents useless lookups as we + * hook those functions and nobody else should modify them. Not strictly + * necessary, but nice to have. */ +# define visibility_protected __attribute__((visibility("protected"))) #else # define noinline # define always_inline # define unused +# define visibility_protected #endif /* Branch prediction information for the compiler. */ diff --git a/src/hookmacros.h b/src/hookmacros.h index 8a05811..539d0d9 100644 --- a/src/hookmacros.h +++ b/src/hookmacros.h @@ -103,29 +103,38 @@ #define HOOK_FUNC_DEF1(type, name, type1, arg1) \ static type (*real_ ## name)(type1); \ + type name(type1) visibility_protected; \ type name(type1 arg1) #define HOOK_FUNC_DEF2(type, name, type1, arg1, type2, arg2) \ static type (*real_ ## name)(type1, type2); \ + type name(type1, type2) visibility_protected; \ type name(type1 arg1, type2 arg2) #define HOOK_FUNC_DEF3(type, name, type1, arg1, type2, arg2, type3, arg3) \ static type (*real_ ## name)(type1, type2, type3); \ + type name(type1, type2, type3) visibility_protected; \ type name(type1 arg1, type2 arg2, type3 arg3) #define HOOK_FUNC_DEF4(type, name, type1, arg1, type2, arg2, type3, arg3, type4, arg4) \ static type (*real_ ## name)(type1, type2, type3, type4); \ + type name(type1, type2, type3, type4) visibility_protected; \ type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4) #define HOOK_FUNC_VAR_DEF2(type, name, type1, arg1, type2, arg2) \ static type (*real_ ## name)(type1, type2, ...); \ + type name(type1, type2, ...) visibility_protected; \ type name(type1 arg1, type2 arg2, ...) #define HOOK_FUNC_SIMPLE3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + type name(type1, type2, type3) visibility_protected; \ type name(type1 arg1, type2 arg2, type3 arg3) #define HOOK_FUNC_VAR_SIMPLE1(type, name, type1, arg1) \ + type name(type1, ...) visibility_protected; \ type name(type1 arg1, ...) #define HOOK_FUNC_VAR_SIMPLE2(type, name, type1, arg1, type2, arg2) \ + type name(type1, type2, ...) visibility_protected; \ type name(type1 arg1, type2 arg2, ...) #define HOOK_FUNC_VAR_SIMPLE3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + type name(type1, type2, type3, ...) visibility_protected; \ type name(type1 arg1, type2 arg2, type3 arg3, ...) #define HOOK_VOID1(type, name, fd, type1, arg1) \ -- 2.44.1