Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
4 changes: 4 additions & 0 deletions src/igloo_debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ struct igloo_debug_config {
bool vma; // Enable debug for VMA tracking
bool syscall; // Enable debug for syscall tracking
bool osi; // Enable debug for OSI features
bool kprobe; // Enable debug for kprobe module
};

// Global debug configuration (defined in igloo_hc.c)
Expand All @@ -31,4 +32,7 @@ extern struct igloo_debug_config igloo_debug;
#define igloo_debug_osi(fmt, ...) \
do { if (igloo_debug.osi) pr_emerg("IGLOO-OSI: " fmt, ##__VA_ARGS__); } while (0)

#define igloo_debug_kprobe(fmt, ...) \
do { if (igloo_debug.kprobe) pr_emerg("IGLOO-KPROBE: " fmt, ##__VA_ARGS__); } while (0)

#endif /* _IGLOO_DEBUG_H */
6 changes: 5 additions & 1 deletion src/igloo_hypercall_consts.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ enum igloo_hypercall_constants {
IGLOO_HYP_UPROBE_ENTER = 0x6901,
IGLOO_HYP_UPROBE_RETURN = 0x6902,

/* Kprobe operations */
IGLOO_HYP_KPROBE_ENTER = 0x6903,
IGLOO_HYP_KPROBE_RETURN = 0x6904,

IGLOO_HYPER_ENABLE_PORTAL_INTERRUPT = 0x7901,
IGLOO_HYPER_PORTAL_INTERRUPT = 0x7902,

Expand All @@ -51,4 +55,4 @@ enum igloo_hypercall_constants {
IGLOO_MODULE_BASE = 0x6408400C,
IGLOO_INIT_MODULE = 0x6408400D,

};
};
291 changes: 291 additions & 0 deletions src/portal/portal_kprobe.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,291 @@
#include "portal_internal.h"
#include <linux/slab.h>
#include <linux/kprobes.h>
#include <linux/hashtable.h>
#include <linux/syscalls.h>
#include <linux/version.h>

/* TODO: consider using tracepoints if available */

// Helper macro for kprobe debug logs
#define kprobe_debug(fmt, ...) igloo_debug_kprobe(fmt, ##__VA_ARGS__)

// Maximum number of kprobes we can register
#define MAX_KPROBES 1024

Comment thread
zestrada marked this conversation as resolved.
// Structure to track a registered kprobe
struct portal_kprobe {
uint64_t id; // Unique ID for this probe
struct hlist_node hlist; // For tracking in hash table
char *symbol; // Symbol name
char *filter_comm; // Process name filter (NULL = no filter)
uint64_t probe_type; // Type of probe (entry or return)

// kprobe specific structures
struct kprobe kp;
struct kretprobe rp;
bool kp_registered;
bool rp_registered;

// PID filtering support
uint64_t filter_pid; // PID to filter on (0 = no filter/match any)
};

// Structure for kprobe registration
struct kprobe_registration {
char symbol[256]; // Symbol name
unsigned long offset; // Offset in the function (only for kprobe)
unsigned long type; // ENTRY, RETURN, or BOTH
unsigned long pid; // PID filter or CURRENT_PID_NUM for any
char comm[TASK_COMM_LEN]; // Process name filter (empty for none)
} __attribute__((packed));

// Hash table to track kprobes by ID
static DEFINE_HASHTABLE(kprobe_table, 10); // 1024 buckets
static DEFINE_SPINLOCK(kprobe_lock);
static atomic_t kprobe_id_counter = ATOMIC_INIT(0);

// Global atomic counter for syscall sequence numbers
Comment thread
zestrada marked this conversation as resolved.
Outdated
static atomic64_t kprobe_sequence_counter = ATOMIC64_INIT(0);

struct portal_event {
uint64_t id;
struct task_struct *task;
struct pt_regs *regs;
};

static void do_hyp_kprobe(bool is_enter, uint64_t id, struct pt_regs *regs) {
// Set the sequence number atomically
uint64_t sequence = atomic64_inc_return(&kprobe_sequence_counter);

struct portal_event pe = {
.id = id,
.task = current,
.regs = regs,
Comment thread
zestrada marked this conversation as resolved.
Outdated
};

// Add the hook_id and metadata to the call so the hypervisor knows which hook was triggered
// and has access to syscall metadata - pass the hook_id as third argument
igloo_portal(is_enter ? IGLOO_HYP_KPROBE_ENTER : IGLOO_HYP_KPROBE_RETURN,
sequence, (unsigned long)&pe);
}

// Kprobe pre_handler (Entry)
static int portal_kprobe_pre_handler(struct kprobe *p, struct pt_regs *regs) {
struct portal_kprobe *pk = container_of(p, struct portal_kprobe, kp);

kprobe_debug("igloo: kprobe entry: id=%llu, symbol=%s, proc=%s, pid=%d\n",
(unsigned long long)(pk->id), pk->symbol,
current->comm, task_pid_nr(current));

if (pk->filter_comm && strncmp(current->comm, pk->filter_comm, TASK_COMM_LEN) != 0) {
return 0;
}

if ((pk->filter_pid) != CURRENT_PID_NUM &&
(pk->filter_pid) != task_pid_nr(current)) {
return 0;
}

do_hyp_kprobe(true, pk->id, regs);
return 0;
}

// Kretprobe handler (Return)
static int portal_kretprobe_handler(struct kretprobe_instance *ri, struct pt_regs *regs) {
struct portal_kprobe *pk;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0)
struct kretprobe *curr_rp = get_kretprobe(ri);
pk = container_of(curr_rp, struct portal_kprobe, rp);
#else
/* legacy path */
Comment thread
zestrada marked this conversation as resolved.
pk = container_of(ri->rp, struct portal_kprobe, rp);
#endif

kprobe_debug("igloo: kprobe return: id=%llu, symbol=%s, proc=%s, pid=%d\n",
(unsigned long long)(pk->id), pk->symbol,
current->comm, task_pid_nr(current));

if (pk->filter_comm && strncmp(current->comm, pk->filter_comm, TASK_COMM_LEN) != 0) {
return 0;
}

if ((pk->filter_pid) != CURRENT_PID_NUM &&
(pk->filter_pid) != task_pid_nr(current)) {
return 0;
}

do_hyp_kprobe(false, pk->id, regs);
return 0;
}

// Search for a kprobe by ID
static struct portal_kprobe *find_kprobe_by_id(unsigned long id)
{
struct portal_kprobe *pk;

spin_lock(&kprobe_lock);
hash_for_each_possible(kprobe_table, pk, hlist, id) {
if ((pk->id) == id) {
spin_unlock(&kprobe_lock);
return pk;
}
}
spin_unlock(&kprobe_lock);

return NULL;
}

// Handler for registering a new kprobe
void handle_op_register_kprobe(portal_region *mem_region)
{
struct portal_kprobe *pk;
struct kprobe_registration *reg;
unsigned long id;
int ret;
char *filter_comm = NULL;

// Map the input data to our registration structure
reg = (struct kprobe_registration *) PORTAL_DATA(mem_region);

// Ensure the symbol is null-terminated
reg->symbol[sizeof(reg->symbol) - 1] = '\0';
reg->comm[TASK_COMM_LEN - 1] = '\0';

// Set filter_comm if not empty
if (reg->comm[0] != '\0') {
filter_comm = reg->comm;
}

kprobe_debug("igloo: Registering kprobe for symbol=%s, offset=%lu, type=%lu, filter=%s, pid=%lu\n",
reg->symbol, reg->offset, reg->type,
filter_comm ? filter_comm : "none",
reg->pid);

// Allocate a new kprobe structure
pk = kzalloc(sizeof(*pk), GFP_KERNEL);
if (!pk) {
kprobe_debug("igloo: Failed to allocate kprobe structure\n");
goto fail;
}

// Allocate memory for symbol
pk->symbol = kstrdup(reg->symbol, GFP_KERNEL);
if (!pk->symbol) {
kprobe_debug("igloo: Failed to allocate symbol memory\n");
goto fail_free_pk;
}

pk->probe_type = reg->type;
pk->filter_pid = reg->pid;

// Save process filter if provided
if (filter_comm) {
pk->filter_comm = kstrdup(filter_comm, GFP_KERNEL);
if (!pk->filter_comm) {
kprobe_debug("igloo: Failed to allocate filter_comm memory\n");
goto fail_free_symbol;
}
}

// Get a unique ID for this kprobe
id = atomic_inc_return(&kprobe_id_counter);
pk->id = id;

// Register KPROBE (Entry)
if (reg->type == PORTAL_UPROBE_TYPE_ENTRY || reg->type == PORTAL_UPROBE_TYPE_BOTH) {
Comment thread
zestrada marked this conversation as resolved.
Outdated
pk->kp.symbol_name = pk->symbol;
pk->kp.offset = reg->offset;
pk->kp.pre_handler = portal_kprobe_pre_handler;

ret = register_kprobe(&pk->kp);
if (ret < 0) {
kprobe_debug("igloo: Failed to register kprobe: %d\n", ret);
goto fail_free_comm;
}
pk->kp_registered = true;
}

// Register KRETPROBE (Return)
if (reg->type == PORTAL_UPROBE_TYPE_RETURN || reg->type == PORTAL_UPROBE_TYPE_BOTH) {
Comment thread
zestrada marked this conversation as resolved.
Outdated
// kretprobe doesn't support offset usually, or it must be 0 (entry)
// We'll use the symbol.
pk->rp.kp.symbol_name = pk->symbol;
// pk->rp.kp.offset = reg->offset; // Usually 0 for kretprobe
Comment thread
zestrada marked this conversation as resolved.
Outdated
pk->rp.handler = portal_kretprobe_handler;
pk->rp.maxactive = 0; // Default maxactive

ret = register_kretprobe(&pk->rp);
if (ret < 0) {
kprobe_debug("igloo: Failed to register kretprobe: %d\n", ret);
// If we already registered kprobe, we should unregister it
if (pk->kp_registered) {
unregister_kprobe(&pk->kp);
}
goto fail_free_comm;
}
pk->rp_registered = true;
}
Comment thread
zestrada marked this conversation as resolved.

// Add to hash table
spin_lock(&kprobe_lock);
hash_add(kprobe_table, &pk->hlist, id);
spin_unlock(&kprobe_lock);

// Return success with the unique ID
mem_region->header.size = id; // Return the ID in size

mem_region->header.op = HYPER_RESP_READ_NUM;
return;

fail_free_comm:
if (pk->filter_comm) kfree(pk->filter_comm);
fail_free_symbol:
kfree(pk->symbol);
fail_free_pk:
kfree(pk);
fail:
mem_region->header.op = HYPER_RESP_READ_FAIL;
}

// Handler for unregistering a kprobe
void handle_op_unregister_kprobe(portal_region *mem_region)
{
unsigned long id;
struct portal_kprobe *pk;

// ID is stored in header.addr
id = mem_region->header.addr;

kprobe_debug("igloo: Unregistering kprobe with ID=%lu\n", id);

// Find the kprobe
pk = find_kprobe_by_id(id);
if (!pk) {
kprobe_debug("igloo: Kprobe with ID %lu not found\n", id);
mem_region->header.op = HYPER_RESP_READ_FAIL;
return;
}
Comment thread
zestrada marked this conversation as resolved.

if (pk->kp_registered) {
unregister_kprobe(&pk->kp);
}
if (pk->rp_registered) {
unregister_kretprobe(&pk->rp);
}

// Remove from hash table
spin_lock(&kprobe_lock);
hash_del(&pk->hlist);
spin_unlock(&kprobe_lock);

// Free resources
kfree(pk->symbol);
if (pk->filter_comm) {
kfree(pk->filter_comm);
}
kfree(pk);

// Return success
mem_region->header.op = HYPER_RESP_READ_OK;
}
4 changes: 3 additions & 1 deletion src/portal/portal_op_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
X(write_file, WRITE_FILE) \
X(register_uprobe, REGISTER_UPROBE) \
X(unregister_uprobe, UNREGISTER_UPROBE) \
X(register_kprobe, REGISTER_KPROBE) \
X(unregister_kprobe, UNREGISTER_KPROBE) \
X(register_syscall_hook, REGISTER_SYSCALL_HOOK) \
X(ffi_exec, FFI_EXEC) \
X(kallsyms_lookup, KALLSYMS_LOOKUP) \
X(tramp_generate, TRAMP_GENERATE) \
X(hyperfs_add_hyperfile, HYPERFS_ADD_HYPERFILE) \
X(copy_buf_guest, COPY_BUF_GUEST)
X(copy_buf_guest, COPY_BUF_GUEST)