.. _program_listing_file_src_client_intercept.cpp: Program Listing for File intercept.cpp ====================================== |exhale_lsh| :ref:`Return to documentation for file ` (``src/client/intercept.cpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp /* Copyright 2018-2025, Barcelona Supercomputing Center (BSC), Spain Copyright 2015-2025, Johannes Gutenberg Universitaet Mainz, Germany This software was partially supported by the EC H2020 funded project NEXTGenIO (Project ID: 671951, www.nextgenio.eu). This software was partially supported by the ADA-FS project under the SPPEXA project funded by the DFG. This software was partially supported by the the European Union’s Horizon 2020 JTI-EuroHPC research and innovation programme, by the project ADMIRE (Project ID: 956748, admire-eurohpc.eu) This project was partially promoted by the Ministry for Digital Transformation and the Civil Service, within the framework of the Recovery, Transformation and Resilience Plan - Funded by the European Union -NextGenerationEU. This file is part of GekkoFS' POSIX interface. GekkoFS' POSIX interface is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. GekkoFS' POSIX interface is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with GekkoFS' POSIX interface. If not, see . SPDX-License-Identifier: LGPL-3.0-or-later */ #include #include #include #include #include #include #include #include extern "C" { #include #include #include #include } #ifdef BYPASS_SYSCALL int (*intercept_hook_point)(long syscall_number, long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long* result){}; void (*intercept_hook_point_clone_child)(unsigned long flags, void* child_stack, int* ptid, int* ctid, long newtls){}; void (*intercept_hook_point_clone_parent)(unsigned long flags, void* child_stack, int* ptid, int* ctid, long newtls, long returned_pid){}; void (*intercept_hook_point_post_kernel)(long syscall_number, long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long result){}; #endif namespace { thread_local bool reentrance_guard_flag; thread_local gkfs::syscall::info saved_syscall_info; constexpr void save_current_syscall_info(gkfs::syscall::info info) { saved_syscall_info = info; } constexpr void reset_current_syscall_info() { saved_syscall_info = gkfs::syscall::no_info; } inline gkfs::syscall::info get_current_syscall_info() { return saved_syscall_info; } /* * hook_internal -- interception hook for internal syscalls * * This hook is basically used to keep track of file descriptors created * internally by the library itself. This is important because some * applications (e.g. ssh) may attempt to close all open file descriptors * which would leave the library internals in an inconsistent state. * We forward syscalls to the kernel but we keep track of any syscalls that may * create or destroy a file descriptor so that we can mark them as 'internal'. */ inline int hook_internal(long syscall_number, long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long* result) { #if defined(GKFS_ENABLE_LOGGING) && defined(GKFS_DEBUG_BUILD) const long args[gkfs::syscall::MAX_ARGS] = {arg0, arg1, arg2, arg3, arg4, arg5}; #endif LOG(SYSCALL, gkfs::syscall::from_internal_code | gkfs::syscall::to_hook | gkfs::syscall::not_executed, syscall_number, args); switch(syscall_number) { #ifdef SYS_open case SYS_open: *result = syscall_no_intercept_wrapper( syscall_number, reinterpret_cast(arg0), static_cast(arg1), static_cast(arg2)); if(*result > 0) { *result = CTX->register_internal_fd(*result); } break; #endif #ifdef SYS_creat case SYS_creat: *result = syscall_no_intercept_wrapper( syscall_number, reinterpret_cast(arg0), O_WRONLY | O_CREAT | O_TRUNC, static_cast(arg1)); if(*result > 0) { *result = CTX->register_internal_fd(*result); } break; #endif case SYS_openat: *result = syscall_no_intercept_wrapper( syscall_number, static_cast(arg0), reinterpret_cast(arg1), static_cast(arg2), static_cast(arg3)); if(*result > 0) { *result = CTX->register_internal_fd(*result); } break; #ifdef SYS_epoll_create case SYS_epoll_create: *result = syscall_no_intercept_wrapper(syscall_number, static_cast(arg0)); if(*result > 0) { *result = CTX->register_internal_fd(*result); } break; #endif case SYS_epoll_create1: *result = syscall_no_intercept_wrapper(syscall_number, static_cast(arg0)); if(*result > 0) { *result = CTX->register_internal_fd(*result); } break; case SYS_dup: *result = syscall_no_intercept_wrapper( syscall_number, static_cast(arg0)); if(*result > 0) { *result = CTX->register_internal_fd(*result); } break; #ifdef SYS_dup2 case SYS_dup2: *result = syscall_no_intercept_wrapper( syscall_number, static_cast(arg0), static_cast(arg1)); if(*result > 0) { *result = CTX->register_internal_fd(*result); } break; #endif case SYS_dup3: *result = syscall_no_intercept_wrapper( syscall_number, static_cast(arg0), static_cast(arg1), static_cast(arg2)); if(*result > 0) { *result = CTX->register_internal_fd(*result); } break; #ifdef SYS_inotify_init case SYS_inotify_init: *result = syscall_no_intercept_wrapper(syscall_number); if(*result >= 0) { *result = CTX->register_internal_fd(*result); } break; #endif case SYS_inotify_init1: *result = syscall_no_intercept_wrapper(syscall_number, static_cast(arg0)); if(*result >= 0) { *result = CTX->register_internal_fd(*result); } break; case SYS_perf_event_open: *result = syscall_no_intercept_wrapper( syscall_number, reinterpret_cast(arg0), static_cast(arg1), static_cast(arg2), static_cast(arg3), static_cast(arg4)); if(*result >= 0) { *result = CTX->register_internal_fd(*result); } break; #ifdef SYS_signalfd case SYS_signalfd: *result = syscall_no_intercept_wrapper( syscall_number, static_cast(arg0), reinterpret_cast(arg1)); if(*result >= 0) { *result = CTX->register_internal_fd(*result); } break; #endif case SYS_signalfd4: *result = syscall_no_intercept_wrapper( syscall_number, static_cast(arg0), reinterpret_cast(arg1), static_cast(arg2)); if(*result >= 0) { *result = CTX->register_internal_fd(*result); } break; case SYS_timerfd_create: *result = syscall_no_intercept_wrapper(syscall_number, static_cast(arg0), static_cast(arg1)); if(*result >= 0) { *result = CTX->register_internal_fd(*result); } break; case SYS_socket: *result = syscall_no_intercept_wrapper( syscall_number, static_cast(arg0), static_cast(arg1), static_cast(arg2)); if(*result >= 0) { *result = CTX->register_internal_fd(*result); } break; case SYS_socketpair: *result = syscall_no_intercept_wrapper( syscall_number, static_cast(arg0), static_cast(arg1), static_cast(arg2), reinterpret_cast(arg3)); if(*result >= 0) { reinterpret_cast(arg3)[0] = CTX->register_internal_fd( reinterpret_cast(arg3)[0]); reinterpret_cast(arg3)[1] = CTX->register_internal_fd( reinterpret_cast(arg3)[1]); } break; #ifdef SYS_pipe case SYS_pipe: *result = syscall_no_intercept_wrapper( syscall_number, reinterpret_cast(arg0)); if(*result >= 0) { reinterpret_cast(arg0)[0] = CTX->register_internal_fd( reinterpret_cast(arg0)[0]); reinterpret_cast(arg0)[1] = CTX->register_internal_fd( reinterpret_cast(arg0)[1]); } break; #endif case SYS_pipe2: *result = syscall_no_intercept_wrapper(syscall_number, reinterpret_cast(arg0), static_cast(arg1)); if(*result >= 0) { reinterpret_cast(arg0)[0] = CTX->register_internal_fd( reinterpret_cast(arg0)[0]); reinterpret_cast(arg0)[1] = CTX->register_internal_fd( reinterpret_cast(arg0)[1]); } break; #ifdef SYS_eventfd case SYS_eventfd: *result = syscall_no_intercept_wrapper(syscall_number, static_cast(arg0)); if(*result >= 0) { *result = CTX->register_internal_fd(*result); } break; #endif case SYS_eventfd2: *result = syscall_no_intercept_wrapper(syscall_number, static_cast(arg0), static_cast(arg1)); if(*result >= 0) { *result = CTX->register_internal_fd(*result); } break; case SYS_recvmsg: { *result = syscall_no_intercept_wrapper( syscall_number, static_cast(arg0), reinterpret_cast(arg1), static_cast(arg2)); // The recvmsg() syscall can receive file descriptors from another // process that the kernel automatically adds to the client's fds // as if dup2 had been called. Whenever that happens, we need to // make sure that we register these additional fds as internal, or // we could inadvertently overwrite them if(*result >= 0) { auto* hdr = reinterpret_cast(arg1); struct cmsghdr* cmsg = CMSG_FIRSTHDR(hdr); for(; cmsg != NULL; cmsg = CMSG_NXTHDR(hdr, cmsg)) { if(cmsg->cmsg_type == SCM_RIGHTS) { size_t nfd = cmsg->cmsg_len > CMSG_LEN(0) ? (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int) : 0; int* fds = reinterpret_cast(CMSG_DATA(cmsg)); for(size_t i = 0; i < nfd; ++i) { LOG(DEBUG, "recvmsg() provided extra fd {}", fds[i]); // ensure we update the fds in cmsg // if they have been relocated fds[i] = CTX->register_internal_fd(fds[i]); } } } } break; } case SYS_accept: *result = syscall_no_intercept_wrapper( syscall_number, static_cast(arg0), reinterpret_cast(arg1), reinterpret_cast(arg2)); if(*result >= 0) { *result = CTX->register_internal_fd(*result); } break; case SYS_accept4: *result = syscall_no_intercept_wrapper( syscall_number, static_cast(arg0), reinterpret_cast(arg1), reinterpret_cast(arg2), static_cast(arg3)); if(*result >= 0) { *result = CTX->register_internal_fd(*result); } break; case SYS_fcntl: *result = syscall_no_intercept_wrapper( syscall_number, static_cast(arg0), static_cast(arg1), arg2); if(*result >= 0) { if((static_cast(arg1) == F_DUPFD || static_cast(arg1) == F_DUPFD_CLOEXEC)) { *result = CTX->register_internal_fd(*result); } } break; case SYS_close: *result = syscall_no_intercept_wrapper(syscall_number, static_cast(arg0)); if(*result >= 0) { CTX->unregister_internal_fd(arg0); } break; #ifdef SYS_close_range case SYS_close_range: *result = syscall_no_intercept_wrapper( syscall_number, static_cast(arg0), static_cast(arg1), static_cast(arg2)); if(*result >= 0) { for(auto i = arg0; i < arg1; i++) { if(arg1 == INT_MAX or i >= GKFS_MAX_INTERNAL_FDS) { break; } CTX->unregister_internal_fd(i); } } break; #endif default: // ignore any other syscalls, i.e.: pass them on to the // kernel (syscalls forwarded to the kernel that return are // logged in hook_forwarded_syscall()) ::save_current_syscall_info(gkfs::syscall::from_internal_code | gkfs::syscall::to_kernel | gkfs::syscall::not_executed); return gkfs::syscall::forward_to_kernel; } LOG(SYSCALL, gkfs::syscall::from_internal_code | gkfs::syscall::to_hook | gkfs::syscall::executed, syscall_number, args, *result); return gkfs::syscall::hooked; } /* * hook -- interception hook for application syscalls * * This hook is used to implement any application filesystem-related * syscalls. */ inline int hook(long syscall_number, long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long* result) { #if defined(GKFS_ENABLE_LOGGING) && defined(GKFS_DEBUG_BUILD) const long args[gkfs::syscall::MAX_ARGS] = {arg0, arg1, arg2, arg3, arg4, arg5}; #endif LOG(SYSCALL, gkfs::syscall::from_external_code | gkfs::syscall::to_hook | gkfs::syscall::not_executed, syscall_number, args); switch(syscall_number) { case SYS_execve: *result = syscall_no_intercept_wrapper( syscall_number, reinterpret_cast(arg0), reinterpret_cast(arg1), reinterpret_cast(arg2)); break; #ifdef SYS_execveat case SYS_execveat: *result = syscall_no_intercept_wrapper( syscall_number, arg0, reinterpret_cast(arg1), reinterpret_cast(arg2), reinterpret_cast(arg3), arg4); break; #endif #ifdef SYS_open case SYS_open: *result = gkfs::hook::hook_openat( AT_FDCWD, reinterpret_cast(arg0), static_cast(arg1), static_cast(arg2)); break; #endif #ifdef SYS_creat case SYS_creat: *result = gkfs::hook::hook_openat( AT_FDCWD, reinterpret_cast(arg0), O_WRONLY | O_CREAT | O_TRUNC, static_cast(arg1)); break; #endif case SYS_openat: *result = gkfs::hook::hook_openat( static_cast(arg0), reinterpret_cast(arg1), static_cast(arg2), static_cast(arg3)); break; case SYS_close: *result = gkfs::hook::hook_close(static_cast(arg0)); break; #ifdef SYS_close_range case SYS_close_range: for(auto i = arg0; i <= arg1; i++) { if(i >= GKFS_MAX_OPEN_FDS) break; if(CTX->file_map()->exist(i)) { gkfs::syscall::gkfs_close(i); } *result = 0; } *result = 0; break; #endif // SYS_close_range #ifdef SYS_stat case SYS_stat: *result = gkfs::hook::hook_stat(reinterpret_cast(arg0), reinterpret_cast(arg1)); break; #endif #ifdef STATX_TYPE case SYS_statx: *result = gkfs::hook::hook_statx( static_cast(arg0), reinterpret_cast(arg1), static_cast(arg2), static_cast(arg3), reinterpret_cast(arg4)); break; #endif #ifdef SYS_lstat case SYS_lstat: *result = gkfs::hook::hook_lstat( reinterpret_cast(arg0), reinterpret_cast(arg1)); break; #endif case SYS_fstat: *result = gkfs::hook::hook_fstat( static_cast(arg0), reinterpret_cast(arg1)); break; case SYS_newfstatat: *result = gkfs::hook::hook_fstatat( static_cast(arg0), reinterpret_cast(arg1), reinterpret_cast(arg2), static_cast(arg3)); break; case SYS_read: *result = gkfs::hook::hook_read(static_cast(arg0), reinterpret_cast(arg1), static_cast(arg2)); break; case SYS_pread64: *result = gkfs::hook::hook_pread(static_cast(arg0), reinterpret_cast(arg1), static_cast(arg2), static_cast(arg3)); break; case SYS_readv: *result = gkfs::hook::hook_readv( static_cast(arg0), reinterpret_cast(arg1), static_cast(arg2)); break; case SYS_preadv: *result = gkfs::hook::hook_preadv( static_cast(arg0), reinterpret_cast(arg1), static_cast(arg2), static_cast(arg3), static_cast(arg4)); break; case SYS_pwrite64: *result = gkfs::hook::hook_pwrite( static_cast(arg0), reinterpret_cast(arg1), static_cast(arg2), static_cast(arg3)); break; case SYS_write: *result = gkfs::hook::hook_write(static_cast(arg0), reinterpret_cast(arg1), static_cast(arg2)); break; case SYS_writev: *result = gkfs::hook::hook_writev( static_cast(arg0), reinterpret_cast(arg1), static_cast(arg2)); break; case SYS_pwritev: *result = gkfs::hook::hook_pwritev( static_cast(arg0), reinterpret_cast(arg1), static_cast(arg2), static_cast(arg3), static_cast(arg4)); break; #ifdef SYS_unlink case SYS_unlink: *result = gkfs::hook::hook_unlinkat( AT_FDCWD, reinterpret_cast(arg0), 0); break; #endif case SYS_unlinkat: *result = gkfs::hook::hook_unlinkat( static_cast(arg0), reinterpret_cast(arg1), static_cast(arg2)); break; #ifdef SYS_rmdir case SYS_rmdir: *result = gkfs::hook::hook_unlinkat( AT_FDCWD, reinterpret_cast(arg0), AT_REMOVEDIR); break; #endif #ifdef SYS_symlink case SYS_symlink: *result = gkfs::hook::hook_symlinkat( reinterpret_cast(arg0), AT_FDCWD, reinterpret_cast(arg1)); break; #endif case SYS_symlinkat: *result = gkfs::hook::hook_symlinkat( reinterpret_cast(arg0), static_cast(arg1), reinterpret_cast(arg2)); break; #ifdef SYS_access case SYS_access: *result = gkfs::hook::hook_access(reinterpret_cast(arg0), static_cast(arg1)); break; #endif case SYS_faccessat: *result = gkfs::hook::hook_faccessat( static_cast(arg0), reinterpret_cast(arg1), static_cast(arg2)); break; #ifdef SYS_faccessat2 case SYS_faccessat2: *result = gkfs::hook::hook_faccessat2( static_cast(arg0), reinterpret_cast(arg1), static_cast(arg2), static_cast(arg3)); break; #endif case SYS_lseek: *result = gkfs::hook::hook_lseek(static_cast(arg0), static_cast(arg1), static_cast(arg2)); break; case SYS_truncate: *result = gkfs::hook::hook_truncate( reinterpret_cast(arg0), static_cast(arg1)); break; case SYS_ftruncate: *result = gkfs::hook::hook_ftruncate( static_cast(arg0), static_cast(arg1)); break; case SYS_dup: *result = gkfs::hook::hook_dup(static_cast(arg0)); break; #ifdef SYS_dup2 case SYS_dup2: *result = gkfs::hook::hook_dup2(static_cast(arg0), static_cast(arg1)); break; #endif case SYS_dup3: *result = gkfs::hook::hook_dup3(static_cast(arg0), static_cast(arg1), static_cast(arg2)); break; #ifdef SYS_getdents case SYS_getdents: *result = gkfs::hook::hook_getdents( static_cast(arg0), reinterpret_cast(arg1), static_cast(arg2)); break; #endif case SYS_getdents64: *result = gkfs::hook::hook_getdents64( static_cast(arg0), reinterpret_cast(arg1), static_cast(arg2)); break; case SYS_mkdirat: *result = gkfs::hook::hook_mkdirat( static_cast(arg0), reinterpret_cast(arg1), static_cast(arg2)); break; #ifdef SYS_mkdir case SYS_mkdir: *result = gkfs::hook::hook_mkdirat( AT_FDCWD, reinterpret_cast(arg0), static_cast(arg1)); break; #endif #ifdef SYS_chmod case SYS_chmod: *result = gkfs::hook::hook_fchmodat(AT_FDCWD, reinterpret_cast(arg0), static_cast(arg1)); break; #endif case SYS_fchmod: *result = gkfs::hook::hook_fchmod(static_cast(arg0), static_cast(arg1)); break; case SYS_fchmodat: *result = gkfs::hook::hook_fchmodat(static_cast(arg0), reinterpret_cast(arg1), static_cast(arg2)); break; case SYS_flock: *result = gkfs::hook::hook_flock(static_cast(arg0), static_cast(arg1)); break; case SYS_chdir: *result = gkfs::hook::hook_chdir(reinterpret_cast(arg0)); break; case SYS_fchdir: *result = gkfs::hook::hook_fchdir(static_cast(arg0)); break; case SYS_getcwd: *result = gkfs::hook::hook_getcwd(reinterpret_cast(arg0), static_cast(arg1)); break; #ifdef SYS_readlink case SYS_readlink: *result = gkfs::hook::hook_readlinkat( AT_FDCWD, reinterpret_cast(arg0), reinterpret_cast(arg1), static_cast(arg2)); break; #endif case SYS_readlinkat: *result = gkfs::hook::hook_readlinkat( static_cast(arg0), reinterpret_cast(arg1), reinterpret_cast(arg2), static_cast(arg3)); break; case SYS_fcntl: *result = gkfs::hook::hook_fcntl(static_cast(arg0), static_cast(arg1), static_cast(arg2)); break; #ifdef SYS_rename case SYS_rename: *result = gkfs::hook::hook_renameat( AT_FDCWD, reinterpret_cast(arg0), AT_FDCWD, reinterpret_cast(arg1), 0); break; #endif #ifdef SYS_renameat case SYS_renameat: *result = gkfs::hook::hook_renameat( static_cast(arg0), reinterpret_cast(arg1), static_cast(arg2), reinterpret_cast(arg3), 0); break; #endif #ifdef SYS_renameat2 case SYS_renameat2: *result = gkfs::hook::hook_renameat( static_cast(arg0), reinterpret_cast(arg1), static_cast(arg2), reinterpret_cast(arg3), static_cast(arg4)); break; #endif case SYS_fstatfs: *result = gkfs::hook::hook_fstatfs( static_cast(arg0), reinterpret_cast(arg1)); break; case SYS_statfs: *result = gkfs::hook::hook_statfs( reinterpret_cast(arg0), reinterpret_cast(arg1)); break; case SYS_fdatasync: case SYS_fsync: *result = gkfs::hook::hook_fsync(static_cast(arg0)); break; case SYS_getxattr: *result = gkfs::hook::hook_getxattr( reinterpret_cast(arg0), reinterpret_cast(arg1), reinterpret_cast(arg2), static_cast(arg4)); break; #ifdef SYS_listxattr case SYS_listxattr: *result = gkfs::hook::hook_listxattr( reinterpret_cast(arg0), reinterpret_cast(arg1), static_cast(arg2)); break; #endif #ifdef SYS_llistxattr case SYS_llistxattr: *result = gkfs::hook::hook_llistxattr( reinterpret_cast(arg0), reinterpret_cast(arg1), static_cast(arg2)); break; #endif #ifdef SYS_flistxattr case SYS_flistxattr: *result = gkfs::hook::hook_flistxattr(reinterpret_cast(arg0), reinterpret_cast(arg1), static_cast(arg2)); break; #endif case SYS_lgetxattr: *result = gkfs::hook::hook_lgetxattr( reinterpret_cast(arg0), reinterpret_cast(arg1), reinterpret_cast(arg2), static_cast(arg4)); break; case SYS_fallocate: *result = gkfs::hook::hook_fallocate( static_cast(arg0), static_cast(arg1), static_cast(arg2), static_cast(arg3)); break; case SYS_fadvise64: *result = gkfs::hook::hook_fadvise64( static_cast(arg0), static_cast(arg1), static_cast(arg2), static_cast(arg4)); break; default: // ignore any other syscalls, i.e.: pass them on to the kernel // (syscalls forwarded to the kernel that return are logged in // hook_forwarded_syscall()) ::save_current_syscall_info(gkfs::syscall::from_external_code | gkfs::syscall::to_kernel | gkfs::syscall::not_executed); return gkfs::syscall::forward_to_kernel; } LOG(SYSCALL, gkfs::syscall::from_external_code | gkfs::syscall::to_hook | gkfs::syscall::executed, syscall_number, args, *result); return gkfs::syscall::hooked; } #ifdef SYS_socketcall /* Wraps socketcall in powerpc9, we only change syscalls that need special * treatment */ long socketcall_wrapper(long syscall_number, long& arg0, long& arg1, long& arg2, long& arg3, long& arg4, long& arg5) { switch(static_cast(arg0)) { case 1: syscall_number = SYS_socket; break; case 5: syscall_number = SYS_accept; break; case 17: syscall_number = SYS_recvmsg; break; case 18: syscall_number = SYS_accept4; break; case 19: syscall_number = SYS_recvmmsg; break; default: break; } if(syscall_number != SYS_socketcall) { long int* parameters = (long int*) arg1; arg0 = static_cast(*parameters); parameters++; arg1 = static_cast(*parameters); parameters++; arg2 = static_cast(*parameters); parameters++; arg3 = static_cast(*parameters); parameters++; arg4 = static_cast(*parameters); parameters++; arg5 = static_cast(*parameters); } return syscall_number; } #endif void hook_forwarded_syscall(long syscall_number, long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long result) { if(::get_current_syscall_info() == gkfs::syscall::no_info) { return; } #if defined(GKFS_ENABLE_LOGGING) && defined(GKFS_DEBUG_BUILD) const long args[gkfs::syscall::MAX_ARGS] = {arg0, arg1, arg2, arg3, arg4, arg5}; #endif LOG(SYSCALL, ::get_current_syscall_info() | gkfs::syscall::executed, syscall_number, args, result); ::reset_current_syscall_info(); } void hook_clone_at_child(unsigned long flags, void* child_stack, int* ptid, int* ctid, long newtls) { #if defined(GKFS_ENABLE_LOGGING) && defined(GKFS_DEBUG_BUILD) const long args[gkfs::syscall::MAX_ARGS] = { static_cast(flags), reinterpret_cast(child_stack), reinterpret_cast(ptid), reinterpret_cast(ctid), static_cast(newtls), 0}; #endif reentrance_guard_flag = true; LOG(SYSCALL, ::get_current_syscall_info() | gkfs::syscall::executed, SYS_clone, args, 0); reentrance_guard_flag = false; } void hook_clone_at_parent(unsigned long flags, void* child_stack, int* ptid, int* ctid, long newtls, long returned_pid) { #if defined(GKFS_ENABLE_LOGGING) && defined(GKFS_DEBUG_BUILD) const long args[gkfs::syscall::MAX_ARGS] = { static_cast(flags), reinterpret_cast(child_stack), reinterpret_cast(ptid), reinterpret_cast(ctid), static_cast(newtls), 0}; #endif reentrance_guard_flag = true; LOG(SYSCALL, ::get_current_syscall_info() | gkfs::syscall::executed, SYS_clone, args, returned_pid); reentrance_guard_flag = false; } } // namespace namespace gkfs::preload { int internal_hook_guard_wrapper(long syscall_number, long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long* syscall_return_value) { assert(CTX->interception_enabled()); #ifdef SYS_socketcall if(syscall_number == SYS_socketcall) syscall_number = socketcall_wrapper(syscall_number, arg0, arg1, arg2, arg3, arg4, arg5); #endif if(reentrance_guard_flag) { ::save_current_syscall_info(gkfs::syscall::from_internal_code | gkfs::syscall::to_kernel | gkfs::syscall::not_executed); return gkfs::syscall::forward_to_kernel; } int was_hooked = 0; reentrance_guard_flag = true; was_hooked = hook_internal(syscall_number, arg0, arg1, arg2, arg3, arg4, arg5, syscall_return_value); reentrance_guard_flag = false; return was_hooked; } /* * hook_guard_wrapper -- a wrapper which can notice reentrance. * * The reentrance_guard_flag flag allows the library to distinguish the hooking * of its own syscalls. E.g. while handling an open() syscall, * libgkfs_intercept might call fopen(), which in turn uses an open() * syscall internally. This internally used open() syscall is once again * forwarded to libgkfs_intercept, but using this flag we can notice this * case of reentering itself. * * XXX This approach still contains a very significant bug, as libgkfs_intercept * being called inside a signal handler might easily forward a mock fd to the * kernel. */ int hook_guard_wrapper(long syscall_number, long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long* syscall_return_value) { assert(CTX->interception_enabled()); #ifdef SYS_socketcall if(syscall_number == SYS_socketcall) syscall_number = socketcall_wrapper(syscall_number, arg0, arg1, arg2, arg3, arg4, arg5); #endif 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; return was_hooked; } void start_self_interception() { LOG(DEBUG, "Enabling syscall interception for self"); intercept_hook_point = internal_hook_guard_wrapper; intercept_hook_point_post_kernel = hook_forwarded_syscall; intercept_hook_point_clone_child = hook_clone_at_child; intercept_hook_point_clone_parent = hook_clone_at_parent; } void start_interception() { assert(CTX->interception_enabled()); LOG(DEBUG, "Enabling syscall interception for client process"); // Set up the callback function pointer intercept_hook_point = hook_guard_wrapper; intercept_hook_point_post_kernel = hook_forwarded_syscall; intercept_hook_point_clone_child = hook_clone_at_child; intercept_hook_point_clone_parent = hook_clone_at_parent; } void stop_interception() { assert(CTX->interception_enabled()); LOG(DEBUG, "Disabling syscall interception for client process"); // Reset callback function pointer intercept_hook_point = nullptr; intercept_hook_point_post_kernel = nullptr; intercept_hook_point_clone_child = nullptr; intercept_hook_point_clone_parent = nullptr; } } // namespace gkfs::preload