Commit ebc23cb9 authored by Ramon Nou's avatar Ramon Nou
Browse files

fix syscall tls recursion

parent 73227dff
Loading
Loading
Loading
Loading
Loading
+48 −25
Original line number Diff line number Diff line
@@ -36,7 +36,7 @@

  SPDX-License-Identifier: LGPL-3.0-or-later
*/

#include <pthread.h>
#include <client/intercept.hpp>
#include <client/preload.hpp>
#include <client/hooks.hpp>
@@ -74,7 +74,13 @@ void (*intercept_hook_point_post_kernel)(long syscall_number, long arg0,
#endif
namespace {

thread_local bool reentrance_guard_flag;
static pthread_key_t reentrance_guard_key;
static pthread_once_t key_once_control = PTHREAD_ONCE_INIT;
static void make_key() {
    // This function will be called exactly once per process.
    pthread_key_create(&reentrance_guard_key, NULL);
}

thread_local gkfs::syscall::info saved_syscall_info;

constexpr void
@@ -533,7 +539,8 @@ hook(long syscall_number, long arg0, long arg1, long arg2, long arg3, long arg4,

        case SYS_execve:
            // If we do not set this to false, we are in trouble with vforks
            reentrance_guard_flag = false;
pthread_once(&key_once_control, make_key);
            pthread_setspecific(reentrance_guard_key, NULL);
    		*result = syscall_no_intercept_wrapper(
                    syscall_number, reinterpret_cast<const char*>(arg0),
                    reinterpret_cast<const char* const*>(arg1),
@@ -543,7 +550,8 @@ hook(long syscall_number, long arg0, long arg1, long arg2, long arg3, long arg4,
#ifdef SYS_execveat
        case SYS_execveat:
            // If we do not set this to false, we are in trouble with vforks
            reentrance_guard_flag = false;
pthread_once(&key_once_control, make_key);
            pthread_setspecific(reentrance_guard_key, NULL);
	    *result = syscall_no_intercept_wrapper(
                    syscall_number, arg0, reinterpret_cast<const char*>(arg1),
                    reinterpret_cast<const char* const*>(arg2),
@@ -1011,12 +1019,19 @@ socketcall_wrapper(long syscall_number, long& arg0, long& arg1, long& arg2,
}
#endif


void
hook_forwarded_syscall(long syscall_number, long arg0, long arg1, long arg2,
                       long arg3, long arg4, long arg5, long result) {
    
    pthread_once(&key_once_control, make_key);
    if (pthread_getspecific(reentrance_guard_key) != NULL) {
        return;
    }
    pthread_setspecific(reentrance_guard_key, (void*) 1);


    if(::get_current_syscall_info() == gkfs::syscall::no_info) {
        pthread_setspecific(reentrance_guard_key, NULL);
        return;
    }

@@ -1029,6 +1044,8 @@ hook_forwarded_syscall(long syscall_number, long arg0, long arg1, long arg2,
        syscall_number, args, result);

    ::reset_current_syscall_info();
    
    pthread_setspecific(reentrance_guard_key, NULL);
}

void
@@ -1042,12 +1059,13 @@ hook_clone_at_child(unsigned long flags, void* child_stack, int* ptid,
            static_cast<long>(newtls),    0};
#endif

    reentrance_guard_flag = true;
    pthread_once(&key_once_control, make_key);
    pthread_setspecific(reentrance_guard_key, (void*) 1);

    LOG(SYSCALL, ::get_current_syscall_info() | gkfs::syscall::executed,
        SYS_clone, args, 0);

    reentrance_guard_flag = false;
    pthread_setspecific(reentrance_guard_key, NULL);
}

void
@@ -1061,18 +1079,20 @@ hook_clone_at_parent(unsigned long flags, void* child_stack, int* ptid,
            static_cast<long>(newtls),    0};
#endif

    reentrance_guard_flag = true;
    pthread_once(&key_once_control, make_key);
    pthread_setspecific(reentrance_guard_key, (void*) 1);

    LOG(SYSCALL, ::get_current_syscall_info() | gkfs::syscall::executed,
        SYS_clone, args, returned_pid);

    reentrance_guard_flag = false;
    pthread_setspecific(reentrance_guard_key, NULL);
}

} // namespace

namespace gkfs::preload {

// This function is inside 'namespace gkfs::preload'
int
internal_hook_guard_wrapper(long syscall_number, long arg0, long arg1,
                            long arg2, long arg3, long arg4, long arg5,
@@ -1085,7 +1105,8 @@ internal_hook_guard_wrapper(long syscall_number, long arg0, long arg1,
                                            arg3, arg4, arg5);
#endif

    if(reentrance_guard_flag) {
    pthread_once(&key_once_control, make_key);
    if (pthread_getspecific(reentrance_guard_key) != NULL) {
        ::save_current_syscall_info(gkfs::syscall::from_internal_code |
                                    gkfs::syscall::to_kernel |
                                    gkfs::syscall::not_executed);
@@ -1094,15 +1115,14 @@ internal_hook_guard_wrapper(long syscall_number, long arg0, long arg1,

    int was_hooked = 0;

    reentrance_guard_flag = true;
    pthread_setspecific(reentrance_guard_key, (void*) 1);
    was_hooked = hook_internal(syscall_number, arg0, arg1, arg2, arg3, arg4,
                               arg5, syscall_return_value);
    reentrance_guard_flag = false;
    pthread_setspecific(reentrance_guard_key, NULL);

    return was_hooked;
}


/*
 * hook_guard_wrapper -- a wrapper which can notice reentrance.
 *
@@ -1122,6 +1142,17 @@ hook_guard_wrapper(long syscall_number, long arg0, long arg1, long arg2,
                   long arg3, long arg4, long arg5,
                   long* syscall_return_value) {

    // --- START OF REVISED FIX for hook_guard_wrapper ---
    pthread_once(&key_once_control, make_key); // Ensure the key is created
    if (pthread_getspecific(reentrance_guard_key) != NULL) {
        // If the guard is set, forward the syscall to the kernel and return.
        return gkfs::syscall::forward_to_kernel;
    }
    // Set the guard to a non-NULL value.
    pthread_setspecific(reentrance_guard_key, (void*) 1);
    // --- END OF REVISED FIX for hook_guard_wrapper ---


    assert(CTX->interception_enabled());

#ifdef SYS_socketcall
@@ -1132,19 +1163,11 @@ hook_guard_wrapper(long syscall_number, long arg0, long arg1, long arg2,

    int was_hooked = 0;

    if(reentrance_guard_flag) {

        was_hooked = hook_internal(syscall_number, arg0, arg1, arg2, arg3, arg4,
                                   arg5, syscall_return_value);
        return was_hooked;
    }

    reentrance_guard_flag = true;

    was_hooked = ::hook(syscall_number, arg0, arg1, arg2, arg3, arg4, arg5,
                        syscall_return_value);

    reentrance_guard_flag = false;
    // Unset the guard before returning
    pthread_setspecific(reentrance_guard_key, NULL);

    return was_hooked;
}