fix SIGBUS due to mapping being larger than file

This commit is contained in:
2025-12-17 16:15:41 +01:00
parent d52cf8aaaf
commit d25cf59380
4 changed files with 27 additions and 5 deletions

View File

@@ -212,6 +212,10 @@ pub const Statistics = struct {
/// NOTE: This function leaves the region as R|W and the caller is responsible for changing it to
/// the desired protection
pub fn patchRegion(region: []align(page_size) u8) !void {
log.info(
"Patching region: 0x{x} - 0x{x}",
.{ @intFromPtr(region.ptr), @intFromPtr(&region[region.len - 1]) },
);
// For now just do a coarse lock.
// TODO: should we make this more fine grained?
mutex.lock();

View File

@@ -256,10 +256,10 @@ fn patchLoadedElf(base: usize) !void {
const page_start = mem.alignBackward(usize, vaddr, page_size);
const page_end = mem.alignForward(usize, vaddr + memsz, page_size);
const size = page_end - page_start;
const region = @as([*]align(page_size) u8, @ptrFromInt(page_start))[0 .. page_end - page_start];
const region = @as([*]align(page_size) u8, @ptrFromInt(page_start))[0..size];
log.info("Patching segment: 0x{x} - 0x{x}", .{ page_start, page_end });
try Patcher.patchRegion(region);
try posix.mprotect(region, elfToMmapProt(phdr.p_flags));
}

View File

@@ -6,6 +6,8 @@ const assert = std.debug.assert;
const page_size = std.heap.pageSize();
const log = std.log.scoped(.syscalls);
/// Represents the stack layout pushed by `syscallEntry` before calling the handler.
pub const SavedContext = extern struct {
padding: u64, // Result of `sub $8, %rsp` for alignment
@@ -90,13 +92,25 @@ export fn syscall_handler(ctx: *SavedContext) callconv(.c) void {
// Execute the syscall first to get the address (rax)
ctx.rax = executeSyscall(ctx);
const addr = ctx.rax;
const len = ctx.rsi;
var len = ctx.rsi;
const flags: linux.MAP = @bitCast(@as(u32, @intCast(ctx.r10)));
const fd: linux.fd_t = @bitCast(@as(u32, @truncate(ctx.r8)));
const offset = ctx.r9;
const is_error = @as(i64, @bitCast(ctx.rax)) < 0;
if (is_error) return;
if ((prot & posix.PROT.EXEC) == 0) return;
if (len <= 0) return;
// If file-backed (not anonymous), clamp len to file size to avoid SIGBUS
if (!flags.ANONYMOUS) {
var stat: linux.Stat = undefined;
if (0 == linux.fstat(fd, &stat) and linux.S.ISREG(stat.mode)) {
const file_size: u64 = @intCast(stat.size);
len = if (offset >= file_size) 0 else @min(len, file_size - offset);
}
}
if (len <= 0) return;
// mmap addresses are always page aligned
const ptr = @as([*]align(page_size) u8, @ptrFromInt(addr));
// Check if we can patch it