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
74 changes: 38 additions & 36 deletions src/portal/portal_mem.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,12 @@ bool igloo_is_kernel_addr(unsigned long addr)
return (addr >= PAGE_OFFSET);

#elif defined(CONFIG_RISCV)
/* RISC-V kernel virtual address space check */
#if defined(CONFIG_64BIT)
/* For RISC-V 64-bit, kernel addresses are in the upper half */
/* Check if address is in kernel virtual address space */
/* RISC-V uses canonical addresses where kernel space starts high */
return (addr >= KERNEL_LINK_ADDR) || (addr >= PAGE_OFFSET);
#else
/* RISC-V 32-bit */
return (addr >= PAGE_OFFSET);
#endif
/*
* RISC-V has multiple kernel virtual ranges. Kernel stacks can live below
* PAGE_OFFSET/KERNEL_LINK_ADDR (for example ff20... on the 6.13 virt
* kernels), so classify against the user/kernel split instead.
*/
return addr >= TASK_SIZE;

#elif defined(CONFIG_PPC)
return is_kernel_addr(addr);
Expand Down Expand Up @@ -65,6 +61,24 @@ bool igloo_is_kernel_addr(unsigned long addr)
#endif
}

static long igloo_kernel_read_nofault(void *dst, const void *src, size_t size)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
return copy_from_kernel_nofault(dst, src, size);
#else
return probe_kernel_read(dst, src, size);
#endif
}

static long igloo_kernel_write_nofault(void *dst, const void *src, size_t size)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
return copy_to_kernel_nofault(dst, src, size);
#else
return probe_kernel_write(dst, src, size);
#endif
}

struct task_struct *get_target_task_by_id(portal_region* mem_region)
{
pid_t target_pid = (pid_t)(mem_region->header.pid);
Expand Down Expand Up @@ -102,27 +116,21 @@ void handle_op_read(portal_region *mem_region)
(unsigned long long)addr, (unsigned long long)size);

