[tor-commits] [torsocks/master] Ensure that torsocks initializes itself in the presence of C++.

dgoulet at torproject.org dgoulet at torproject.org
Tue May 12 15:28:23 UTC 2015


commit d45e4f2329e2630a9471ea72a48e9c77556100f1
Author: Yawning Angel <yawning at 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 at schwanenlied.me>
    Signed-off-by: David Goulet <dgoulet at 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 */



More information about the tor-commits mailing list