Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 1 addition & 87 deletions src/hooks/syscalls_hc.c
Original file line number Diff line number Diff line change
Expand Up @@ -300,8 +300,8 @@ static long process_syscall_hooks(
long modified_ret = orig_ret;
struct kernel_syscall_hook *hook;
int i;
bool info_initialized = false;
rcu_read_lock();
fill_handler(&original_info, argc, args, syscall_name);
// 1. Check the "match all syscalls" list first
if (!hlist_empty(&syscall_all_hooks)) {
hlist_for_each_entry_rcu(hook, &syscall_all_hooks, name_hlist) {
Expand All @@ -312,10 +312,6 @@ static long process_syscall_hooks(
matches = hook->hook.on_return && hook_matches_syscall_return(hook, syscall_name, argc, args, modified_ret);
}
if (matches) {
if (unlikely(!info_initialized)) {
fill_handler(&original_info, argc, args, syscall_name);
info_initialized = true;
}
memcpy(&syscall_args_holder, &original_info, sizeof(struct syscall_event));
syscall_args_holder.hook = &hook->hook;
if (!is_entry) syscall_args_holder.retval = modified_ret;
Expand Down Expand Up @@ -359,10 +355,6 @@ static long process_syscall_hooks(
matches = hook->hook.on_return && hook_matches_syscall_return(hook, syscall_name, argc, args, modified_ret);
}
if (matches) {
if (unlikely(!info_initialized)) {
fill_handler(&original_info, argc, args, syscall_name);
info_initialized = true;
}
memcpy(&syscall_args_holder, &original_info, sizeof(struct syscall_event));
syscall_args_holder.hook = &hook->hook;
if (!is_entry) syscall_args_holder.retval = modified_ret;
Expand Down Expand Up @@ -464,81 +456,3 @@ int syscalls_hc_init(void) {
hash_init(syscall_hook_table);
return 0;
}

/* Deferred unregistration so we can invoke from inside a syscall callback */
struct deferred_unregister {
struct kernel_syscall_hook *hook;
struct list_head list;
};
static LIST_HEAD(deferred_unregister_list);
static DEFINE_SPINLOCK(deferred_unregister_lock);

static void process_deferred_unregisters(struct work_struct *work)
{
struct deferred_unregister *deferred, *tmp;
LIST_HEAD(local_list);

// Move all pending unregistrations to local list
spin_lock(&deferred_unregister_lock);
list_splice_init(&deferred_unregister_list, &local_list);
spin_unlock(&deferred_unregister_lock);

// Process unregistrations outside of spinlock
list_for_each_entry_safe(deferred, tmp, &local_list, list) {
DBG_PRINTK("IGLOO: Processing deferred unregistration for hook %p\n",
deferred->hook);

// Actually unregister the hook
unregister_syscall_hook(deferred->hook);

// Clean up the deferred entry
list_del(&deferred->list);
kfree(deferred);
}
}

static DECLARE_WORK(deferred_unregister_work, process_deferred_unregisters);

static int do_unregister_syscall_hook(struct kernel_syscall_hook *hook_ptr)
{
if (!hook_ptr) {
return -EINVAL;
}

spin_lock(&syscall_hook_lock);

hash_del(&hook_ptr->hlist);
hlist_del_rcu(&hook_ptr->name_hlist);

spin_unlock(&syscall_hook_lock);

kfree_rcu(hook_ptr, rcu);

DBG_PRINTK("IGLOO: Unregistered syscall hook %p\n", hook_ptr);
return 0;
}

// Modified unregister function
int unregister_syscall_hook(struct kernel_syscall_hook *hook_ptr)
{
struct deferred_unregister *deferred;

// Check if we're in syscall processing context
if (in_interrupt() || rcu_read_lock_held()) {
// Defer the unregistration
deferred = kmalloc(sizeof(*deferred), GFP_ATOMIC);
if (!deferred) return -ENOMEM;

deferred->hook = hook_ptr;
spin_lock(&deferred_unregister_lock);
list_add_tail(&deferred->list, &deferred_unregister_list);
spin_unlock(&deferred_unregister_lock);

// Schedule work to process deferred unregistrations
schedule_work(&deferred_unregister_work);
return 0;
}

// Safe to unregister immediately
return do_unregister_syscall_hook(hook_ptr);
}
29 changes: 13 additions & 16 deletions src/hooks/syscalls_hc.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,18 @@ struct syscall_hook {
bool on_return; /* Hook on syscall return */
bool on_all; /* Hook on all syscalls */
char name[SYSCALL_NAME_MAX_LEN]; /* Name of syscall to hook */

/* PID filtering */
bool pid_filter_enabled; /* Enable PID filtering */
pid_t filter_pid; /* Process ID to filter on */

/* Process filtering */
bool comm_filter_enabled; /* Enable process name filtering */
char comm_filter[TASK_COMM_LEN]; /* Process name to match */

/* Argument filtering with complex comparisons */
struct value_filter arg_filters[IGLOO_SYSCALL_MAXARGS]; /* Argument filters */

/* Return value filtering with complex comparisons */
struct value_filter retval_filter; /* Return value filter */
};
Expand Down Expand Up @@ -84,36 +84,33 @@ extern struct hlist_head syscall_hook_table[1024];
extern spinlock_t syscall_hook_lock;


/* Unregister a syscall hook using its pointer */
int unregister_syscall_hook(struct kernel_syscall_hook *hook_ptr);

int syscalls_hc_init(void);

/* Normalize syscall names by removing common prefixes like 'sys_', '_sys_', 'compat_sys_' */
static inline const char *normalize_syscall_name(const char *name)
{
if (!name)
return NULL;

/* Skip leading underscores (e.g. _sys_) */
while (*name == '_')
name++;

/* Check for 'sys_' prefix */
if (strncmp(name, "sys_", 4) == 0)
return name + 4;

/* Check for 'compat_sys_' prefix */
if (strncmp(name, "compat_sys_", 11) == 0)
return name + 11;

/* Check for other arch-specific prefixes */
if (strncmp(name, "arm64_sys_", 10) == 0)
return name + 10;

if (strncmp(name, "riscv_sys_", 10) == 0)
return name + 10;

return name;
}

Expand All @@ -123,11 +120,11 @@ static inline u32 syscall_name_hash(const char *str)
const char *normalized;
if (!str)
return 0;

// First normalize the syscall name to handle various prefixes
normalized = normalize_syscall_name(str);

return full_name_hash(NULL, normalized, strlen(normalized));
}

#endif /* _SYSCALLS_HC_H */
#endif /* _SYSCALLS_HC_H */
1 change: 1 addition & 0 deletions src/portal/portal_op_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
X(register_uprobe, REGISTER_UPROBE) \
X(unregister_uprobe, UNREGISTER_UPROBE) \
X(register_syscall_hook, REGISTER_SYSCALL_HOOK) \
X(unregister_syscall_hook, UNREGISTER_SYSCALL_HOOK) \
X(ffi_exec, FFI_EXEC) \
X(kallsyms_lookup, KALLSYMS_LOOKUP) \
X(tramp_generate, TRAMP_GENERATE) \
Expand Down
112 changes: 103 additions & 9 deletions src/portal/portal_syscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,20 @@ void handle_op_register_syscall_hook(portal_region *mem_region)
{
struct syscall_hook *hook;
struct kernel_syscall_hook *kernel_hook;

igloo_pr_debug("igloo: Handling HYPER_OP_REGISTER_SYSCALL_HOOK\n");

// Map the data buffer to our hook structure
hook = (struct syscall_hook *)PORTAL_DATA(mem_region);

// Allocate a new kernel hook structure
kernel_hook = kzalloc(sizeof(*kernel_hook), GFP_KERNEL);
if (!kernel_hook) {
igloo_pr_debug("igloo: Failed to allocate kernel_hook structure\n");
mem_region->header.op = HYPER_RESP_READ_FAIL;
return;
}

// Copy the hook configuration
memcpy(&kernel_hook->hook, hook, sizeof(struct syscall_hook));
kernel_hook->in_use = true;
Expand All @@ -46,11 +46,11 @@ void handle_op_register_syscall_hook(portal_region *mem_region)
} else {
kernel_hook->normalized_name[0] = '\0';
}

// Add to the main hook table indexed by pointer address
spin_lock(&syscall_hook_lock);
hash_add_rcu(syscall_hook_table, &kernel_hook->hlist, (unsigned long)kernel_hook);

// Also add to name-based hash table for faster lookups
if (kernel_hook->hook.on_all) {
// Special case for hooks that want all syscalls
Expand All @@ -60,10 +60,104 @@ void handle_op_register_syscall_hook(portal_region *mem_region)
u32 name_hash = syscall_name_hash(kernel_hook->hook.name);
hash_add_rcu(syscall_name_table, &kernel_hook->name_hlist, name_hash);
}

spin_unlock(&syscall_hook_lock);

// Return the hook's memory address in the size field
mem_region->header.size = (unsigned long)kernel_hook;
mem_region->header.op = HYPER_RESP_READ_NUM;
}
}

/* Deferred unregistration so we can invoke from inside a syscall callback */
struct deferred_unregister {
struct kernel_syscall_hook *hook;
struct list_head list;
};
static LIST_HEAD(deferred_unregister_list);
static DEFINE_SPINLOCK(deferred_unregister_lock);

static int do_unregister_syscall_hook(struct kernel_syscall_hook *hook_ptr)
{
if (!hook_ptr) {
return -EINVAL;
}

spin_lock(&syscall_hook_lock);

hash_del(&hook_ptr->hlist);
hlist_del_rcu(&hook_ptr->name_hlist);

spin_unlock(&syscall_hook_lock);

kfree_rcu(hook_ptr, rcu);

igloo_pr_debug("igloo: Unregistered syscall hook %p\n", hook_ptr);
return 0;
}

static void process_deferred_unregisters(struct work_struct *work)
{
struct deferred_unregister *deferred, *tmp;
LIST_HEAD(local_list);

// Move all pending unregistrations to local list
spin_lock(&deferred_unregister_lock);
list_splice_init(&deferred_unregister_list, &local_list);
spin_unlock(&deferred_unregister_lock);

// Process unregistrations outside of spinlock
list_for_each_entry_safe(deferred, tmp, &local_list, list) {
igloo_pr_debug("IGLOO: Processing deferred unregistration for hook %p\n",
deferred->hook);

// Actually unregister the hook
do_unregister_syscall_hook(deferred->hook);

// Clean up the deferred entry
list_del(&deferred->list);
kfree(deferred);
}
}

static DECLARE_WORK(deferred_unregister_work, process_deferred_unregisters);


void handle_op_unregister_syscall_hook(portal_region *mem_region)
{
struct deferred_unregister *deferred;
struct kernel_syscall_hook *hook_ptr;

igloo_pr_debug("igloo: Handling HYPER_OP_UNREGISTER_SYSCALL_HOOK\n");

// Map the data buffer to our hook structure
hook_ptr = (struct kernel_syscall_hook *)PORTAL_DATA(mem_region);

//First, disable hook
WRITE_ONCE(hook_ptr->hook.enabled, false);

// Check if we're in syscall processing context
if (in_interrupt() || rcu_read_lock_held()) {
// Defer the unregistration
deferred = kmalloc(sizeof(*deferred), GFP_ATOMIC);
if (!deferred) {
mem_region->header.op = HYPER_RESP_READ_FAIL;
return;
}

deferred->hook = hook_ptr;
spin_lock(&deferred_unregister_lock);
list_add_tail(&deferred->list, &deferred_unregister_list);
spin_unlock(&deferred_unregister_lock);

// Schedule work to process deferred unregistrations
schedule_work(&deferred_unregister_work);
mem_region->header.op = HYPER_RESP_READ_NUM;
mem_region->header.size = (unsigned long)0;
return;
}

// Safe to unregister immediately
do_unregister_syscall_hook(hook_ptr);
mem_region->header.op = HYPER_RESP_READ_NUM;
mem_region->header.size = (unsigned long)0;
}