if (is_kernel_address) {
// Handle kernel memory - directly copy with memcpy
// Handle kernel memory, including stack/vmalloc/module addresses.
igloo_pr_debug("igloo: Reading from kernel address %#lx, size %zu\n", addr, size);
if (size > CHUNK_SIZE) {
igloo_pr_debug("igloo: Requested size too large, truncating to %zu\n", (size_t)CHUNK_SIZE);
size = CHUNK_SIZE;
}

// Only access memory we think is valid
if (virt_addr_valid((void *)addr)) {
// Use memcpy with safety precautions
unsigned long flags;
local_irq_save(flags);
pagefault_disable();
memcpy(PORTAL_DATA(mem_region), (void *)addr, size);
pagefault_enable();
local_irq_restore(flags);


resp = igloo_kernel_read_nofault(PORTAL_DATA(mem_region), (void *)addr, size);
if (resp == 0) {
mem_region->header.op = HYPER_RESP_READ_OK;
mem_region->header.size = size;
} else {
igloo_pr_debug("igloo: Invalid kernel address %#lx\n", addr);
igloo_pr_debug(
"igloo: kernel read failed for addr %#lx, size %zu, resp %d\n",
addr, size, resp);
mem_region->header.op = HYPER_RESP_READ_FAIL;
}
} else {
Expand Down Expand Up @@ -158,28 +166,22 @@ void handle_op_write(portal_region *mem_region)
(unsigned long long)addr, (unsigned long long)size);

if (igloo_is_kernel_addr(addr)) {
// Handle kernel memory writes - use memcpy, but with caution
// Handle kernel memory writes, including stack/vmalloc/module addresses.
igloo_pr_debug("igloo: Writing to kernel address %#lx, size %zu\n", addr, size);

if (size > CHUNK_SIZE) {
igloo_pr_debug("igloo: Requested size too large, truncating to %zu\n", (size_t)CHUNK_SIZE);
size = CHUNK_SIZE;
}

// Only write to memory we think is valid and writable
if (virt_addr_valid((void *)addr)) {
// Use memcpy with safety precautions
unsigned long flags;
local_irq_save(flags);
pagefault_disable();
memcpy((void *)addr, PORTAL_DATA(mem_region), size);
pagefault_enable();
local_irq_restore(flags);


resp = igloo_kernel_write_nofault((void *)addr, PORTAL_DATA(mem_region), size);
if (resp == 0) {
igloo_pr_debug("igloo: Successfully wrote to kernel address %#lx\n", addr);
mem_region->header.op = HYPER_RESP_WRITE_OK;
} else {
igloo_pr_debug("igloo: Invalid kernel address %#lx\n", addr);
igloo_pr_debug(
"igloo: kernel write failed for addr %#lx, size %zu, resp %d\n",
addr, size, resp);
mem_region->header.op = HYPER_RESP_WRITE_FAIL;
}
} else {
Expand Down
118 changes: 108 additions & 10 deletions src/portal/portal_osi.c
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,7 @@ void handle_op_read_fds(portal_region *mem_region)
int start_fd = 0;
int i;
size_t max_fds;
bool reached_end = false;
char *data_buf = PORTAL_DATA(mem_region);
struct osi_result_header *header = (struct osi_result_header *)data_buf;
struct osi_fd_entry *fd_entry;
Expand Down Expand Up @@ -844,16 +845,90 @@ void handle_op_read_fds(portal_region *mem_region)
string_offset = sizeof(struct osi_result_header) + (max_fds * sizeof(struct osi_fd_entry));
string_buf = data_buf + string_offset;

// Access the process's file descriptor table
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
/*
* Modern fd tables must be walked through the exported helper. Raw fdtable
* entries are RCU-managed and can be stale by the time d_path() consumes
* them.
*/
{
unsigned int fd_cursor;

for (fd_cursor = 0; ; fd_cursor++) {
file = fget_task_next(task, &fd_cursor);
if (!file) {
break;
}
total_count++;
fput(file);
}
header->total_count = total_count;

for (fd_cursor = (unsigned int)start_fd; ; fd_cursor++) {
file = fget_task_next(task, &fd_cursor);
if (!file) {
reached_end = true;
break;
}
i = (int)fd_cursor;

if (count >= max_fds || string_offset >= CHUNK_SIZE - PATH_MAX) {
mem_region->header.addr = i;
fput(file);
break;
}

// Fill in fd entry
fd_entry->fd = (i);
fd_entry->name_offset = (string_offset);

// Move to temporary string buffer for path
path_buf = kmalloc(PATH_MAX, GFP_KERNEL);
if (path_buf) {
char *path = d_path(&file->f_path, path_buf, PATH_MAX);
if (!IS_ERR(path)) {
size_t name_len = strlen(path);

// Check if we have enough space for this name
if (string_offset + name_len + 1 <= CHUNK_SIZE) {
// Copy path to string buffer
strncpy(string_buf, path, name_len);
string_buf[name_len] = '\0';

// Update string buffer position
string_buf += name_len + 1;
string_offset += name_len + 1;

// Increment counts
fd_entry++;
count++;

igloo_debug_osi("igloo: Processed fd %d: %s\n", i, path);
} else {
// Not enough space for this entry's name
mem_region->header.addr = (i);
igloo_debug_osi("igloo: Not enough space for fd %d path, will continue from here next time\n", i);
kfree(path_buf);
fput(file);
break;
}
}
kfree(path_buf);
path_buf = NULL;
}
fput(file);
}
}
#else
task_lock(task);
if (!task->files) {
files = task->files;
if (!files) {
task_unlock(task);
mem_region->header.size = (sizeof(struct osi_result_header));
mem_region->header.op = (HYPER_RESP_READ_OK);
return;
}

files = task->files;
spin_lock(&files->file_lock);
fdt = files_fdtable(files);

Expand All @@ -865,31 +940,56 @@ void handle_op_read_fds(portal_region *mem_region)

// Store total count in header
header->total_count = (total_count);
spin_unlock(&files->file_lock);
task_unlock(task);

// Start filling fd entries from start_fd
for (i = start_fd; i < fdt->max_fds; i++) {
for (i = start_fd; ; i++) {
task_lock(task);
files = task->files;
if (!files) {
reached_end = true;
task_unlock(task);
break;
}

spin_lock(&files->file_lock);
fdt = files_fdtable(files);
if (i >= fdt->max_fds) {
reached_end = true;
spin_unlock(&files->file_lock);
task_unlock(task);
break;
}

file = fdt->fd[i];

if (!file){
spin_unlock(&files->file_lock);
task_unlock(task);
igloo_debug_osi("igloo: No file for fd %d\n", i);
continue;
}

if (count >= max_fds || string_offset >= CHUNK_SIZE - PATH_MAX) {
// Store the next fd number to start from in the next call
mem_region->header.addr = (i + 1);
mem_region->header.addr = i;
spin_unlock(&files->file_lock);
task_unlock(task);
break;
}

// Get a reference to the file
get_file(file);
spin_unlock(&files->file_lock);
task_unlock(task);

// Fill in fd entry
fd_entry->fd = (i);
fd_entry->name_offset = (string_offset);

// Move to temporary string buffer for path
path_buf = kmalloc(PATH_MAX, GFP_ATOMIC);
path_buf = kmalloc(PATH_MAX, GFP_KERNEL);
if (path_buf) {
char *path = d_path(&file->f_path, path_buf, PATH_MAX);
if (!IS_ERR(path)) {
Expand Down Expand Up @@ -923,11 +1023,10 @@ void handle_op_read_fds(portal_region *mem_region)
// Release the file reference
fput(file);
}
spin_unlock(&files->file_lock);
task_unlock(task);
#endif

// If we processed all FDs, set next count to 0 to indicate completion
if (i >= fdt->max_fds) {
if (reached_end) {
mem_region->header.addr = 0;
}

Expand Down Expand Up @@ -981,4 +1080,3 @@ void handle_op_osi_proc_ptregs(portal_region *mem_region)
igloo_debug_osi("igloo: ptregs pointer returned for current task: %p\n", regs);
return;
}

15 changes: 13 additions & 2 deletions src/portal/portal_tramp.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,22 @@
/* We ALWAYS include kprobes as the universal fallback */
#include <linux/kprobes.h>

/*
* The Python trampoline path consumes the second hypercall argument as a real
* struct pt_regs *. On riscv64, fprobe/ftrace on modern kernels passes an
* ftrace_regs object through this callback path, which is not layout-compatible
* with struct pt_regs and causes null/shifted callback arguments. Use kprobes
* there until we add a native ftrace_regs payload decoder.
*/
#if defined(CONFIG_RISCV)
#define USE_KPROBE_TRAMPOLINES
#endif

/* Try to include high-speed tracing mechanisms if the kernel version allows */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0) && !defined(USE_KPROBE_TRAMPOLINES)
#define USE_FPROBE
#include <linux/fprobe.h>
#elif defined(CONFIG_DYNAMIC_FTRACE_WITH_REGS)
#elif defined(CONFIG_DYNAMIC_FTRACE_WITH_REGS) && !defined(USE_KPROBE_TRAMPOLINES)
#define USE_FTRACE
#include <linux/ftrace.h>
#endif
Expand Down
Loading