From a76d9531b5b6b5410cfd8fcd6ce5b781555efa92 Mon Sep 17 00:00:00 2001 From: Luke Craig Date: Sun, 17 May 2026 12:59:35 -0400 Subject: [PATCH 1/3] fix(driver): preserve x86 hypercall registers --- src/ehypercall.h | 17 ++++++++++++----- src/hooks/block_mounts.c | 2 +- src/hooks/igloo_open.c | 2 +- src/hooks/ioctl_hc.c | 2 +- src/hooks/signal_hc.c | 2 +- src/hooks/sock_hc.c | 2 +- src/hooks/syscalls_hc.c | 2 +- src/hooks/uname_hc.c | 2 +- src/hyperfs/hyperfs.c | 2 +- src/igloo_hc.c | 2 +- src/igloo_hypercall.h | 28 ++++++++++++++++++++++++++++ src/portal/portal_internal.h | 2 +- 12 files changed, 50 insertions(+), 15 deletions(-) create mode 100644 src/igloo_hypercall.h diff --git a/src/ehypercall.h b/src/ehypercall.h index 8f9def7..3ab906c 100644 --- a/src/ehypercall.h +++ b/src/ehypercall.h @@ -48,13 +48,16 @@ static inline unsigned long igloo_hypercall4(unsigned long num, unsigned long ar return reg0; #elif defined(CONFIG_X86_64) - unsigned long reg0 = num; + register unsigned long reg0 asm("rax") = num; + register unsigned long reg1 asm("rdi") = arg1; + register unsigned long reg2 asm("rsi") = arg2; + register unsigned long reg3 asm("rdx") = arg3; register unsigned long reg4 asm("r10") = arg4; // r10 used for 4th arg in syscall ABI asm volatile( "outl %%eax, $0x88" : "+a"(reg0) // hypercall num + return value in rax - : "D"(arg1), "S"(arg2), "d"(arg3), "r"(reg4) + : "r"(reg1), "r"(reg2), "r"(reg3), "r"(reg4) : "memory" ); @@ -62,12 +65,16 @@ static inline unsigned long igloo_hypercall4(unsigned long num, unsigned long ar #elif defined(CONFIG_I386) // Matches the "other implementation" (standard Linux Syscall ABI) - unsigned long reg0 = num; + register unsigned long reg0 asm("eax") = num; + register unsigned long reg1 asm("ebx") = arg1; + register unsigned long reg2 asm("ecx") = arg2; + register unsigned long reg3 asm("edx") = arg3; + register unsigned long reg4 asm("esi") = arg4; asm volatile( "outl %%eax, $0x88" : "+a"(reg0) // hypercall num + return value in eax - : "b"(arg1), "c"(arg2), "d"(arg3), "S"(arg4) + : "r"(reg1), "r"(reg2), "r"(reg3), "r"(reg4) : "memory" ); @@ -124,4 +131,4 @@ static inline unsigned long igloo_hypercall4(unsigned long num, unsigned long ar #error "No igloo_hypercall4 support for architecture" #endif } -#endif \ No newline at end of file +#endif diff --git a/src/hooks/block_mounts.c b/src/hooks/block_mounts.c index babcc6d..3359640 100644 --- a/src/hooks/block_mounts.c +++ b/src/hooks/block_mounts.c @@ -6,7 +6,7 @@ #include #include #include -#include "hypercall.h" +#include "igloo_hypercall.h" #include "igloo.h" bool igloo_should_block_mount(struct path *path); diff --git a/src/hooks/igloo_open.c b/src/hooks/igloo_open.c index 0cea9ce..fec3651 100644 --- a/src/hooks/igloo_open.c +++ b/src/hooks/igloo_open.c @@ -6,7 +6,7 @@ #include #include #include -#include "hypercall.h" +#include "igloo_hypercall.h" #include "igloo.h" #include #include diff --git a/src/hooks/ioctl_hc.c b/src/hooks/ioctl_hc.c index a2fc890..831d6f1 100644 --- a/src/hooks/ioctl_hc.c +++ b/src/hooks/ioctl_hc.c @@ -5,7 +5,7 @@ #include #include #include -#include "hypercall.h" +#include "igloo_hypercall.h" #include "igloo.h" #include #include diff --git a/src/hooks/signal_hc.c b/src/hooks/signal_hc.c index 4e5d07b..0fc8058 100644 --- a/src/hooks/signal_hc.c +++ b/src/hooks/signal_hc.c @@ -5,7 +5,7 @@ #include #include #include -#include "hypercall.h" +#include "igloo_hypercall.h" #include "igloo.h" #include "signal_hc.h" #include "portal/portal.h" diff --git a/src/hooks/sock_hc.c b/src/hooks/sock_hc.c index 93ee2fb..a0c0a37 100644 --- a/src/hooks/sock_hc.c +++ b/src/hooks/sock_hc.c @@ -4,7 +4,7 @@ #include #include #include -#include "hypercall.h" +#include "igloo_hypercall.h" #include #include #include diff --git a/src/hooks/syscalls_hc.c b/src/hooks/syscalls_hc.c index da29f0e..52bf0b3 100644 --- a/src/hooks/syscalls_hc.c +++ b/src/hooks/syscalls_hc.c @@ -7,7 +7,7 @@ #include #include #include -#include "hypercall.h" // Content is now included directly below +#include "igloo_hypercall.h" // Content is now included directly below #include "igloo.h" #include #include diff --git a/src/hooks/uname_hc.c b/src/hooks/uname_hc.c index 55c7957..3233f19 100644 --- a/src/hooks/uname_hc.c +++ b/src/hooks/uname_hc.c @@ -5,7 +5,7 @@ #include #include #include -#include "hypercall.h" +#include "igloo_hypercall.h" #include "igloo.h" #include #include diff --git a/src/hyperfs/hyperfs.c b/src/hyperfs/hyperfs.c index 675a6f0..b28d092 100644 --- a/src/hyperfs/hyperfs.c +++ b/src/hyperfs/hyperfs.c @@ -6,7 +6,7 @@ #include #include #include -#include "hypercall.h" +#include "igloo_hypercall.h" #include "portal/portal.h" // #include "../internal.h" #include "ioctl_hc.h" diff --git a/src/igloo_hc.c b/src/igloo_hc.c index 237047f..c6e17e6 100644 --- a/src/igloo_hc.c +++ b/src/igloo_hc.c @@ -11,7 +11,7 @@ #include #include "syscalls_hc.h" #include "portal/portal.h" -#include "hypercall.h" +#include "igloo_hypercall.h" #include "igloo_hypercall_consts.h" #include "hyperfs/hyperfs.h" diff --git a/src/igloo_hypercall.h b/src/igloo_hypercall.h new file mode 100644 index 0000000..05e985f --- /dev/null +++ b/src/igloo_hypercall.h @@ -0,0 +1,28 @@ +#ifndef IGLOO_DRIVER_HYPERCALL_H +#define IGLOO_DRIVER_HYPERCALL_H + +#include +#include "ehypercall.h" + +static inline unsigned long igloo_hypercall(unsigned long num, + unsigned long arg1) +{ + return igloo_hypercall4(num, arg1, 0, 0, 0); +} + +static inline unsigned long igloo_hypercall2(unsigned long num, + unsigned long arg1, + unsigned long arg2) +{ + return igloo_hypercall4(num, arg1, arg2, 0, 0); +} + +static inline unsigned long igloo_hypercall3(unsigned long num, + unsigned long arg1, + unsigned long arg2, + unsigned long arg3) +{ + return igloo_hypercall4(num, arg1, arg2, arg3, 0); +} + +#endif diff --git a/src/portal/portal_internal.h b/src/portal/portal_internal.h index d75a35b..568a6d0 100644 --- a/src/portal/portal_internal.h +++ b/src/portal/portal_internal.h @@ -18,7 +18,7 @@ #include #include #include -#include "hypercall.h" +#include "igloo_hypercall.h" #include "igloo.h" #include "syscalls_hc.h" #include "igloo_debug.h" From a2107ab0350e15cf7dd3abacce578d3bb6902518 Mon Sep 17 00:00:00 2001 From: Luke Craig Date: Sun, 17 May 2026 13:22:13 -0400 Subject: [PATCH 2/3] fix(driver): route pid procfs and signals through kernel hooks --- src/hooks/signal_hc.c | 57 ++++----------------------- src/portal/portal_procfs.c | 79 ++++++++++++++++++++++++++------------ src/portal/portal_types.h | 1 + 3 files changed, 63 insertions(+), 74 deletions(-) diff --git a/src/hooks/signal_hc.c b/src/hooks/signal_hc.c index 0fc8058..0126f65 100644 --- a/src/hooks/signal_hc.c +++ b/src/hooks/signal_hc.c @@ -1,10 +1,10 @@ #include #include #include -#include -#include #include #include +#include +#include #include "igloo_hypercall.h" #include "igloo.h" #include "signal_hc.h" @@ -18,12 +18,6 @@ static struct hlist_head any_signal_hooks; static DEFINE_SPINLOCK(signal_hook_lock); static atomic_t global_signal_hook_count = ATOMIC_INIT(0); -struct signal_probe_data { - struct task_struct *task; -}; - -static struct kretprobe signal_rp; - static void do_signal_hyp(struct signal_event *event) { igloo_portal(IGLOO_HYP_SIGNAL_DELIVER, (unsigned long)event, 0); } @@ -41,37 +35,16 @@ static bool hook_matches_signal(struct kernel_signal_hook *hook, int sig, struct return true; } -static int handler_entry(struct kretprobe_instance *ri, struct pt_regs *regs) { - struct signal_probe_data *data = (struct signal_probe_data *)ri->data; - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0) - data->task = current; -#else - data->task = (struct task_struct *)regs_get_argument(regs, 0); - if (!data->task) - data->task = current; -#endif - - return 0; -} - -static int handler_ret(struct kretprobe_instance *ri, struct pt_regs *regs) { - int sig; - struct task_struct *t; +static bool igloo_signal_deliver(int sig, struct task_struct *t) { struct kernel_signal_hook *hook; struct signal_event event; bool drop = false; bool needs_hyp = false; - struct signal_probe_data *data = (struct signal_probe_data *)ri->data; if (atomic_read(&global_signal_hook_count) == 0) - return 0; - - sig = (int)igloo_regs_get_return_value(regs); - if (sig <= 0) - return 0; + return false; - t = data->task ? data->task : current; + if (!t) return false; rcu_read_lock(); @@ -114,30 +87,14 @@ static int handler_ret(struct kretprobe_instance *ri, struct pt_regs *regs) { } rcu_read_unlock(); - if (drop) { - igloo_regs_set_return_value(regs, 0); - } - - return 0; + return drop; } int signal_hc_init(void) { - int ret; hash_init(signal_hook_table); INIT_HLIST_HEAD(&any_signal_hooks); - memset(&signal_rp, 0, sizeof(signal_rp)); - signal_rp.kp.symbol_name = "dequeue_signal"; - signal_rp.entry_handler = handler_entry; - signal_rp.handler = handler_ret; - signal_rp.data_size = sizeof(struct signal_probe_data); - signal_rp.maxactive = 64; - - ret = register_kretprobe(&signal_rp); - if (ret < 0) - printk(KERN_ERR "IGLOO: Failed to register kretprobe on dequeue_signal: %d\n", ret); - else - printk(KERN_INFO "IGLOO: Registered signal kretprobe on dequeue_signal\n"); + igloo_signal_deliver_hook = igloo_signal_deliver; return 0; } diff --git a/src/portal/portal_procfs.c b/src/portal/portal_procfs.c index f659679..69eebaa 100644 --- a/src/portal/portal_procfs.c +++ b/src/portal/portal_procfs.c @@ -301,7 +301,8 @@ igloo_convert_ops_to_proc_ops(const struct igloo_proc_ops *ops, struct proc_ops out->proc_get_unmapped_area = ops->get_unmapped_area; return out; } -#else +#endif + static inline const struct file_operations * igloo_convert_ops_to_fops(const struct igloo_proc_ops *ops, struct file_operations *out) { @@ -325,7 +326,6 @@ igloo_convert_ops_to_fops(const struct igloo_proc_ops *ops, struct file_operatio out->get_unmapped_area = ops->get_unmapped_area; return out; } -#endif // Unified proc_create wrapper static struct proc_dir_entry *igloo_proc_create_data(const char *name, umode_t mode, @@ -357,6 +357,24 @@ static struct proc_dir_entry *igloo_proc_create_data(const char *name, umode_t m #endif } +static struct proc_dir_entry *igloo_proc_create_pid_file_data(const char *name, umode_t mode, + struct igloo_proc_ops *uops, + void *data, + bool enable_default_mmap) +{ + struct file_operations *fops; + + if (enable_default_mmap && uops->mmap == NULL) { + uops->mmap = igloo_proxy_mmap; + } + + fops = kmalloc(sizeof(struct file_operations), GFP_KERNEL); + if (!fops) + return NULL; + igloo_convert_ops_to_fops(uops, fops); + return igloo_proc_create_pid_data(name, mode, fops, data); +} + /* ========================================================================= * 1. INTERNAL PROCFS SYMBOL RESOLUTION * ========================================================================= */ @@ -780,35 +798,45 @@ void handle_op_procfs_create_file(portal_region *mem_region) goto out; } - // Parent must be provided (0 means root) + // Parent must be provided (0 means root, PROCFS_PID_PARENT_ID means /proc/) if (req->parent_id) { - parent_dir_struct = find_proc_dir_struct_by_id(req->parent_id); - if (!parent_dir_struct) { - printk(KERN_EMERG "portal_procfs: Invalid parent_id=%d for file '%s'\n", req->parent_id, entry_name); - mem_region->header.op = HYPER_RESP_WRITE_FAIL; - goto out; - } - synthetic_pid_parent = parent_dir_struct->synthetic_pid; - if (synthetic_pid_parent) { - parent = ensure_pid_template_parent(); - if (!parent) { - printk(KERN_EMERG "portal_procfs: Failed to create pid template parent for '%s'\n", entry_name); - mem_region->header.op = HYPER_RESP_WRITE_FAIL; - goto out; - } + if (req->parent_id == PROCFS_PID_PARENT_ID) { + parent = NULL; } else { - parent = parent_dir_struct->entry; - if (!parent) { - printk(KERN_EMERG "portal_procfs: Invalid empty parent_id=%d for file '%s'\n", req->parent_id, entry_name); + parent_dir_struct = find_proc_dir_struct_by_id(req->parent_id); + if (!parent_dir_struct) { + printk(KERN_EMERG "portal_procfs: Invalid parent_id=%d for file '%s'\n", req->parent_id, entry_name); mem_region->header.op = HYPER_RESP_WRITE_FAIL; goto out; } + synthetic_pid_parent = parent_dir_struct->synthetic_pid; + if (synthetic_pid_parent) { + parent = ensure_pid_template_parent(); + if (!parent) { + printk(KERN_EMERG "portal_procfs: Failed to create pid template parent for '%s'\n", entry_name); + mem_region->header.op = HYPER_RESP_WRITE_FAIL; + goto out; + } + } else { + parent = parent_dir_struct->entry; + if (!parent) { + printk(KERN_EMERG "portal_procfs: Invalid empty parent_id=%d for file '%s'\n", req->parent_id, entry_name); + mem_region->header.op = HYPER_RESP_WRITE_FAIL; + goto out; + } + } } } - // Safety: Fetch the entry to check its type - existing = find_proc_subdir_entry(parent, entry_name); - exists = (existing != NULL); + // Safety: Fetch the entry to check its type. PID-relative entries are + // resolved by proc_tgid_base_lookup, not the root procfs rb-tree. + if (req->parent_id == PROCFS_PID_PARENT_ID) { + existing = NULL; + exists = false; + } else { + existing = find_proc_subdir_entry(parent, entry_name); + exists = (existing != NULL); + } // printk(KERN_EMERG "portal_procfs: parent=%p, entry_name='%s'\n", parent, entry_name); @@ -852,7 +880,10 @@ void handle_op_procfs_create_file(portal_region *mem_region) enable_default_mmap = (req->size > 0 || req->support_mmap); // Create the file and bind the tracker - file = igloo_proc_create_data(entry_name, file_mode, parent, &req->fops, pe, enable_default_mmap); + if (req->parent_id == PROCFS_PID_PARENT_ID) + file = igloo_proc_create_pid_file_data(entry_name, file_mode, &req->fops, pe, enable_default_mmap); + else + file = igloo_proc_create_data(entry_name, file_mode, parent, &req->fops, pe, enable_default_mmap); if (!file) { printk(KERN_EMERG "portal_procfs: Failed to create proc entry: %s\n", entry_name); kfree(pe->name); diff --git a/src/portal/portal_types.h b/src/portal/portal_types.h index 59b73f3..4304412 100644 --- a/src/portal/portal_types.h +++ b/src/portal/portal_types.h @@ -126,6 +126,7 @@ struct portal_hyperfs_add_hyperfile_args { #define PROCFS_MAX_PATH 256 +#define PROCFS_PID_PARENT_ID -1 // Universal proc ops struct struct igloo_proc_ops { From 612ddf034b7d0026d49c558d64e3dff6fc9d5ea9 Mon Sep 17 00:00:00 2001 From: Luke Craig Date: Sun, 17 May 2026 14:11:39 -0400 Subject: [PATCH 3/3] fixup --- src/hooks/signal_hc.c | 57 +++++++++++++++++++++++++++++++++----- src/portal/portal_procfs.c | 51 +++++++++++++++++----------------- 2 files changed, 76 insertions(+), 32 deletions(-) diff --git a/src/hooks/signal_hc.c b/src/hooks/signal_hc.c index 0126f65..0fc8058 100644 --- a/src/hooks/signal_hc.c +++ b/src/hooks/signal_hc.c @@ -1,10 +1,10 @@ #include #include #include +#include +#include #include #include -#include -#include #include "igloo_hypercall.h" #include "igloo.h" #include "signal_hc.h" @@ -18,6 +18,12 @@ static struct hlist_head any_signal_hooks; static DEFINE_SPINLOCK(signal_hook_lock); static atomic_t global_signal_hook_count = ATOMIC_INIT(0); +struct signal_probe_data { + struct task_struct *task; +}; + +static struct kretprobe signal_rp; + static void do_signal_hyp(struct signal_event *event) { igloo_portal(IGLOO_HYP_SIGNAL_DELIVER, (unsigned long)event, 0); } @@ -35,16 +41,37 @@ static bool hook_matches_signal(struct kernel_signal_hook *hook, int sig, struct return true; } -static bool igloo_signal_deliver(int sig, struct task_struct *t) { +static int handler_entry(struct kretprobe_instance *ri, struct pt_regs *regs) { + struct signal_probe_data *data = (struct signal_probe_data *)ri->data; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0) + data->task = current; +#else + data->task = (struct task_struct *)regs_get_argument(regs, 0); + if (!data->task) + data->task = current; +#endif + + return 0; +} + +static int handler_ret(struct kretprobe_instance *ri, struct pt_regs *regs) { + int sig; + struct task_struct *t; struct kernel_signal_hook *hook; struct signal_event event; bool drop = false; bool needs_hyp = false; + struct signal_probe_data *data = (struct signal_probe_data *)ri->data; if (atomic_read(&global_signal_hook_count) == 0) - return false; + return 0; + + sig = (int)igloo_regs_get_return_value(regs); + if (sig <= 0) + return 0; - if (!t) return false; + t = data->task ? data->task : current; rcu_read_lock(); @@ -87,14 +114,30 @@ static bool igloo_signal_deliver(int sig, struct task_struct *t) { } rcu_read_unlock(); - return drop; + if (drop) { + igloo_regs_set_return_value(regs, 0); + } + + return 0; } int signal_hc_init(void) { + int ret; hash_init(signal_hook_table); INIT_HLIST_HEAD(&any_signal_hooks); - igloo_signal_deliver_hook = igloo_signal_deliver; + memset(&signal_rp, 0, sizeof(signal_rp)); + signal_rp.kp.symbol_name = "dequeue_signal"; + signal_rp.entry_handler = handler_entry; + signal_rp.handler = handler_ret; + signal_rp.data_size = sizeof(struct signal_probe_data); + signal_rp.maxactive = 64; + + ret = register_kretprobe(&signal_rp); + if (ret < 0) + printk(KERN_ERR "IGLOO: Failed to register kretprobe on dequeue_signal: %d\n", ret); + else + printk(KERN_INFO "IGLOO: Registered signal kretprobe on dequeue_signal\n"); return 0; } diff --git a/src/portal/portal_procfs.c b/src/portal/portal_procfs.c index 69eebaa..f8d0070 100644 --- a/src/portal/portal_procfs.c +++ b/src/portal/portal_procfs.c @@ -357,24 +357,6 @@ static struct proc_dir_entry *igloo_proc_create_data(const char *name, umode_t m #endif } -static struct proc_dir_entry *igloo_proc_create_pid_file_data(const char *name, umode_t mode, - struct igloo_proc_ops *uops, - void *data, - bool enable_default_mmap) -{ - struct file_operations *fops; - - if (enable_default_mmap && uops->mmap == NULL) { - uops->mmap = igloo_proxy_mmap; - } - - fops = kmalloc(sizeof(struct file_operations), GFP_KERNEL); - if (!fops) - return NULL; - igloo_convert_ops_to_fops(uops, fops); - return igloo_proc_create_pid_data(name, mode, fops, data); -} - /* ========================================================================= * 1. INTERNAL PROCFS SYMBOL RESOLUTION * ========================================================================= */ @@ -617,6 +599,18 @@ static struct portal_procfs_entry *find_pid_template(const char *parent, const c } } spin_unlock(&procfs_pid_template_lock); + if (entry) + return entry; + + spin_lock(&procfs_pid_template_lock); + list_for_each_entry(tmpl, &procfs_pid_template_list, list) { + if (!strcmp(tmpl->parent, "*") && + strlen(tmpl->name) == len && !memcmp(tmpl->name, name, len)) { + entry = tmpl->entry; + break; + } + } + spin_unlock(&procfs_pid_template_lock); return entry; } @@ -787,6 +781,7 @@ void handle_op_procfs_create_file(portal_region *mem_region) int id; char *entry_name; bool exists, enable_default_mmap, synthetic_pid_parent = false; + const char *pid_template_key = NULL; req->path[PROCFS_MAX_PATH - 1] = '\0'; entry_name = req->path; @@ -801,7 +796,14 @@ void handle_op_procfs_create_file(portal_region *mem_region) // Parent must be provided (0 means root, PROCFS_PID_PARENT_ID means /proc/) if (req->parent_id) { if (req->parent_id == PROCFS_PID_PARENT_ID) { - parent = NULL; + parent = ensure_pid_template_parent(); + if (!parent) { + printk(KERN_EMERG "portal_procfs: Failed to create pid template parent for '%s'\n", entry_name); + mem_region->header.op = HYPER_RESP_WRITE_FAIL; + goto out; + } + synthetic_pid_parent = true; + pid_template_key = "*"; } else { parent_dir_struct = find_proc_dir_struct_by_id(req->parent_id); if (!parent_dir_struct) { @@ -825,6 +827,8 @@ void handle_op_procfs_create_file(portal_region *mem_region) goto out; } } + if (synthetic_pid_parent) + pid_template_key = parent_dir_struct->path; } } @@ -880,10 +884,7 @@ void handle_op_procfs_create_file(portal_region *mem_region) enable_default_mmap = (req->size > 0 || req->support_mmap); // Create the file and bind the tracker - if (req->parent_id == PROCFS_PID_PARENT_ID) - file = igloo_proc_create_pid_file_data(entry_name, file_mode, &req->fops, pe, enable_default_mmap); - else - file = igloo_proc_create_data(entry_name, file_mode, parent, &req->fops, pe, enable_default_mmap); + file = igloo_proc_create_data(entry_name, file_mode, parent, &req->fops, pe, enable_default_mmap); if (!file) { printk(KERN_EMERG "portal_procfs: Failed to create proc entry: %s\n", entry_name); kfree(pe->name); @@ -903,8 +904,8 @@ void handle_op_procfs_create_file(portal_region *mem_region) list_add(&pe->list, &procfs_entry_list); spin_unlock(&procfs_entry_lock); - if (synthetic_pid_parent) - remember_pid_template(parent_dir_struct->path, entry_name, pe); + if (synthetic_pid_parent && pid_template_key) + remember_pid_template(pid_template_key, entry_name, pe); // printk(KERN_EMERG "portal_procfs: Created procfs entry '%s' with id %d\n", entry_name, id);