diff --git a/src/loader.rs b/src/loader.rs index f8b37c0..208cc3e 100644 --- a/src/loader.rs +++ b/src/loader.rs @@ -80,6 +80,9 @@ pub fn load( .unwrap(); let page_round_down = |addr: usize| addr / page_size * page_size; let page_round_up = |addr: usize| (addr + (page_size - 1)) / page_size * page_size; + + let mut segments: Vec<(usize, usize, ProtFlags)> = Vec::new(); + for ph in elf.program_headers { if ph.p_type != PT_LOAD { continue; @@ -104,7 +107,7 @@ pub fn load( mmap( Some(addr), size, - prot | ProtFlags::PROT_WRITE, // TODO: read only fix + prot | ProtFlags::PROT_WRITE, MapFlags::MAP_PRIVATE | MapFlags::MAP_FIXED, Some(&file), offset, @@ -119,6 +122,7 @@ pub fn load( page_round_up(file_end_addr) - file_end_addr, ); } + segments.push((addr.get(), size.get(), prot)); } // Relocations (needed for musl but not glibc FWICT) @@ -130,5 +134,12 @@ pub fn load( unsafe { ptr::write(dst, src) } } + // after relocations, so writes into not-yet-protected segments still work + for (addr, size, prot) in segments { + unsafe { + nix::sys::mman::mprotect(addr as *mut nix::libc::c_void, size, prot).unwrap(); + } + } + (base_addr, elf.header, opt_interp) } diff --git a/src/stack.rs b/src/stack.rs index b61b4cc..e6102d3 100644 --- a/src/stack.rs +++ b/src/stack.rs @@ -10,7 +10,7 @@ use nix::{ libc::{ getauxval, AT_BASE, AT_CLKTCK, AT_EGID, AT_ENTRY, AT_EUID, AT_EXECFN, AT_FLAGS, AT_GID, AT_HWCAP, AT_NULL, AT_PAGESZ, AT_PHDR, AT_PHENT, AT_PHNUM, AT_PLATFORM, AT_RANDOM, - AT_SECURE, AT_UID, + AT_SECURE, AT_SYSINFO_EHDR, AT_UID, }, sys::mman::{mmap, MapFlags, ProtFlags}, unistd::{getegid, geteuid, getgid, getuid, SysconfVar}, @@ -69,6 +69,7 @@ impl<'a, A: AsRef, E: AsRef> StackBuilder<'a, A, E> { (AT_RANDOM, at_random_addr), (AT_CLKTCK, sysconf(SysconfVar::CLK_TCK)), (AT_HWCAP, unsafe { getauxval(AT_HWCAP) }), + (AT_SYSINFO_EHDR, unsafe { getauxval(AT_SYSINFO_EHDR) }), (AT_EGID, getegid().as_raw().into()), (AT_GID, getgid().as_raw().into()), (AT_EUID, geteuid().as_raw().into()), @@ -153,6 +154,23 @@ impl<'a, A: AsRef, E: AsRef> StackBuilder<'a, A, E> { } } +// start_stack (field 28) is the initial sp; unlike environ it survives setenv(). +fn original_auxv() -> Option<*mut usize> { + let stat = std::fs::read_to_string("/proc/self/stat").ok()?; + let fields = stat.rsplit_once(')')?.1; + let start_stack: usize = fields.split_whitespace().nth(25)?.parse().ok()?; + let sp = start_stack as *const usize; + if sp.is_null() { + return None; + } + let argc = unsafe { *sp }; + let mut envp = unsafe { (sp as *const usize).add(argc + 2) }; + while unsafe { *envp } != 0 { + envp = unsafe { envp.add(1) }; + } + Some(unsafe { (envp as *mut usize).add(1) }) +} + pub fn make_stack( interp_addr: Option, bin_addr: usize, @@ -203,5 +221,22 @@ pub fn make_stack( unsafe { std::ptr::copy_nonoverlapping(data.as_ptr(), sp as *mut u8, data.len()) } + if let Some(mut auxv) = original_auxv() { + unsafe { + while *auxv != AT_NULL as usize { + let value = auxv.add(1); + match *auxv { + x if x == AT_BASE as usize => *value = interp_addr.unwrap_or_default(), + x if x == AT_PHDR as usize => *value = bin_addr + bin_header.e_phoff as usize, + x if x == AT_PHNUM as usize => *value = bin_header.e_phnum as usize, + x if x == AT_PHENT as usize => *value = bin_header.e_phentsize as usize, + x if x == AT_ENTRY as usize => *value = bin_addr + bin_header.e_entry as usize, + _ => {} + } + auxv = auxv.add(2); + } + } + } + sp }