fix SIGBUS due to mapping being larger than file
This commit is contained in:
@@ -19,7 +19,7 @@ IvyBridge(2012) and AMD Zen 2 Family 17H(2019) and Linux 5.9(2020).
|
|||||||
the stack, so `ucontext` isn't on top anymore.
|
the stack, so `ucontext` isn't on top anymore.
|
||||||
- [x] `/proc/self/exe`: intercept calls to `readlink`/`readlinkat` with that as argument
|
- [x] `/proc/self/exe`: intercept calls to `readlink`/`readlinkat` with that as argument
|
||||||
- [x] `auxv`: check if that is setup correctly and completely
|
- [x] `auxv`: check if that is setup correctly and completely
|
||||||
- [ ] JIT support: intercept `mmap`, `mprotect` and `mremap` that change pages to be executable
|
- [x] JIT support: intercept `mmap`, `mprotect` and `mremap` that change pages to be executable
|
||||||
- [ ] `SIGILL` patching fallback
|
- [ ] `SIGILL` patching fallback
|
||||||
- [x] `vdso` handling
|
- [x] `vdso` handling
|
||||||
- [x] check why the libc tests are flaky
|
- [x] check why the libc tests are flaky
|
||||||
@@ -31,6 +31,8 @@ IvyBridge(2012) and AMD Zen 2 Family 17H(2019) and Linux 5.9(2020).
|
|||||||
- [ ] Ghost page edge case: In all patch strategies, if a range spans multiple pages and we `mmap`
|
- [ ] Ghost page edge case: In all patch strategies, if a range spans multiple pages and we `mmap`
|
||||||
the first one but can't `mmap` the second one we just let the first one mapped. It would be better
|
the first one but can't `mmap` the second one we just let the first one mapped. It would be better
|
||||||
to unmap them
|
to unmap them
|
||||||
|
- [ ] Right now when patching we mmap a page and may not use it, but we still leave it mapped. This
|
||||||
|
leaks memory. If we fix this correctly the Ghost page issue is also fixed
|
||||||
- [ ] Re-entrancy for `patchRegion`
|
- [ ] Re-entrancy for `patchRegion`
|
||||||
- when a signal comes, while we are in that function, and we need to patch something due to the
|
- when a signal comes, while we are in that function, and we need to patch something due to the
|
||||||
signal we will deadlock
|
signal we will deadlock
|
||||||
@@ -46,3 +48,5 @@ IvyBridge(2012) and AMD Zen 2 Family 17H(2019) and Linux 5.9(2020).
|
|||||||
- [ ] `modify_ldt`: check what we need to intercept and change
|
- [ ] `modify_ldt`: check what we need to intercept and change
|
||||||
- [ ] `set_tid_address`: check what we need to intercept and change
|
- [ ] `set_tid_address`: check what we need to intercept and change
|
||||||
- [ ] performance optimizations for patched code? Peephole might be possible
|
- [ ] performance optimizations for patched code? Peephole might be possible
|
||||||
|
- [ ] maybe add a way to run something after the client is finished
|
||||||
|
- could be useful for statistics, cleanup(if necessary), or notifying of suppressed warnings
|
||||||
|
|||||||
@@ -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
|
/// NOTE: This function leaves the region as R|W and the caller is responsible for changing it to
|
||||||
/// the desired protection
|
/// the desired protection
|
||||||
pub fn patchRegion(region: []align(page_size) u8) !void {
|
pub fn patchRegion(region: []align(page_size) u8) !void {
|
||||||
|
log.info(
|
||||||
|
"Patching region: 0x{x} - 0x{x}",
|
||||||
|
.{ @intFromPtr(region.ptr), @intFromPtr(®ion[region.len - 1]) },
|
||||||
|
);
|
||||||
// For now just do a coarse lock.
|
// For now just do a coarse lock.
|
||||||
// TODO: should we make this more fine grained?
|
// TODO: should we make this more fine grained?
|
||||||
mutex.lock();
|
mutex.lock();
|
||||||
|
|||||||
@@ -256,10 +256,10 @@ fn patchLoadedElf(base: usize) !void {
|
|||||||
|
|
||||||
const page_start = mem.alignBackward(usize, vaddr, page_size);
|
const page_start = mem.alignBackward(usize, vaddr, page_size);
|
||||||
const page_end = mem.alignForward(usize, vaddr + memsz, 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 Patcher.patchRegion(region);
|
||||||
try posix.mprotect(region, elfToMmapProt(phdr.p_flags));
|
try posix.mprotect(region, elfToMmapProt(phdr.p_flags));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ const assert = std.debug.assert;
|
|||||||
|
|
||||||
const page_size = std.heap.pageSize();
|
const page_size = std.heap.pageSize();
|
||||||
|
|
||||||
|
const log = std.log.scoped(.syscalls);
|
||||||
|
|
||||||
/// Represents the stack layout pushed by `syscallEntry` before calling the handler.
|
/// Represents the stack layout pushed by `syscallEntry` before calling the handler.
|
||||||
pub const SavedContext = extern struct {
|
pub const SavedContext = extern struct {
|
||||||
padding: u64, // Result of `sub $8, %rsp` for alignment
|
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)
|
// Execute the syscall first to get the address (rax)
|
||||||
ctx.rax = executeSyscall(ctx);
|
ctx.rax = executeSyscall(ctx);
|
||||||
const addr = ctx.rax;
|
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;
|
const is_error = @as(i64, @bitCast(ctx.rax)) < 0;
|
||||||
if (is_error) return;
|
if (is_error) return;
|
||||||
if ((prot & posix.PROT.EXEC) == 0) 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
|
// mmap addresses are always page aligned
|
||||||
const ptr = @as([*]align(page_size) u8, @ptrFromInt(addr));
|
const ptr = @as([*]align(page_size) u8, @ptrFromInt(addr));
|
||||||
// Check if we can patch it
|
// Check if we can patch it
|
||||||
|
|||||||
Reference in New Issue
Block a user