Merge branch 'auxv' into 'master'

Implement auxiliary vector

See merge request redox-os/kernel!136
This commit is contained in:
Jeremy Soller
2020-07-21 14:16:05 +00:00
7 changed files with 237 additions and 100 deletions

View File

@@ -196,10 +196,16 @@ impl Grant {
pub fn start_address(&self) -> VirtualAddress {
self.start
}
pub unsafe fn set_start_address(&mut self, start: VirtualAddress) {
self.start = start;
}
pub fn size(&self) -> usize {
self.size
}
pub unsafe fn set_size(&mut self, size: usize) {
self.size = size;
}
pub fn flags(&self) -> EntryFlags {
self.flags

View File

@@ -73,6 +73,11 @@ impl<'a> Elf<'a> {
pub fn entry(&self) -> usize {
self.header.e_entry as usize
}
/// Get the program header offset
pub fn program_headers(&self) -> usize {
self.header.e_phoff as usize
}
}
pub struct ElfSections<'a> {

View File

@@ -187,7 +187,7 @@ pub extern fn userspace_init() {
}
}
syscall::fexec_kernel(fd, args.into_boxed_slice(), vars.into_boxed_slice(), None).expect("failed to execute init");
syscall::fexec_kernel(fd, args.into_boxed_slice(), vars.into_boxed_slice(), None, None).expect("failed to execute init");
panic!("init returned");
}

View File

