commit d0f4415714af4d36f019ba50a3d368e532303010
Author: David Goulet <dgoulet(a)ev0ke.net>
Date: Thu Oct 31 15:52:09 2013 -0400
Lookup symbols in libc in constructor
This is to make sure that before main() we get the libc pointer from the
system library.
This commit also adds mmap/munmap support for syscall(). A comment in
lib/syscall.c explains for what specific case this is needed.
Finally, stop looking up symbols that were already looked up in the
constructor.
Signed-off-by: David Goulet <dgoulet(a)ev0ke.net>
---
configure.ac | 153 ++++++++-------------------------------------------
src/common/compat.h | 10 ++++
src/lib/close.c | 5 +-
src/lib/connect.c | 3 -
src/lib/socket.c | 3 -
src/lib/syscall.c | 70 ++++++++++++++++++++---
src/lib/torsocks.c | 39 +++++++++++--
7 files changed, 132 insertions(+), 151 deletions(-)
diff --git a/configure.ac b/configure.ac
index 6c9c5fd..23fc6c8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -65,26 +65,6 @@ AC_CHECK_FUNCS(strcspn strdup strerror strspn strtol mmap strcasecmp \
# in torsocks.c and elsewhere is platform-dependent.
##############################################################################
-dnl First find the library that contains connect() (obviously
-dnl the most important library for us). Once we've found it
-dnl we chuck it on the end of LIBS, that lib may end up there
-dnl more than once (since we do our search with an empty libs
-dnl list) but that isn't a problem
-OLDLIBS="${LIBS}"
-LIBS=
-CONNECTLIB=
-for LIB in c socket; do
- AC_CHECK_LIB("${LIB}",connect,[
- CONNECTLIB="${LIB}"
- break
- ],)
-done
-LIBS="${OLDLIBS} -l${CONNECTLIB}"
-if test "${CONNECTLIB}" = ""; then
- AC_MSG_ERROR('Could not find library containing connect()')
-fi
-
-
dnl Check for socket
AC_CHECK_FUNC(socket,, [
AC_CHECK_LIB(socket, socket,,AC_MSG_ERROR("socket function not found"))])
@@ -131,124 +111,39 @@ case $host in
;;
esac
-
-##############################################################################
-# 3. Check if we need to use --enable-oldmethod, regardless of what was
-# given on the ./configure command line.
-##############################################################################
-
-dnl If we're using gcc here define _GNU_SOURCE
-AC_MSG_CHECKING(for RTLD_NEXT from dlfcn.h)
-AC_EGREP_CPP(yes,
-[
- #include <dlfcn.h>
- #ifdef RTLD_NEXT
- yes
- #endif
-], [
- AC_MSG_RESULT(yes)
-], [
- AC_MSG_RESULT(no)
- AC_MSG_CHECKING(for RTLD_NEXT from dlfcn.h with _GNU_SOURCE)
- AC_EGREP_CPP(yes,
- [
- #define _GNU_SOURCE
- #include <dlfcn.h>
- #ifdef RTLD_NEXT
- yes
- #endif
- ], [
- AC_MSG_RESULT(yes)
- AC_DEFINE([USE_GNU_SOURCE],[],[Description])
- ], [
- AC_MSG_RESULT(no)
- AC_DEFINE([USE_OLD_DLSYM],[],[Description])
- oldmethod="yes"
- ])
-])
-
if test "x${enable_envconf}" = "x"; then
AC_DEFINE([ALLOW_ENV_CONFIG],[],[Description])
fi
-##############################################################################
-# 3. If --enable-oldmethod was requested, perform the necessary checks
-##############################################################################
+dnl Check that find is available, it should be somehere
+dnl in the path
+AC_CHECK_PROG(FIND, find, find)
+if test "${FIND}" = ""; then
+ AC_MSG_ERROR('find not found in path')
+fi
-if test "${enable_oldmethod}" = "yes"; then
- AC_DEFINE([USE_OLD_DLSYM],[],[Description])
- oldmethod="yes"
+dnl Find tail, it should always be somewhere in the path
+dnl but for safety's sake
+AC_CHECK_PROG(TAIL, tail, tail)
+if test "${TAIL}" = ""; then
+ AC_MSG_ERROR('tail not found in path')
fi
-dnl If we have to use the old method of overriding connect (i.e no
-dnl RTLD_NEXT) we need to know the location of the library that
-dnl contains connect(), select(), poll() and close()
-if test "${oldmethod}" = "yes"; then
- dnl We need to find the path to the library, to do
- dnl this we use find on the usual suspects, i.e /lib and
- dnl /usr/lib
-
- dnl Check that find is available, it should be somehere
- dnl in the path
- AC_CHECK_PROG(FIND, find, find)
- if test "${FIND}" = ""; then
- AC_MSG_ERROR('find not found in path')
- fi
-
- dnl Find tail, it should always be somewhere in the path
- dnl but for safety's sake
- AC_CHECK_PROG(TAIL, tail, tail)
- if test "${TAIL}" = ""; then
- AC_MSG_ERROR('tail not found in path')
- fi
-
- dnl Now find the library we need
- AC_MSG_CHECKING(location of lib${CONNECTLIB}.so)
- LIBCONNECT=
- for DIR in '/lib' '/usr/lib'; do
- if test "${LIBCONNECT}" = ""; then
- LIBCONNECT=`$FIND $DIR -name "lib${CONNECTLIB}.so.?" 2>/dev/null | $TAIL -1`
- fi
- done
- AC_DEFINE_UNQUOTED([LIBCONNECT],["${LIBCONNECT}"],[Description])
- if test "${LIBCONNECT}" = ""; then
- AC_MSG_ERROR("not found!")
- fi
-
- AC_MSG_RESULT($LIBCONNECT)
-
- dnl Now find the resolve library we need
- AC_MSG_CHECKING(location of libresolv.so)
- LIBRESOLV=
- for DIR in '/lib' '/usr/lib'; do
- if test "${LIBRESOLV}" = ""; then
- LIBRESOLV=`$FIND $DIR -name "libresolv.so.?" 2>/dev/null | $TAIL -1`
- fi
- done
- AC_DEFINE_UNQUOTED([LIBRESOLV],["${LIBRESOLV}"],[Description])
- if test "${LIBRESOLV}" = ""; then
- AC_MSG_ERROR("not found!")
- fi
-
- AC_MSG_RESULT($LIBRESOLV)
-
- dnl close() should be in libc, find it
- AC_MSG_CHECKING(location of libc.so)
- LIBC=
- for DIR in '/lib' '/usr/lib'; do
- if test "${LIBC}" = ""; then
- LIBC=`$FIND $DIR -name "libc.so.?" 2>/dev/null | $TAIL -1`
- fi
- done
-
- AC_DEFINE_UNQUOTED([LIBC],["${LIBC}"],[Description])
- if test "${LIBC}" = ""; then
- AC_MSG_ERROR("not found!")
- fi
-
- AC_MSG_RESULT($LIBC)
+dnl Get libc full system path. Use prefix or some hardcoded standard
+dnl location on Unixish system.
+AC_MSG_CHECKING(location of libc.so)
+for DIR in "$prefix/lib" "$prefix/usr/lib" '/lib' '/usr/lib'; do
+ if test "${LIBC_PATH}" = ""; then
+ LIBC_PATH=`$FIND $DIR -name "libc.so.?" 2>/dev/null | $TAIL -1`
+ fi
+done
+AC_DEFINE_UNQUOTED([LIBC_PATH],["${LIBC_PATH}"],[Description])
+if test "${LIBC_PATH}" = ""; then
+ AC_MSG_ERROR("not found!")
fi
+AC_MSG_RESULT($LIBC_PATH)
+
##############################################################################
# 5. Determine how to preload libtorsocks.so on this system.
# On Linux this is with the LD_PRELOAD variable, on OSX
diff --git a/src/common/compat.h b/src/common/compat.h
index 8207fc0..2eacb35 100644
--- a/src/common/compat.h
+++ b/src/common/compat.h
@@ -69,10 +69,18 @@ void tsocks_mutex_unlock(tsocks_mutex_t *m);
#ifndef __NR_close
#define __NR_close -1
#endif
+#ifndef __NR_mmap
+#define __NR_mmap -1
+#endif
+#ifndef __NR_munmap
+#define __NR_munmap -1
+#endif
#define TSOCKS_NR_SOCKET __NR_socket
#define TSOCKS_NR_CONNECT __NR_connect
#define TSOCKS_NR_CLOSE __NR_close
+#define TSOCKS_NR_MMAP __NR_mmap
+#define TSOCKS_NR_MUNMAP __NR_munmap
#endif /* __linux__ */
@@ -84,6 +92,8 @@ void tsocks_mutex_unlock(tsocks_mutex_t *m);
#define TSOCKS_NR_SOCKET SYS_socket
#define TSOCKS_NR_CONNECT SYS_connect
#define TSOCKS_NR_CLOSE SYS_close
+#define TSOCKS_NR_MMAP SYS_mmap
+#define TSOCKS_NR_MUNMAP SYS_munmap
#endif /* __FreeBSD__, __FreeBSD_kernel__, __darwin__ */
diff --git a/src/lib/close.c b/src/lib/close.c
index 699b93c..b8a6364 100644
--- a/src/lib/close.c
+++ b/src/lib/close.c
@@ -20,6 +20,9 @@
#include "torsocks.h"
+/* close(2) */
+TSOCKS_LIBC_DECL(close, LIBC_CLOSE_RET_TYPE, LIBC_CLOSE_SIG)
+
/*
* Torsocks call for close(2).
*/
@@ -58,7 +61,5 @@ LIBC_CLOSE_RET_TYPE tsocks_close(LIBC_CLOSE_SIG)
*/
LIBC_CLOSE_DECL
{
- tsocks_libc_close = tsocks_find_libc_symbol(LIBC_CLOSE_NAME_STR,
- TSOCKS_SYM_EXIT_NOT_FOUND);
return tsocks_close(LIBC_CLOSE_ARGS);
}
diff --git a/src/lib/connect.c b/src/lib/connect.c
index 3d75a94..bc3ac9d 100644
--- a/src/lib/connect.c
+++ b/src/lib/connect.c
@@ -143,8 +143,5 @@ error:
*/
LIBC_CONNECT_DECL
{
- /* Find symbol if not already set. Exit if not found. */
- tsocks_libc_connect = tsocks_find_libc_symbol(LIBC_CONNECT_NAME_STR,
- TSOCKS_SYM_EXIT_NOT_FOUND);
return tsocks_connect(LIBC_CONNECT_ARGS);
}
diff --git a/src/lib/socket.c b/src/lib/socket.c
index aa5297e..8a4c0d1 100644
--- a/src/lib/socket.c
+++ b/src/lib/socket.c
@@ -53,8 +53,5 @@ LIBC_SOCKET_RET_TYPE tsocks_socket(LIBC_SOCKET_SIG)
*/
LIBC_SOCKET_DECL
{
- /* Find symbol if not already set. Exit if not found. */
- tsocks_libc_socket = tsocks_find_libc_symbol(LIBC_SOCKET_NAME_STR,
- TSOCKS_SYM_EXIT_NOT_FOUND);
return tsocks_socket(LIBC_SOCKET_ARGS);
}
diff --git a/src/lib/syscall.c b/src/lib/syscall.c
index b06e2e3..674ee6e 100644
--- a/src/lib/syscall.c
+++ b/src/lib/syscall.c
@@ -17,6 +17,7 @@
#include <assert.h>
#include <stdarg.h>
+#include <sys/mman.h>
#include <common/log.h>
@@ -25,9 +26,6 @@
/* syscall(2) */
TSOCKS_LIBC_DECL(syscall, LIBC_SYSCALL_RET_TYPE, LIBC_SYSCALL_SIG)
-/* close(2) */
-TSOCKS_LIBC_DECL(close, LIBC_CLOSE_RET_TYPE, LIBC_CLOSE_SIG)
-
/*
* Handle close syscall to be called with tsocks call.
*/
@@ -71,11 +69,45 @@ static LIBC_CONNECT_RET_TYPE handle_connect(va_list args)
}
/*
+ * Handle mmap(2) syscall.
+ */
+static LIBC_SYSCALL_RET_TYPE handle_mmap(va_list args)
+{
+ void *addr;
+ size_t len;
+ int prot, flags, fd;
+ off_t offset;
+
+ addr = va_arg(args, __typeof__(addr));
+ len = va_arg(args, __typeof__(len));
+ prot = va_arg(args, __typeof__(prot));
+ flags = va_arg(args, __typeof__(flags));
+ fd = va_arg(args, __typeof__(fd));
+ offset = va_arg(args, __typeof__(offset));
+
+ return (LIBC_SYSCALL_RET_TYPE) mmap(addr, len, prot, flags, fd, offset);
+}
+
+/*
+ * Handle munmap(2) syscall.
+ */
+static LIBC_SYSCALL_RET_TYPE handle_munmap(va_list args)
+{
+ void *addr;
+ size_t len;
+
+ addr = va_arg(args, __typeof__(addr));
+ len = va_arg(args, __typeof__(len));
+
+ return (LIBC_SYSCALL_RET_TYPE) munmap(addr, len);
+}
+
+/*
* Torsocks call for syscall(2)
*/
LIBC_SYSCALL_RET_TYPE tsocks_syscall(long int __number, va_list args)
{
- long int ret;
+ LIBC_SYSCALL_RET_TYPE ret;
DBG("[syscall] Syscall libc wrapper number %ld called", __number);
@@ -89,6 +121,32 @@ LIBC_SYSCALL_RET_TYPE tsocks_syscall(long int __number, va_list args)
case TSOCKS_NR_CLOSE:
ret = handle_close(args);
break;
+ case TSOCKS_NR_MMAP:
+ /*
+ * The mmap/munmap syscall are handled here for a very specific case so
+ * buckle up here for the explanation :).
+ *
+ * Considering an application that handles its own memory using a
+ * malloc(2) hook for instance *AND* mmap() is called with syscall(),
+ * we have to route the call to the libc in order to complete the
+ * syscall() symbol lookup.
+ *
+ * The lookup process of the libdl (using dlsym(3)) calls at some point
+ * malloc for a temporary buffer so we end up in this torsocks wrapper
+ * when mmap() is called to create a new memory region for the
+ * application (remember the malloc hook). When getting here, the libc
+ * syscall() symbol is NOT yet populated because we are in the lookup
+ * code path. For this, we directly call mmap/munmap using the libc so
+ * the lookup can be completed.
+ *
+ * This crazy situation is present in Mozilla Firefox which handles its
+ * own memory using mmap() called by syscall(). Same for munmap().
+ */
+ ret = handle_mmap(args);
+ break;
+ case TSOCKS_NR_MUNMAP:
+ ret = handle_munmap(args);
+ break;
default:
/*
* Deny call since we have no idea if this call can leak or not data
@@ -112,10 +170,6 @@ LIBC_SYSCALL_DECL
LIBC_SYSCALL_RET_TYPE ret;
va_list args;
- /* Find symbol if not already set. Exit if not found. */
- tsocks_libc_syscall = tsocks_find_libc_symbol(LIBC_SYSCALL_NAME_STR,
- TSOCKS_SYM_EXIT_NOT_FOUND);
-
va_start(args, __number);
ret = tsocks_syscall(__number, args);
va_end(args);
diff --git a/src/lib/torsocks.c b/src/lib/torsocks.c
index 833a472..55ae98b 100644
--- a/src/lib/torsocks.c
+++ b/src/lib/torsocks.c
@@ -119,12 +119,39 @@ static void init_config(void)
*/
static void init_libc_symbols(void)
{
- tsocks_libc_connect = tsocks_find_libc_symbol(LIBC_CONNECT_NAME_STR,
- TSOCKS_SYM_EXIT_NOT_FOUND);
- tsocks_libc_close = tsocks_find_libc_symbol(LIBC_CLOSE_NAME_STR,
- TSOCKS_SYM_EXIT_NOT_FOUND);
- tsocks_libc_socket = tsocks_find_libc_symbol(LIBC_SOCKET_NAME_STR,
- TSOCKS_SYM_EXIT_NOT_FOUND);
+ int ret;
+ void *libc_ptr;
+
+ dlerror();
+ libc_ptr = dlopen(LIBC_PATH, RTLD_LAZY);
+ if (!libc_ptr) {
+ ERR("Unable to dlopen() library " LIBC_PATH "(%s)", dlerror());
+ goto error;
+ }
+
+ dlerror();
+ tsocks_libc_connect = dlsym(libc_ptr, LIBC_CONNECT_NAME_STR);
+ tsocks_libc_close = dlsym(libc_ptr, LIBC_CLOSE_NAME_STR);
+ tsocks_libc_socket = dlsym(libc_ptr, LIBC_SOCKET_NAME_STR);
+ tsocks_libc_syscall = dlsym(libc_ptr, LIBC_SYSCALL_NAME_STR);
+ if (!tsocks_libc_connect || !tsocks_libc_close || !tsocks_libc_socket
+ || !tsocks_libc_syscall) {
+ ERR("Unable to lookup symbols in " LIBC_PATH "(%s)", dlerror());
+ goto error;
+ }
+
+ ret = dlclose(libc_ptr);
+ if (ret != 0) {
+ ERR("dlclose: %s", dlerror());
+ }
+ return;
+
+error:
+ ret = dlclose(libc_ptr);
+ if (ret != 0) {
+ ERR("dlclose: %s", dlerror());
+ }
+ clean_exit(EXIT_FAILURE);
}
/*