commit d45e4f2329e2630a9471ea72a48e9c77556100f1 Author: Yawning Angel yawning@schwanenlied.me Date: Sun Apr 5 06:55:11 2015 +0000
Ensure that torsocks initializes itself in the presence of C++.
Fun fact, `__attribute__(constructor)` functions aren't guaranteed to be called before C++ static object constructors. This change ensures that initialization will always be called once (and only once), as needed even if C++ codebases chose to use hijacked symbols in ctors for static objects.
Things that got changed: * Added `tsocks_once()` that is a functional replica of pthread_once, but doesn't require linking in a real pthread library if the application does not. * Changed the initialization/cleanup to use tsocks_once. * Call the initialization from within all of the hijacked libc calls.
Fixes bug #15584.
Signed-off-by: Yawning Angel yawning@schwanenlied.me Signed-off-by: David Goulet dgoulet@ev0ke.net --- src/common/compat.c | 25 +++++++++++++++++++++++++ src/common/compat.h | 14 +++++++++++++- src/common/connection.c | 11 +---------- src/common/connection.h | 1 - src/lib/accept.c | 2 ++ src/lib/close.c | 3 +++ src/lib/connect.c | 2 ++ src/lib/fclose.c | 5 ++++- src/lib/getaddrinfo.c | 1 + src/lib/gethostbyname.c | 6 ++++++ src/lib/getpeername.c | 1 + src/lib/listen.c | 1 + src/lib/recv.c | 2 +- src/lib/sendto.c | 1 + src/lib/socket.c | 2 ++ src/lib/socketpair.c | 2 +- src/lib/syscall.c | 1 + src/lib/torsocks.c | 42 ++++++++++++++++++++++-------------------- src/lib/torsocks.h | 1 + 19 files changed, 88 insertions(+), 35 deletions(-)
diff --git a/src/common/compat.c b/src/common/compat.c index 7d8431d..a861b3d 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -71,4 +71,29 @@ void tsocks_mutex_unlock(tsocks_mutex_t *m) assert(!ret); }
+/* + * Call the given routine once, and only once. tsocks_once returning + * guarantees that the routine has succeded. + */ +void tsocks_once(tsocks_once_t *o, void (*init_routine)(void)) +{ + + /* Why, yes, pthread_once(3P) exists. Said routine requires linking in a + * real pthread library on Linux, while this does not and will do the right + * thing even with the stub implementation. */ + assert(o); + + /* This looks scary and incorrect, till you realize that the + * pthread_mutex routines include memory barriers. */ + if (!o->once) { + return; + } + tsocks_mutex_lock(&o->mutex); + if (o->once) { + init_routine(); + o->once = 0; + } + tsocks_mutex_unlock(&o->mutex); +} + #endif /* __GLIBC__, __darwin__, __FreeBSD__, __NetBSD__ */ diff --git a/src/common/compat.h b/src/common/compat.h index e7e5812..ce47129 100644 --- a/src/common/compat.h +++ b/src/common/compat.h @@ -33,14 +33,26 @@ typedef struct tsocks_mutex_t { } tsocks_mutex_t;
/* Define a tsock mutex variable with the mutex statically initialized. */ +#define TSOCKS_MUTEX_INIT { .mutex = PTHREAD_MUTEX_INITIALIZER } #define TSOCKS_INIT_MUTEX(name) \ - tsocks_mutex_t name = { .mutex = PTHREAD_MUTEX_INITIALIZER } + tsocks_mutex_t name = TSOCKS_MUTEX_INIT
void tsocks_mutex_init(tsocks_mutex_t *m); void tsocks_mutex_destroy(tsocks_mutex_t *m); void tsocks_mutex_lock(tsocks_mutex_t *m); void tsocks_mutex_unlock(tsocks_mutex_t *m);
+typedef struct tsocks_once_t { + int once:1; + tsocks_mutex_t mutex; +} tsocks_once_t; + +/* Define a tsock once variable, statically initialized. */ +#define TSOCKS_INIT_ONCE(name) \ + tsocks_once_t name = { .once = 1, .mutex = TSOCKS_MUTEX_INIT } + +void tsocks_once(tsocks_once_t *o, void (*init_routine)(void)); + #else #error "OS not supported." #endif /* __GLIBC__, __darwin__, __FreeBSD__, __NetBSD__ */ diff --git a/src/common/connection.c b/src/common/connection.c index 4c75579..d4ae722 100644 --- a/src/common/connection.c +++ b/src/common/connection.c @@ -82,7 +82,7 @@ static inline unsigned int conn_hash_fct(struct connection *c) /* * Declare the connection registry. */ -static HT_HEAD(connection_registry, connection) connection_registry_root; +static HT_HEAD(connection_registry, connection) connection_registry_root = HT_INITIALIZER(); HT_PROTOTYPE(connection_registry, connection, node, conn_hash_fct, conn_equal_fct); HT_GENERATE(connection_registry, connection, node, conn_hash_fct, @@ -107,15 +107,6 @@ void connection_registry_unlock(void) }
/* - * Initialize connection registry. - */ -ATTR_HIDDEN -void connection_registry_init(void) -{ - HT_INIT(connection_registry, &connection_registry_root); -} - -/* * Set an already allocated connection address using the given IPv4/6 address, * domain and port. * diff --git a/src/common/connection.h b/src/common/connection.h index 379f158..3c958b7 100644 --- a/src/common/connection.h +++ b/src/common/connection.h @@ -81,7 +81,6 @@ void connection_destroy(struct connection *conn); void connection_remove(struct connection *conn); void connection_insert(struct connection *conn);
-void connection_registry_init(void); void connection_registry_lock(void); void connection_registry_unlock(void);
diff --git a/src/lib/accept.c b/src/lib/accept.c index e9bc36c..21714cb 100644 --- a/src/lib/accept.c +++ b/src/lib/accept.c @@ -85,6 +85,7 @@ error: LIBC_ACCEPT_DECL { if (!tsocks_libc_accept) { + tsocks_initialize(); tsocks_libc_accept = tsocks_find_libc_symbol( LIBC_ACCEPT_NAME_STR, TSOCKS_SYM_EXIT_NOT_FOUND); } @@ -158,6 +159,7 @@ error: LIBC_ACCEPT4_DECL { if (!tsocks_libc_accept4) { + tsocks_initialize(); tsocks_libc_accept4 = tsocks_find_libc_symbol( LIBC_ACCEPT4_NAME_STR, TSOCKS_SYM_EXIT_NOT_FOUND); } diff --git a/src/lib/close.c b/src/lib/close.c index d774c85..7fb9e53 100644 --- a/src/lib/close.c +++ b/src/lib/close.c @@ -61,5 +61,8 @@ LIBC_CLOSE_RET_TYPE tsocks_close(LIBC_CLOSE_SIG) */ LIBC_CLOSE_DECL { + if (!tsocks_libc_close) { + tsocks_initialize(); + } return tsocks_close(LIBC_CLOSE_ARGS); } diff --git a/src/lib/connect.c b/src/lib/connect.c index 1bf81ac..2480643 100644 --- a/src/lib/connect.c +++ b/src/lib/connect.c @@ -231,5 +231,7 @@ error: */ LIBC_CONNECT_DECL { + if (!tsocks_libc_connect) + tsocks_initialize(); return tsocks_connect(LIBC_CONNECT_ARGS); } diff --git a/src/lib/fclose.c b/src/lib/fclose.c index 8d371cd..b6f3251 100644 --- a/src/lib/fclose.c +++ b/src/lib/fclose.c @@ -76,10 +76,13 @@ error: */ LIBC_FCLOSE_DECL { + /* fclose(3) is unique in that it does not call torsocks_initialize(), as + * it is used from within the initialization routine to close the config + * file/log file. This would be a problem, except that all of the global + * state it depends on is statically initialized. */ if (!tsocks_libc_fclose) { tsocks_libc_fclose = tsocks_find_libc_symbol( LIBC_FCLOSE_NAME_STR, TSOCKS_SYM_EXIT_NOT_FOUND); } - return tsocks_fclose(LIBC_FCLOSE_ARGS); } diff --git a/src/lib/getaddrinfo.c b/src/lib/getaddrinfo.c index 0f7fd4f..846eddb 100644 --- a/src/lib/getaddrinfo.c +++ b/src/lib/getaddrinfo.c @@ -128,6 +128,7 @@ error: LIBC_GETADDRINFO_DECL { if (!tsocks_libc_getaddrinfo) { + tsocks_initialize(); tsocks_libc_getaddrinfo = tsocks_find_libc_symbol( LIBC_GETADDRINFO_NAME_STR, TSOCKS_SYM_EXIT_NOT_FOUND); } diff --git a/src/lib/gethostbyname.c b/src/lib/gethostbyname.c index 322f398..93a3483 100644 --- a/src/lib/gethostbyname.c +++ b/src/lib/gethostbyname.c @@ -109,6 +109,7 @@ error: */ LIBC_GETHOSTBYNAME_DECL { + tsocks_initialize(); return tsocks_gethostbyname(LIBC_GETHOSTBYNAME_ARGS); }
@@ -137,6 +138,7 @@ LIBC_GETHOSTBYNAME2_RET_TYPE tsocks_gethostbyname2(LIBC_GETHOSTBYNAME2_SIG) */ LIBC_GETHOSTBYNAME2_DECL { + tsocks_initialize(); return tsocks_gethostbyname2(LIBC_GETHOSTBYNAME2_ARGS); }
@@ -202,6 +204,7 @@ error: */ LIBC_GETHOSTBYADDR_DECL { + tsocks_initialize(); return tsocks_gethostbyaddr(LIBC_GETHOSTBYADDR_ARGS); }
@@ -305,6 +308,7 @@ error: */ LIBC_GETHOSTBYADDR_R_DECL { + tsocks_initialize(); return tsocks_gethostbyaddr_r(LIBC_GETHOSTBYADDR_R_ARGS); }
@@ -380,6 +384,7 @@ error: */ LIBC_GETHOSTBYNAME_R_DECL { + tsocks_initialize(); return tsocks_gethostbyname_r(LIBC_GETHOSTBYNAME_R_ARGS); }
@@ -410,5 +415,6 @@ LIBC_GETHOSTBYNAME2_R_RET_TYPE tsocks_gethostbyname2_r(LIBC_GETHOSTBYNAME2_R_SIG */ LIBC_GETHOSTBYNAME2_R_DECL { + tsocks_initialize(); return tsocks_gethostbyname2_r(LIBC_GETHOSTBYNAME2_R_ARGS); } diff --git a/src/lib/getpeername.c b/src/lib/getpeername.c index d8bf701..2f976a4 100644 --- a/src/lib/getpeername.c +++ b/src/lib/getpeername.c @@ -98,6 +98,7 @@ libc: LIBC_GETPEERNAME_DECL { if (!tsocks_libc_getpeername) { + tsocks_initialize(); tsocks_libc_getpeername = tsocks_find_libc_symbol( LIBC_GETPEERNAME_NAME_STR, TSOCKS_SYM_EXIT_NOT_FOUND); } diff --git a/src/lib/listen.c b/src/lib/listen.c index 38c37d8..5f675c4 100644 --- a/src/lib/listen.c +++ b/src/lib/listen.c @@ -80,6 +80,7 @@ error: LIBC_LISTEN_DECL { if (!tsocks_libc_listen) { + tsocks_initialize(); tsocks_libc_listen = tsocks_find_libc_symbol( LIBC_LISTEN_NAME_STR, TSOCKS_SYM_EXIT_NOT_FOUND); } diff --git a/src/lib/recv.c b/src/lib/recv.c index 6e8a20a..1ff1446 100644 --- a/src/lib/recv.c +++ b/src/lib/recv.c @@ -179,7 +179,7 @@ error: LIBC_RECVMSG_DECL { if (!tsocks_libc_recvmsg) { - /* Find symbol if not already set. Exit if not found. */ + tsocks_initialize(); tsocks_libc_recvmsg = tsocks_find_libc_symbol(LIBC_RECVMSG_NAME_STR, TSOCKS_SYM_EXIT_NOT_FOUND); } diff --git a/src/lib/sendto.c b/src/lib/sendto.c index 3362026..6b1d3ff 100644 --- a/src/lib/sendto.c +++ b/src/lib/sendto.c @@ -73,6 +73,7 @@ libc_sendto: LIBC_SENDTO_DECL { if (!tsocks_libc_sendto) { + tsocks_initialize(); tsocks_libc_sendto = tsocks_find_libc_symbol( LIBC_SENDTO_NAME_STR, TSOCKS_SYM_EXIT_NOT_FOUND); } diff --git a/src/lib/socket.c b/src/lib/socket.c index bdac610..edc24ab 100644 --- a/src/lib/socket.c +++ b/src/lib/socket.c @@ -72,5 +72,7 @@ end: */ LIBC_SOCKET_DECL { + if (!tsocks_libc_socket) + tsocks_initialize(); return tsocks_socket(LIBC_SOCKET_ARGS); } diff --git a/src/lib/socketpair.c b/src/lib/socketpair.c index c1c70f7..263e4d8 100644 --- a/src/lib/socketpair.c +++ b/src/lib/socketpair.c @@ -48,7 +48,7 @@ LIBC_SOCKETPAIR_RET_TYPE tsocks_socketpair(LIBC_SOCKETPAIR_SIG) LIBC_SOCKETPAIR_DECL { if (!tsocks_libc_socketpair) { - /* Find symbol if not already set. Exit if not found. */ + tsocks_initialize(); tsocks_libc_socketpair = tsocks_find_libc_symbol( LIBC_SOCKETPAIR_NAME_STR, TSOCKS_SYM_EXIT_NOT_FOUND); } diff --git a/src/lib/syscall.c b/src/lib/syscall.c index c3bbd56..8b24f4c 100644 --- a/src/lib/syscall.c +++ b/src/lib/syscall.c @@ -480,6 +480,7 @@ LIBC_SYSCALL_DECL va_list args;
if (!tsocks_libc_syscall) { + tsocks_initialize(); tsocks_libc_syscall= tsocks_find_libc_symbol( LIBC_SYSCALL_NAME_STR, TSOCKS_SYM_EXIT_NOT_FOUND); } diff --git a/src/lib/torsocks.c b/src/lib/torsocks.c index 5734af1..8f079fe 100644 --- a/src/lib/torsocks.c +++ b/src/lib/torsocks.c @@ -47,8 +47,11 @@ struct configuration tsocks_config; */ struct onion_pool tsocks_onion_pool;
+/* Indicate if the library was initialized previously. */ +static TSOCKS_INIT_ONCE(init_once); + /* Indicate if the library was cleaned up previously. */ -unsigned int tsocks_cleaned_up = 0; +static TSOCKS_INIT_ONCE(term_once);
/* * Set to 1 if the binary is set with suid or 0 if not. This is set once during @@ -273,7 +276,7 @@ static void init_logging(void) * Lib constructor. Initialize torsocks here before the main execution of the * binary we are preloading. */ -static void __attribute__((constructor)) tsocks_init(void) +static void tsocks_init(void) { int ret;
@@ -293,9 +296,6 @@ static void __attribute__((constructor)) tsocks_init(void) */ init_config();
- /* Initialize connection reigstry. */ - connection_registry_init(); - /* * Initalized the onion pool which maps cookie address to hidden service * onion address. @@ -311,9 +311,14 @@ static void __attribute__((constructor)) tsocks_init(void) /* * Lib destructor. */ -static void __attribute__((destructor)) tsocks_exit(void) +static void tsocks_exit(void) { - tsocks_cleanup(); + /* Cleanup every entries in the onion pool. */ + onion_pool_destroy(&tsocks_onion_pool); + /* Cleanup allocated memory in the config file. */ + config_file_destroy(&tsocks_config.conf_file); + /* Clean up logging. */ + log_destroy(); }
/* @@ -605,20 +610,17 @@ void *tsocks_find_libc_symbol(const char *symbol, }
/* - * Cleanup torsocks library memory and open fd. + * Initialize torsocks library. */ -void tsocks_cleanup(void) +void __attribute__((constructor)) tsocks_initialize(void) { - if (tsocks_cleaned_up) { - return; - } - - /* Cleanup every entries in the onion pool. */ - onion_pool_destroy(&tsocks_onion_pool); - /* Cleanup allocated memory in the config file. */ - config_file_destroy(&tsocks_config.conf_file); - /* Clean up logging. */ - log_destroy(); + tsocks_once(&init_once, &tsocks_init); +}
- tsocks_cleaned_up = 1; +/* + * Cleanup torsocks library memory and open fd. + */ +void __attribute__((destructor)) tsocks_cleanup(void) +{ + tsocks_once(&term_once, &tsocks_exit); } diff --git a/src/lib/torsocks.h b/src/lib/torsocks.h index 81073cf..076f3a5 100644 --- a/src/lib/torsocks.h +++ b/src/lib/torsocks.h @@ -422,6 +422,7 @@ void *tsocks_find_libc_symbol(const char *symbol, enum tsocks_sym_action action); int tsocks_tor_resolve(int af, const char *hostname, void *ip_addr); int tsocks_tor_resolve_ptr(const char *addr, char **ip, int af); +void tsocks_initialize(void); void tsocks_cleanup(void);
#endif /* TORSOCKS_H */
tor-commits@lists.torproject.org