Skip to content
Merged
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
16 changes: 15 additions & 1 deletion src/hooks/syscalls_hc.c
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,21 @@ static inline bool value_matches_filter(long value, const struct value_filter *f

case SYSCALLS_HC_FILTER_BITMASK_CLEAR:
return (value & filter->bitmask) == 0;

case SYSCALLS_HC_FILTER_STR_EXACT:
if (!value || !filter->pattern) return false;
return check_str_exact(value, filter->pattern, filter->pattern_len);

case SYSCALLS_HC_FILTER_STR_STARTSWITH:
if (!value || !filter->pattern) return false;
return check_str_startswith(value, filter->pattern, filter->pattern_len);

case SYSCALLS_HC_FILTER_STR_ENDSWITH:
if (!value || !filter->pattern) return false;
return check_str_endswith(value, filter->pattern, filter->pattern_len);

case SYSCALLS_HC_FILTER_STR_CONTAINS:
if (!value || !filter->pattern) return false;
return check_str_contains(value, filter->pattern, filter->pattern_len);
default:
// Unknown filter type, assume no match
return false;
Expand Down
59 changes: 59 additions & 0 deletions src/hooks/syscalls_hc.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/hashtable.h>
#include <linux/uaccess.h>

#include "igloo_syscall_macros.h"

Expand All @@ -22,6 +23,10 @@ enum value_filter_type {
SYSCALLS_HC_FILTER_ERROR, /* < 0 (error, for return values) */
SYSCALLS_HC_FILTER_BITMASK_SET, /* All specified bits are set */
SYSCALLS_HC_FILTER_BITMASK_CLEAR, /* All specified bits are clear */
SYSCALLS_HC_FILTER_STR_EXACT, /* Exact string match */
SYSCALLS_HC_FILTER_STR_CONTAINS, /* String contains substring */
SYSCALLS_HC_FILTER_STR_STARTSWITH, /* String starts with prefix */
SYSCALLS_HC_FILTER_STR_ENDSWITH, /* String ends with suffix */
};

/* Value filter structure for complex comparisons */
Expand All @@ -32,6 +37,8 @@ struct value_filter {
long min_value; /* Minimum value for range filter */
long max_value; /* Maximum value for range filter */
unsigned long bitmask; /* Bitmask for bit operations */
char *pattern; /* String value for string comparisons */
u32 pattern_len; /* Length of string pattern */
};

/* Syscall hook structure */
Expand Down Expand Up @@ -90,6 +97,58 @@ int unregister_syscall_hook(struct kernel_syscall_hook *hook_ptr);

int syscalls_hc_init(void);

/* Helper for chunked comparison to avoid large stack buffers */
#define CMP_CHUNK_SIZE 64

static inline bool check_str_startswith(long user_ptr, const char *pattern, u32 len) {
char buf[CMP_CHUNK_SIZE];
u32 offset = 0;
long ret;

while (offset < len) {
int chunk = (len - offset > CMP_CHUNK_SIZE) ? CMP_CHUNK_SIZE : (len - offset);
ret = strncpy_from_user(buf, (const char __user *)(user_ptr + offset), chunk);
if (ret < chunk) return false; // EFAULT or hit null too early
if (memcmp(buf, pattern + offset, chunk) != 0) return false;
offset += chunk;
}
return true;
}

static inline bool check_str_exact(long user_ptr, const char *pattern, u32 len) {
char buf[1];
// Check prefix match
if (!check_str_startswith(user_ptr, pattern, len)) return false;
// Verify null termination at len
if (strncpy_from_user(buf, (const char __user *)(user_ptr + len), 1) != 1) return false;
return buf[0] == '\0';
}

static inline bool check_str_endswith(long user_ptr, const char *pattern, u32 pat_len) {
long str_len = strnlen_user((const char __user *)user_ptr, 32768); // Soft limit 32KB
if (str_len <= 0 || str_len - 1 < pat_len) return false;
return check_str_startswith(user_ptr + (str_len - 1 - pat_len), pattern, pat_len);
}

static inline bool check_str_contains(long user_ptr, const char *pattern, u32 pat_len) {
// Naive scanning implementation
char buf[CMP_CHUNK_SIZE];
long str_len = strnlen_user((const char __user *)user_ptr, 32768);
long i;

if (str_len <= 0 || str_len - 1 < pat_len) return false;

// Optimization: find first char then check prefix
for (i = 0; i <= str_len - 1 - pat_len; i++) {
long ret = strncpy_from_user(buf, (const char __user *)(user_ptr + i), 1);
if (ret != 1) return false;
if (buf[0] == pattern[0]) {
if (check_str_startswith(user_ptr + i, pattern, pat_len)) return true;
}
}
return false;
}

/* Normalize syscall names by removing common prefixes like 'sys_', '_sys_', 'compat_sys_' */
static inline const char *normalize_syscall_name(const char *name)
{
Expand Down
11 changes: 10 additions & 1 deletion src/portal/portal_syscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ void handle_op_register_syscall_hook(portal_region *mem_region)
static void unregister_syscall_deferred(struct work_struct *work)
{
struct kernel_syscall_hook *hook_ptr = container_of(work, struct kernel_syscall_hook, unregister_work);

int i = 0;
// 1. Immediately disable the hook (redundant but safe)
hook_ptr->hook.enabled = false;

Expand All @@ -94,6 +94,15 @@ static void unregister_syscall_deferred(struct work_struct *work)
// 3. Decrement global count
atomic_dec(&global_syscall_hook_count);

for (i = 0; i < IGLOO_SYSCALL_MAXARGS; i++) {
if (hook_ptr->hook.arg_filters[i].pattern) {
kfree(hook_ptr->hook.arg_filters[i].pattern);
}
}
if (hook_ptr->hook.retval_filter.pattern) {
kfree(hook_ptr->hook.retval_filter.pattern);
}

// 4. Free memory safely using RCU
kfree_rcu(hook_ptr, rcu);

Expand Down
Loading