0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-05-20 03:43:47 +02:00

ircd::ctx::posix: Trapdoor complex allowing real pthreads to work again.

This commit is contained in:
Jason Volk 2021-01-03 04:12:56 -08:00
parent e37a775e9b
commit 446f319391
5 changed files with 237 additions and 52 deletions

View file

@ -14,8 +14,7 @@ namespace ircd::ctx::posix
struct disable_pthread; struct disable_pthread;
extern log::log log; extern log::log log;
extern bool hook_pthread_create; extern int enable_hook; // -1 = pthread; 0 = auto; 1 = ircd::ctx.
extern bool unhook_pthread_create;
extern std::vector<context> ctxs; extern std::vector<context> ctxs;
} }
@ -25,19 +24,19 @@ namespace ircd::ctx::posix
/// disable_pthread. /// disable_pthread.
struct ircd::ctx::posix::enable_pthread struct ircd::ctx::posix::enable_pthread
{ {
bool theirs int theirs
{ {
unhook_pthread_create enable_hook
}; };
enable_pthread(const bool &ours = true) enable_pthread(const bool &ours = true)
{ {
unhook_pthread_create = ours; enable_hook = ours? -1: theirs;
} }
~enable_pthread() noexcept ~enable_pthread() noexcept
{ {
unhook_pthread_create = theirs; enable_hook = theirs;
} }
}; };
@ -48,18 +47,18 @@ struct ircd::ctx::posix::enable_pthread
/// precedence. /// precedence.
struct ircd::ctx::posix::disable_pthread struct ircd::ctx::posix::disable_pthread
{ {
bool theirs int theirs
{ {
hook_pthread_create enable_hook
}; };
disable_pthread(const bool &ours = true) disable_pthread(const bool &ours = true)
{ {
hook_pthread_create = ours; enable_hook = ours? 1: theirs;
} }
~disable_pthread() ~disable_pthread() noexcept
{ {
hook_pthread_create = theirs; enable_hook = theirs;
} }
}; };

View file

@ -28,7 +28,9 @@ AM_LDFLAGS = \
-Wl,--unresolved-symbols=ignore-in-shared-libs \ -Wl,--unresolved-symbols=ignore-in-shared-libs \
-Wl,--wrap=pthread_create \ -Wl,--wrap=pthread_create \
-Wl,--wrap=pthread_join \ -Wl,--wrap=pthread_join \
-Wl,--wrap=pthread_tryjoin_np \
-Wl,--wrap=pthread_timedjoin_np \ -Wl,--wrap=pthread_timedjoin_np \
-Wl,--wrap=pthread_clockjoin_np \
-Wl,--wrap=pthread_self \ -Wl,--wrap=pthread_self \
-Wl,--wrap=pthread_setname_np \ -Wl,--wrap=pthread_setname_np \
-Wl,-z,nodelete \ -Wl,-z,nodelete \

View file