@@ -1,11 +1,12 @@
use core::cmp;
use crate::context;
use crate::context::memory::Grant;
use crate::memory::{free_frames, used_frames};
use crate::paging::VirtualAddress;
use crate::memory::{free_frames, used_frames, PAGE_SIZE};
use crate::paging::{ActivePageTable, Page, VirtualAddress};
use crate::paging::entry::EntryFlags;
use crate::syscall::data::{Map, StatVfs};
use crate::syscall::data::{Map, Map2, StatVfs};
use crate::syscall::error::*;
use crate::syscall::flag::{PROT_EXEC, PROT_READ, PROT_WRITE};
use crate::syscall::flag::{MapFlags, PROT_EXEC, PROT_READ, PROT_WRITE};
use crate::syscall::scheme::Scheme;
pub struct MemoryScheme;
@@ -24,7 +25,7 @@ impl Scheme for MemoryScheme {
let used = used_frames() as u64;
let free = free_frames() as u64;
stat.f_bsize = 4096;
stat.f_bsize = PAGE_SIZE as u32;
stat.f_blocks = used + free;
stat.f_bfree = free;
stat.f_bavail = stat.f_bfree;
@@ -32,7 +33,7 @@ impl Scheme for MemoryScheme {
Ok(0)
}
fn fmap(&self, _id: usize, map: &Map) -> Result<usize> {
fn fmap2(&self, _id: usize, map: &Map2) -> Result<usize> {
//TODO: Abstract with other grant creation
if map.size == 0 {
Ok(0)
@@ -41,10 +42,22 @@ impl Scheme for MemoryScheme {
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
let context = context_lock.read();
let fixed = map.flags.contains(MapFlags::MAP_FIXED);
let fixed_noreplace = map.flags.contains(MapFlags::MAP_FIXED_NOREPLACE);
let mut grants = context.grants.lock();
let full_size = ((map.size + 4095)/4096) * 4096;
let mut to_address = crate::USER_GRANT_OFFSET;
let full_size = ((map.size + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;
let mut to_address = if map.address == 0 { crate::USER_GRANT_OFFSET } else {
if
map.address + full_size >= crate::PML4_SIZE * 256 // There are 256 PML4 entries reserved for userspace
&& map.address % PAGE_SIZE != 0
{
return Err(Error::new(EINVAL));
}
map.address
};
let mut entry_flags = EntryFlags::PRESENT | EntryFlags::USER_ACCESSIBLE;
if !map.flags.contains(PROT_EXEC) {
@@ -58,27 +71,97 @@ impl Scheme for MemoryScheme {
}
let mut i = 0;
while i < grants.len() {
let start = grants[i].start_address().get();
if to_address + full_size < start {
break;
while i < grants.len() {
let grant = &mut grants[i];
let grant_start = grant.start_address().get();
let grant_len = ((grant.size() + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;
let grant_end = grant_start + grant_len;
if to_address < grant_start || grant_end <= to_address {
// grant has nothing to do with the memory to map, and thus we can safely just
// go on to the next one.
if !fixed {
// Use the default grant offset, or if we've already passed it, anything after that.
to_address = cmp::max(
cmp::max(crate::USER_GRANT_OFFSET, grant_end),
to_address,
);
}
i += 1;
continue;
}
let pages = (grants[i].size() + 4095) / 4096;
let end = start + pages * 4096;
to_address = end;
i += 1;
// check whether this grant overlaps with the memory range to use, by checking that
// the start and end of the grant is not within the memory range to map
if grant_start <= to_address && grant_end > to_address || grant_start <= to_address + full_size && grant_end > to_address + full_size {
// the range overlaps, thus we'll have to continue to the next grant, or to
// insert a new grant at the end (if not MapFlags::MAP_FIXED).
if fixed_noreplace {
println!("grant: conflicts with: {:#x} - {:#x}", grant_start, grant_end);
return Err(Error::new(EEXIST));
} else if fixed {
/*
// shrink the grant, removing it if necessary. since the to_address isn't
// changed at all when mapping to a fixed address, we can just continue to
// the next grant and shrink or remove that one if it was also overlapping.
if to_address + full_size > grant_start {
let new_start = core::cmp::min(grant_end, to_address + full_size);
let new_size = grant.size() - (new_start - grant_start);
unsafe { grant.set_size(new_size) };
grant_len = ((new_size + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;
let new_start = VirtualAddress::new(new_start);
unsafe { grant.set_start_address(new_start) };
grant_start = new_start;
grant_end = grant_start + grant_len;
}
*/
// TODO
return Err(Error::new(EOPNOTSUPP));
} else {
to_address = grant_end;
i += 1;
}
continue;
}
}
grants.insert(i, Grant::map(
VirtualAddress::new(to_address),
full_size,
entry_flags
));
let start_address = VirtualAddress::new(to_address);
let end_address = VirtualAddress::new(to_address + full_size);
// Make sure it's absolutely not mapped already
let active_table = unsafe { ActivePageTable::new() };
for page in Page::range_inclusive(Page::containing_address(start_address), Page::containing_address(end_address)) {
if active_table.translate_page(page).is_some() {
return Err(Error::new(EEXIST))
}
}
grants.insert(i, Grant::map(start_address, full_size, entry_flags));
Ok(to_address)
}
}
fn fmap(&self, id: usize, map: &Map) -> Result<usize> {
if map.flags.contains(MapFlags::MAP_FIXED) {
// not supported for fmap, which lacks the address argument.
return Err(Error::new(EINVAL));
}
self.fmap2(id, &Map2 {
offset: map.offset,
size: map.size,
flags: map.flags,
address: 0,
})
}
fn fcntl(&self, _id: usize, _cmd: usize, _arg: usize) -> Result<usize> {
Ok(0)

View File

@@ -2,7 +2,7 @@ use core::{ascii, mem};
use alloc::string::String;
use alloc::vec::Vec;
use super::data::{Map, Stat, TimeSpec};
use super::data::{Map, Map2, Stat, TimeSpec};
use super::flag::*;
use super::number::*;
use super::validate::*;
@@ -114,6 +114,14 @@ pub fn format_call(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) -
d/mem::size_of::<Map>()
),
),
SYS_FMAP2 => format!(
"fmap2({}, {:?})",
b,
validate_slice(
c as *const Map2,
d/mem::size_of::<Map2>()
),
),
SYS_FUNMAP => format!(
"funmap({:#X})",
b

View File

@@ -23,11 +23,11 @@ use crate::scheme::FileHandle;
use crate::start::usermode;
use crate::syscall::data::{SigAction, Stat};
use crate::syscall::error::*;
use crate::syscall::flag::{wifcontinued, wifstopped, CloneFlags, CLONE_FILES,
CLONE_FS, CLONE_SIGHAND, CLONE_STACK, CLONE_VFORK, CLONE_VM, MapFlags,
PROT_EXEC, PROT_READ, PROT_WRITE, PTRACE_EVENT_CLONE, PTRACE_STOP_EXIT,
SigActionFlags, SIG_BLOCK, SIG_DFL, SIG_SETMASK, SIG_UNBLOCK, SIGCONT, SIGTERM,
WaitFlags, WCONTINUED, WNOHANG,WUNTRACED};
use crate::syscall::flag::{wifcontinued, wifstopped, AT_ENTRY, AT_NULL, AT_PHDR, CloneFlags,
CLONE_FILES, CLONE_FS, CLONE_SIGHAND, CLONE_STACK, CLONE_VFORK, CLONE_VM,
MapFlags, PROT_EXEC, PROT_READ, PROT_WRITE, PTRACE_EVENT_CLONE,
PTRACE_STOP_EXIT, SigActionFlags, SIG_BLOCK, SIG_DFL, SIG_SETMASK, SIG_UNBLOCK,
SIGCONT, SIGTERM, WaitFlags, WCONTINUED, WNOHANG, WUNTRACED};
use crate::syscall::ptrace_event;
use crate::syscall::validate::{validate_slice, validate_slice_mut};
@@ -655,7 +655,8 @@ fn fexec_noreturn(
name: Box<[u8]>,
data: Box<[u8]>,
args: Box<[Box<[u8]>]>,
vars: Box<[Box<[u8]>]>
vars: Box<[Box<[u8]>]>,
auxv: Box<[usize]>,
) -> ! {
let entry;
let singlestep;
@@ -807,27 +808,40 @@ fn fexec_noreturn(
context.tls = Some(tls);
}
let mut push = |arg| {
sp -= mem::size_of::<usize>();
unsafe { *(sp as *mut usize) = arg; }
};
// Push auxiliary vector
push(AT_NULL);
for &arg in auxv.iter().rev() {
push(arg);
}
drop(auxv); // no longer required
let mut arg_size = 0;
// Push arguments and variables
// Push environment variables and arguments
for iter in &[&vars, &args] {
// Push null-terminator
sp -= mem::size_of::<usize>();
unsafe { *(sp as *mut usize) = 0; }
push(0);
// Push content
// Push pointer to content
for arg in iter.iter().rev() {
sp -= mem::size_of::<usize>();
unsafe { *(sp as *mut usize) = crate::USER_ARG_OFFSET + arg_size; }
push(crate::USER_ARG_OFFSET + arg_size);
arg_size += arg.len() + 1;
}
}
// Push arguments length
sp -= mem::size_of::<usize>();
unsafe { *(sp as *mut usize) = args.len(); }
// For some reason, Linux pushes the argument count here (in
// addition to being null-terminated), but not the environment
// variable count.
// TODO: Push more counts? Less? Stop having null-termination?
push(args.len());
// Write environment and argument pointers to USER_ARG_OFFSET
if arg_size > 0 {
let mut memory = context::memory::Memory::new(
VirtualAddress::new(crate::USER_ARG_OFFSET),
@@ -856,8 +870,9 @@ fn fexec_noreturn(
context.image.push(memory.to_shared());
}
// Args no longer required, can deallocate
// Args and vars no longer required, can deallocate
drop(args);
drop(vars);
context.actions = Arc::new(Mutex::new(vec![(
SigAction {
@@ -906,7 +921,7 @@ fn fexec_noreturn(
unsafe { usermode(entry, sp, 0, singlestep) }
}
pub fn fexec_kernel(fd: FileHandle, args: Box<[Box<[u8]>]>, vars: Box<[Box<[u8]>]>, name_override_opt: Option<Box<[u8]>>) -> Result<usize> {
pub fn fexec_kernel(fd: FileHandle, args: Box<[Box<[u8]>]>, vars: Box<[Box<[u8]>]>, name_override_opt: Option<Box<[u8]>>, auxv: Option<Vec<usize>>) -> Result<usize> {
let (uid, gid) = {
let contexts = context::contexts();
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
@@ -978,72 +993,92 @@ pub fn fexec_kernel(fd: FileHandle, args: Box<[Box<[u8]>]>, vars: Box<[Box<[u8]>
return Err(Error::new(E2BIG));
}
match elf::Elf::from(&data) {
Ok(elf) => {
// We check the validity of all loadable sections here
for segment in elf.segments() {
match segment.p_type {
program_header::PT_INTERP => {
//TODO: length restraint, parse interp earlier
let mut interp = vec![0; segment.p_memsz as usize];
unsafe {
intrinsics::copy((elf.data.as_ptr() as usize + segment.p_offset as usize) as *const u8,
interp.as_mut_ptr(),
segment.p_filesz as usize);
}
let mut i = 0;
while i < interp.len() {
if interp[i] == 0 {
break;
}
i += 1;
}
interp.truncate(i);
println!(" interpreter: {:?}", ::core::str::from_utf8(&interp));
let interp_fd = super::fs::open(&interp, super::flag::O_RDONLY | super::flag::O_CLOEXEC)?;
let mut args_vec = Vec::from(args);
args_vec.insert(0, interp.into_boxed_slice());
//TODO: pass file handle in auxv
let name_override = name.into_boxed_slice();
args_vec[1] = name_override.clone();
return fexec_kernel(
interp_fd,
args_vec.into_boxed_slice(),
vars,
Some(name_override),
);
},
program_header::PT_LOAD => {
let voff = segment.p_vaddr as usize % PAGE_SIZE;
let vaddr = segment.p_vaddr as usize - voff;
// Due to the Userspace and kernel TLS bases being located right above 2GB,
// limit any loadable sections to lower than that. Eventually we will need
// to replace this with a more intelligent TLS address
if vaddr >= 0x8000_0000 {
println!("exec: invalid section address {:X}", segment.p_vaddr);
return Err(Error::new(ENOEXEC));
}
},
_ => (),
}
}
},
let elf = match elf::Elf::from(&data) {
Ok(elf) => elf,
Err(err) => {
println!("fexec: failed to execute {}: {}", fd.into(), err);
return Err(Error::new(ENOEXEC));
}
};
// `fexec_kernel` can recurse if an interpreter is found. We get the
// auxiliary vector from the first invocation, which is passed via an
// argument, or if this is the first one we create it.
let auxv = if let Some(auxv) = auxv {
auxv
} else {
let mut auxv = Vec::with_capacity(3);
auxv.push(AT_ENTRY);
auxv.push(elf.entry());
auxv.push(AT_PHDR);
auxv.push(elf.program_headers());
auxv
};
// We check the validity of all loadable sections here
for segment in elf.segments() {
match segment.p_type {
program_header::PT_INTERP => {
//TODO: length restraint, parse interp earlier
let mut interp = vec![0; segment.p_memsz as usize];
unsafe {
intrinsics::copy((elf.data.as_ptr() as usize + segment.p_offset as usize) as *const u8,
interp.as_mut_ptr(),
segment.p_filesz as usize);
}
let mut i = 0;
while i < interp.len() {
if interp[i] == 0 {
break;
}
i += 1;
}
interp.truncate(i);
println!(" interpreter: {:?}", ::core::str::from_utf8(&interp));
let interp_fd = super::fs::open(&interp, super::flag::O_RDONLY | super::flag::O_CLOEXEC)?;
let mut args_vec = Vec::from(args);
//TODO: pass file handle in auxv
let name_override = name.into_boxed_slice();
args_vec[0] = name_override.clone();
// Drop variables, since fexec_kernel probably won't return
drop(elf);
drop(interp);
return fexec_kernel(
interp_fd,
args_vec.into_boxed_slice(),
vars,
Some(name_override),
Some(auxv),
);
},
program_header::PT_LOAD => {
let voff = segment.p_vaddr as usize % PAGE_SIZE;
let vaddr = segment.p_vaddr as usize - voff;
// Due to the Userspace and kernel TLS bases being located right above 2GB,
// limit any loadable sections to lower than that. Eventually we will need
// to replace this with a more intelligent TLS address
if vaddr >= 0x8000_0000 {
println!("exec: invalid section address {:X}", segment.p_vaddr);
return Err(Error::new(ENOEXEC));
}
},
_ => (),
}
}
// This is the point of no return, quite literaly. Any checks for validity need
// to be done before, and appropriate errors returned. Otherwise, we have nothing
// to return to.
fexec_noreturn(setuid, setgid, name.into_boxed_slice(), data.into_boxed_slice(), args, vars);
fexec_noreturn(setuid, setgid, name.into_boxed_slice(), data.into_boxed_slice(), args, vars, auxv.into_boxed_slice());
}
pub fn fexec(fd: FileHandle, arg_ptrs: &[[usize; 2]], var_ptrs: &[[usize; 2]]) -> Result<usize> {
@@ -1064,7 +1099,7 @@ pub fn fexec(fd: FileHandle, arg_ptrs: &[[usize; 2]], var_ptrs: &[[usize; 2]]) -
// Neither arg_ptrs nor var_ptrs should be used after this point, the kernel
// now has owned copies in args and vars
fexec_kernel(fd, args.into_boxed_slice(), vars.into_boxed_slice(), None)
fexec_kernel(fd, args.into_boxed_slice(), vars.into_boxed_slice(), None, None)
}
pub fn exit(status: usize) -> ! {

Submodule syscall updated: 122878874d...6346fd671e