@ -16,6 +16,7 @@
// the project and to other users of the address space. // the project and to other users of the address space.
// //
#include <dlfcn.h>
#include <pthread.h> #include <pthread.h>
#include "ctx_posix.h" #include "ctx_posix.h"
@ -23,35 +24,50 @@
namespace ircd::ctx::posix namespace ircd::ctx::posix
{ {
static bool is_main_thread() noexcept;
static bool hook_enabled() noexcept;
static bool is(const pthread_t &) noexcept; static bool is(const pthread_t &) noexcept;
[[gnu::visibility("internal")]]
extern void *real_pthread; // custom_ptr<void> real_pthread;
} }
using ircd::always_assert; using ircd::always_assert;
/// Unit's logging facility.
decltype(ircd::ctx::posix::log) decltype(ircd::ctx::posix::log)
ircd::ctx::posix::log ircd::ctx::posix::log
{ {
"ctx.posix" "ctx.posix"
}; };
/// When asserted true, real pthread is never created and ircd::ctx is /// Points to a dlopen(3) handle of libpthread.so to give us the untainted
/// spawned instead. By default it's false, which will allow other settings /// location of real pthread functions; regardless of our hook solution for
/// or automated determination to decide. /// the platform. It remains null until this interface is used to spawn an
decltype(ircd::ctx::posix::hook_pthread_create) /// actually real pthread. Note that this might not be available if static
ircd::ctx::posix::hook_pthread_create; /// destruction occurs prior to any joining thread accessing it.
decltype(ircd::ctx::posix::real_pthread)
ircd::ctx::posix::real_pthread
{
nullptr, //::dlclose
};
/// When asserted true, real pthread is always created and ircd::ctx is /// -1 = pthread interface not hooked, forwards to real pthread.
/// never spawned. By default it's false, which will allow other settings /// 0 = determined automatically based on contextual information.
/// or automated determination to decide. /// 1 = pthread interface hooked, forwards to ircd::ctx.
decltype(ircd::ctx::posix::unhook_pthread_create) decltype(ircd::ctx::posix::enable_hook)
ircd::ctx::posix::unhook_pthread_create; ircd::ctx::posix::enable_hook;
/// State container for ircd::ctx's that are being operated through the hooked
/// pthread interface.
decltype(ircd::ctx::posix::ctxs) decltype(ircd::ctx::posix::ctxs)
ircd::ctx::posix::ctxs; ircd::ctx::posix::ctxs;
/// Hook generation macro. If the hook is accomplished with __wrap/__real
/// then also be sure to add the line to the linker flags in Makefile.am.
#define IRCD_WRAP(symbol, target, prototype, body) \ #define IRCD_WRAP(symbol, target, prototype, body) \
extern "C" int __real_##symbol prototype; \ extern "C" int __real_##symbol prototype; \
extern "C" int __wrap_##symbol prototype body \ extern "C" int __wrap_##symbol prototype noexcept body \
extern "C" int symbol prototype __attribute__((weak, alias(target))); extern "C" int symbol prototype __attribute__((weak, alias(target)));
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -65,22 +81,39 @@ IRCD_WRAP(pthread_create, "__wrap_pthread_create",
const pthread_attr_t *const attr, const pthread_attr_t *const attr,
void *(*const start_routine)(void *), void *(*const start_routine)(void *),
void *const arg void *const arg
), { ),
const bool hook_enabled {
{ if(ircd::ctx::posix::hook_enabled())
// When enable_pthread is asserted, the hook is never enabled. return ircd_pthread_create(thread, attr, start_routine, arg);
!ircd::ctx::posix::unhook_pthread_create
// When disable_pthread is asserted, the hook is otherwise enabled. // hack on the hack: linker's __real_pthread_create isn't working and we
// Otherwise if we're already running on an ircd::ctx stack we continue // need something stronger.
// to spawn more ircd::ctx. By default on another stack (or main stack) if(!ircd::ctx::posix::real_pthread)
// we spawn a true pthread. {
&& (ircd::ctx::posix::hook_pthread_create || ircd::ctx::current) // 2p the dlopen() handle so it's init once by any real thread.
static std::mutex mutex;
const std::lock_guard lock
{
mutex
};
if(!ircd::ctx::posix::real_pthread)
ircd::ctx::posix::real_pthread = //.reset
(
dlopen("libpthread.so", RTLD_LOCAL | RTLD_LAZY)
);
}
assert(ircd::ctx::posix::real_pthread);
const auto __real_pthread_create
{
reinterpret_cast<int (*)(pthread_t *, const pthread_attr_t *, void *(*)(void *), void *)>
(
dlsym(ircd::ctx::posix::real_pthread, "pthread_create")
)
}; };
return hook_enabled? return __real_pthread_create(thread, attr, start_routine, arg);
ircd_pthread_create(thread, attr, start_routine, arg):
__real_pthread_create(thread, attr, start_routine, arg);
}) })
int int
@ -120,10 +153,22 @@ IRCD_WRAP(pthread_join, "__wrap_pthread_join",
( (
pthread_t __th, pthread_t __th,
void **__thread_return void **__thread_return
), { ),
return ircd::ctx::posix::is(__th)? {
ircd_pthread_join(__th, __thread_return): if(ircd::ctx::posix::is(__th))
__real_pthread_join(__th, __thread_return); return ircd_pthread_join(__th, __thread_return);
assert(ircd::ctx::posix::real_pthread);
const auto __real_pthread_join
{
reinterpret_cast<int (*)(pthread_t, void **)>
(
dlsym(ircd::ctx::posix::real_pthread, "pthread_join")
)
};
assert(__real_pthread_join);
return __real_pthread_join(__th, __thread_return);
}) })
int int
@ -156,6 +201,27 @@ noexcept
return 0; return 0;
} }
IRCD_WRAP(pthread_tryjoin_np, "__wrap_pthread_tryjoin_np",
(
pthread_t __th,
void **__thread_return
),
{
if(ircd::ctx::posix::is(__th))
return ircd_pthread_tryjoin_np(__th, __thread_return);
assert(ircd::ctx::posix::real_pthread);
const auto __real_pthread_tryjoin_np
{
reinterpret_cast<int (*)(pthread_t, void **)>
(
dlsym(ircd::ctx::posix::real_pthread, "pthread_tryjoin_np")
)
};
return __real_pthread_tryjoin_np(__th, __thread_return);
})
int int
ircd_pthread_tryjoin_np(pthread_t __th, ircd_pthread_tryjoin_np(pthread_t __th,
void **__thread_return) void **__thread_return)
@ -170,11 +236,23 @@ IRCD_WRAP(pthread_timedjoin_np, "__wrap_pthread_timedjoin_np",
pthread_t __th, pthread_t __th,
void **__thread_return, void **__thread_return,
const struct timespec *__abstime const struct timespec *__abstime
), { ),
return ircd::ctx::posix::is(__th)? {
ircd_pthread_timedjoin_np(__th, __thread_return, __abstime): if(ircd::ctx::posix::is(__th))
__real_pthread_timedjoin_np(__th, __thread_return, __abstime); return ircd_pthread_timedjoin_np(__th, __thread_return, __abstime);
});
assert(ircd::ctx::posix::real_pthread);
const auto __real_pthread_timedjoin_np
{
reinterpret_cast<int (*)(pthread_t, void **, const struct timespec *)>
(
dlsym(ircd::ctx::posix::real_pthread, "pthread_timedjoin_np")
)
};
assert(__real_pthread_timedjoin_np);
return __real_pthread_timedjoin_np(__th, __thread_return, __abstime);
})
int int
ircd_pthread_timedjoin_np(pthread_t __th, ircd_pthread_timedjoin_np(pthread_t __th,
@ -187,6 +265,42 @@ noexcept
return 0; return 0;
} }
IRCD_WRAP(pthread_clockjoin_np, "__wrap_pthread_clockjoin_np",
(
pthread_t __th,
void **__thread_return,
clockid_t clockid,
const struct timespec *__abstime
),
{
if(ircd::ctx::posix::is(__th))
return ircd_pthread_clockjoin_np(__th, __thread_return, clockid, __abstime);
assert(ircd::ctx::posix::real_pthread);
const auto __real_pthread_clockjoin_np
{
reinterpret_cast<int (*)(pthread_t, void **, clockid_t, const struct timespec *)>
(
dlsym(ircd::ctx::posix::real_pthread, "pthread_clockjoin_np")
)
};
assert(__real_pthread_clockjoin_np);
return __real_pthread_clockjoin_np(__th, __thread_return, clockid, __abstime);
})
int
ircd_pthread_clockjoin_np(pthread_t __th,
void **__thread_return,
clockid_t clockid,
const struct timespec *__abstime)
noexcept
{
//TODO: XXX ctx clock join
ircd_pthread_join(__th, __thread_return);
return 0;
}
void void
__attribute__((noreturn)) __attribute__((noreturn))
ircd_pthread_exit(void *const retval) ircd_pthread_exit(void *const retval)
@ -210,9 +324,17 @@ __real_pthread_self(void);
extern "C" pthread_t extern "C" pthread_t
__wrap_pthread_self(void) __wrap_pthread_self(void)
{ {
return ircd::ctx::current? const bool hook_enabled
ircd_pthread_self(): {
__real_pthread_self(); true
&& ircd::ctx::current
&& ircd::ctx::posix::enable_hook >= 0
};
if(hook_enabled)
return ircd_pthread_self();
return __real_pthread_self();
} }
#if 0 #if 0
@ -360,10 +482,22 @@ IRCD_WRAP(pthread_setname_np, "__wrap_pthread_setname_np",
( (
pthread_t __target_thread, pthread_t __target_thread,
const char *__name const char *__name
), { ),
return ircd::ctx::posix::is(__target_thread)? {
ircd_pthread_setname_np(__target_thread, __name): if(ircd::ctx::posix::is(__target_thread))
__real_pthread_setname_np(__target_thread, __name); return ircd_pthread_setname_np(__target_thread, __name);
assert(ircd::ctx::posix::real_pthread);
const auto __real_pthread_setname_np
{
reinterpret_cast<int (*)(pthread_t, const char *)>
(
dlsym(ircd::ctx::posix::real_pthread, "pthread_setname_np")
)
};
assert(__real_pthread_setname_np);
return __real_pthread_setname_np(__target_thread, __name);
}) })
int int
@ -1532,6 +1666,10 @@ bool
ircd::ctx::posix::is(const pthread_t &target) ircd::ctx::posix::is(const pthread_t &target)
noexcept noexcept
{ {
// Can't be an ircd::ctx if it's not the main thread, nor can we look.
if(!is_main_thread())
return false;
const auto it const auto it
{ {
std::find_if(begin(ctxs), end(ctxs), [&] std::find_if(begin(ctxs), end(ctxs), [&]
@ -1543,3 +1681,39 @@ noexcept
return it != end(ctxs); return it != end(ctxs);
} }
bool
ircd::ctx::posix::hook_enabled()
noexcept
{
// The hook is only enabled on the main thread.
if(!is_main_thread())
return false;
// When disable_pthread is asserted, the hook is always enabled.
if(ircd::ctx::posix::enable_hook > 0)
return true;
// When enable_pthread is asserted, the hook is never enabled.
if(ircd::ctx::posix::enable_hook < 0)
return false;
// Consider the hook enabled if called from an ircd::ctx stack, since
// that is clearly our code, and if we call into a library on such a
// stack we will use an explicit enable_pthread if we need it.
//
// OTOH, when not on an ircd::ctx stack, we assume the call is coming from
// some other code is running somewhere else in the address space, perhaps
// totally unrelated, and we give that the expected unhooked behavior.
return ircd::ctx::current != nullptr;
}
bool
ircd::ctx::posix::is_main_thread()
noexcept
{
return false
|| ircd::ios::handler::current != nullptr
|| ircd::ctx::current != nullptr
|| ircd::ctx::is_main_thread();
}

View file

@ -37,6 +37,13 @@ ircd_pthread_timedjoin_np(pthread_t __th,
const struct timespec *__abstime) const struct timespec *__abstime)
noexcept; noexcept;
extern "C" int
ircd_pthread_clockjoin_np(pthread_t __th,
void **__thread_return,
clockid_t clockid,
const struct timespec *__abstime)
noexcept;
extern "C" int extern "C" int
ircd_pthread_detach(pthread_t __th) ircd_pthread_detach(pthread_t __th)
noexcept; noexcept;

View file

@ -17,7 +17,10 @@ ircd::ios::log
/// "main" thread for IRCd; the one the main context landed on. /// "main" thread for IRCd; the one the main context landed on.
decltype(ircd::ios::main_thread_id) decltype(ircd::ios::main_thread_id)
ircd::ios::main_thread_id; ircd::ios::main_thread_id
{
std::this_thread::get_id()
};
/// The embedder/executable's (library user) asio::executor provided on init. /// The embedder/executable's (library user) asio::executor provided on init.
decltype(ircd::ios::user) decltype(ircd::ios::user)