Merge branch 'userspace_fexec' into 'master'

Userspace fexec

See merge request redox-os/relibc!343
This commit is contained in:
Jeremy Soller
2022-07-27 15:39:33 +00:00
23 changed files with 1328 additions and 389 deletions
+4
View File
@@ -13,3 +13,7 @@
[submodule "pthreads-emb"]
path = pthreads-emb
url = https://gitlab.redox-os.org/redox-os/pthreads-emb.git
[submodule "compiler-builtins"]
path = compiler-builtins
url = https://gitlab.redox-os.org/redox-os/compiler-builtins.git
branch = relibc_fix_dup_symbols
Generated
+74 -53
View File
@@ -42,11 +42,11 @@ checksum = "2db2df1ebc842c41fd2c4ae5b5a577faf63bd5151b953db752fc686812bee318"
dependencies = [
"clap",
"log",
"proc-macro2 1.0.36",
"quote 1.0.16",
"proc-macro2 1.0.42",
"quote 1.0.20",
"serde",
"serde_json",
"syn 1.0.89",
"syn 1.0.98",
"tempfile",
"toml",
]
@@ -108,9 +108,9 @@ version = "0.1.0"
[[package]]
name = "fastrand"
version = "1.7.0"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf"
checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
dependencies = [
"instant",
]
@@ -146,9 +146,9 @@ dependencies = [
[[package]]
name = "itoa"
version = "1.0.1"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
[[package]]
name = "lazy_static"
@@ -165,33 +165,34 @@ version = "0.1.0"
[[package]]
name = "libc"
version = "0.2.121"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f"
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
[[package]]
name = "lock_api"
version = "0.4.6"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b"
checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.16"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if",
]
[[package]]
name = "memchr"
version = "2.4.1"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "memoffset"
@@ -204,9 +205,9 @@ dependencies = [
[[package]]
name = "num-traits"
version = "0.2.14"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
]
@@ -227,16 +228,16 @@ version = "0.4.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
dependencies = [
"unicode-xid 0.1.0",
"unicode-xid",
]
[[package]]
name = "proc-macro2"
version = "1.0.36"
version = "1.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
checksum = "c278e965f1d8cf32d6e0e96de3d3e79712178ae67986d9cf9151f51e95aac89b"
dependencies = [
"unicode-xid 0.2.2",
"unicode-ident",
]
[[package]]
@@ -250,11 +251,11 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.16"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4af2ec4714533fcdf07e886f17025ace8b997b9ce51204ee69b6da831c3da57"
checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
dependencies = [
"proc-macro2 1.0.36",
"proc-macro2 1.0.42",
]
[[package]]
@@ -297,6 +298,15 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
[[package]]
name = "redox-exec"
version = "0.1.0"
dependencies = [
"goblin",
"plain",
"redox_syscall 0.3.0",
]
[[package]]
name = "redox_syscall"
version = "0.1.57"
@@ -312,6 +322,15 @@ dependencies = [
"bitflags",
]
[[package]]
name = "redox_syscall"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca8ce969b6629faa6f60a71f84022b0217e5ca8bcaeb6d11654506a55ebf8fed"
dependencies = [
"bitflags",
]
[[package]]
name = "relibc"
version = "0.2.5"
@@ -324,12 +343,14 @@ dependencies = [
"lazy_static",
"memchr",
"memoffset",
"plain",
"posix-regex",
"ralloc",
"rand",
"redox_syscall 0.2.15",
"redox-exec",
"redox_syscall 0.3.0",
"sc",
"spin 0.9.2",
"spin 0.9.4",
]
[[package]]
@@ -361,9 +382,9 @@ dependencies = [
[[package]]
name = "ryu"
version = "1.0.9"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
[[package]]
name = "sc"
@@ -421,29 +442,29 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "serde"
version = "1.0.136"
version = "1.0.140"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
checksum = "fc855a42c7967b7c369eb5860f7164ef1f6f81c20c7cc1141f2a604e18723b03"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.136"
version = "1.0.140"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
checksum = "6f2122636b9fe3b81f1cb25099fcf2d3f542cdb1d45940d56c713158884a05da"
dependencies = [
"proc-macro2 1.0.36",
"quote 1.0.16",
"syn 1.0.89",
"proc-macro2 1.0.42",
"quote 1.0.20",
"syn 1.0.98",
]
[[package]]
name = "serde_json"
version = "1.0.79"
version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95"
checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7"
dependencies = [
"itoa",
"ryu",
@@ -458,9 +479,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "spin"
version = "0.9.2"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5"
checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09"
dependencies = [
"lock_api",
]
@@ -479,18 +500,18 @@ checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
dependencies = [
"proc-macro2 0.4.30",
"quote 0.6.13",
"unicode-xid 0.1.0",
"unicode-xid",
]
[[package]]
name = "syn"
version = "1.0.89"
version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea297be220d52398dcc07ce15a209fce436d361735ac1db700cab3b6cdfb9f54"
checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
dependencies = [
"proc-macro2 1.0.36",
"quote 1.0.16",
"unicode-xid 0.2.2",
"proc-macro2 1.0.42",
"quote 1.0.20",
"unicode-ident",
]
[[package]]
@@ -518,9 +539,9 @@ dependencies = [
[[package]]
name = "toml"
version = "0.5.8"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
dependencies = [
"serde",
]
@@ -531,6 +552,12 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e92e959f029e4f8ee25d70d15ab58d2b46f98a17bc238b9265ff0c26f6f3d67f"
[[package]]
name = "unicode-ident"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7"
[[package]]
name = "unicode-width"
version = "0.1.9"
@@ -543,12 +570,6 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "vec_map"
version = "0.8.2"
+4 -2
View File
@@ -9,7 +9,7 @@ name = "relibc"
crate-type = ["staticlib"]
[workspace]
members = ["src/crt0", "src/crti", "src/crtn", "src/ld_so"]
members = ["src/crt0", "src/crti", "src/crtn", "src/ld_so", "src/platform/redox/redox-exec"]
exclude = ["core_io", "ralloc", "tests"]
[build-dependencies]
@@ -24,6 +24,7 @@ memoffset = "0.5.1"
posix-regex = { path = "posix-regex", features = ["no_std"] }
rand = { version = "0.5.5", default-features = false }
memchr = { version = "2.2.0", default-features = false }
plain = "0.2"
[dependencies.goblin]
version = "0.0.21"
@@ -39,8 +40,9 @@ optional = true
sc = "0.2.3"
[target.'cfg(target_os = "redox")'.dependencies]
redox_syscall = "0.2.15"
redox_syscall = "0.3"
spin = "0.9.0"
redox-exec = { path = "src/platform/redox/redox-exec" }
[features]
default = []
+7 -22
View File
@@ -2,13 +2,11 @@ TARGET?=$(shell rustc -Z unstable-options --print target-spec-json | grep llvm-t
CARGO?=cargo
CARGO_TEST?=$(CARGO)
CARGOFLAGS?=-Z build-std=core,alloc,compiler_builtins
CARGO_COMMON_FLAGS=-Z build-std=core,alloc,compiler_builtins
CARGOFLAGS?=$(CARGO_COMMON_FLAGS)
RUSTCFLAGS?=
export OBJCOPY?=objcopy
# When using xargo, build it in local location
export XARGO_HOME=$(CURDIR)/target/xargo
BUILD="target/$(TARGET)"
CARGOFLAGS+="--target=$(TARGET)"
@@ -51,21 +49,7 @@ SRC=\
Cargo.* \
$(shell find src -type f)
# FIXME: Remove the following line. It's only required since xargo automatically links with compiler_builtins, which conflicts with the compiler_builtins that rustc always links with.
WEAKEN_SYMBOLS=\
-W __divti3 \
-W __fixdfti \
-W __floattidf \
-W __muloti4 \
-W __udivti3 \
-W __umodti3 \
-W __rust_probestack \
-W __rust_alloc \
-W __rust_alloc_zeroed \
-W __rust_dealloc \
-W __rust_realloc \
-W __rdl_oom \
-W __rg_oom
BUILTINS_VERSION=0.1.70
.PHONY: all clean fmt install install-headers libs submodules test
@@ -146,8 +130,7 @@ $(BUILD)/debug/libc.so: $(BUILD)/debug/librelibc.a $(BUILD)/pthreads-emb/libpthr
$(BUILD)/debug/librelibc.a: $(SRC)
CARGO_INCREMENTAL=0 $(CARGO) rustc $(CARGOFLAGS) -- --emit link=$@ $(RUSTCFLAGS)
# FIXME: Remove the following line. It's only required since xargo automatically links with compiler_builtins, which conflicts with the compiler_builtins that rustc always links with.
$(OBJCOPY) $@ $(WEAKEN_SYMBOLS)
./renamesyms.sh $@ $(BUILD)/debug/deps/
touch $@
$(BUILD)/debug/crt0.o: $(SRC)
@@ -185,7 +168,9 @@ $(BUILD)/release/libc.so: $(BUILD)/release/librelibc.a $(BUILD)/pthreads-emb/lib
$(BUILD)/release/librelibc.a: $(SRC)
CARGO_INCREMENTAL=0 $(CARGO) rustc --release $(CARGOFLAGS) -- --emit link=$@ $(RUSTCFLAGS)
$(OBJCOPY) $@ $(WEAKEN_SYMBOLS)
# TODO: Better to only allow a certain whitelisted set of symbols? Perhaps
# use some cbindgen hook, specify them manually, or grep for #[no_mangle].
./renamesyms.sh $@ $(BUILD)/release/deps/
touch $@
$(BUILD)/release/crt0.o: $(SRC)
Executable
+27
View File
@@ -0,0 +1,27 @@
#!/bin/sh
target=$1
deps_dir=$2
if [ -z "$target" ] || [ -z "$deps_dir" ]; then
echo "Usage:\n\t./renamesyms.sh TARGET DEPS_DIR"
exit 1
fi
symbols_file=`mktemp`
special_syms="__rg_alloc __rg_dealloc __rg_realloc __rg_alloc_zeroed"
for dep in `find $deps_dir -type f -name "*.rlib"`; do
nm --format=posix -g "$dep" 2>/dev/null | sed 's/.*:.*//g' | awk '{if ($2 == "T") print $1}' | sed 's/^\(.*\)$/\1 __relibc_\1/g' >> $symbols_file
done
for special_sym in $special_syms; do
echo "$special_sym __relibc_$special_sym" >> $symbols_file
done
sorted_file=`mktemp`
sort -u "$symbols_file" > "$sorted_file"
rm -f "$symbols_file"
objcopy --redefine-syms="$sorted_file" "$target"
rm -f "$sorted_file"
+1 -1
View File
@@ -1 +1 @@
nightly-2021-06-15
nightly-2022-03-18
+12
View File
@@ -8,10 +8,22 @@ use core::arch::global_asm;
#[cfg(target_arch = "x86_64")]
global_asm!("
.globl _start
.type _start, @function
_start:
mov rdi, rsp
and rsp, 0xFFFFFFFFFFFFFFF0
sub rsp, 8
mov DWORD PTR [rsp], 0x00001F80
ldmxcsr [rsp]
mov WORD PTR [rsp], 0x031F
fldcw [rsp]
add rsp, 8
call relibc_start
.size _start, . - _start
");
#[cfg(target_arch = "aarch64")]
global_asm!("
+60 -71
View File
@@ -324,16 +324,7 @@ pub extern "C" fn gcvt(value: c_double, ndigit: c_int, buf: *mut c_char) -> *mut
}
unsafe fn find_env(search: *const c_char) -> Option<(usize, *mut c_char)> {
for (i, item) in platform::inner_environ.iter().enumerate() {
let mut item = *item;
if item.is_null() {
assert_eq!(
i,
platform::inner_environ.len() - 1,
"an early null pointer in environ vector"
);
break;
}
for (i, mut item) in platform::environ_iter().enumerate() {
let mut search = search;
loop {
let end_of_query = *search == 0 || *search == b'=' as c_char;
@@ -341,7 +332,7 @@ unsafe fn find_env(search: *const c_char) -> Option<(usize, *mut c_char)> {
if *item == b'=' as c_char || end_of_query {
if *item == b'=' as c_char && end_of_query {
// Both keys env here
return Some((i, item.offset(1)));
return Some((i, item.add(1)));
} else {
break;
}
@@ -351,10 +342,11 @@ unsafe fn find_env(search: *const c_char) -> Option<(usize, *mut c_char)> {
break;
}
item = item.offset(1);
search = search.offset(1);
item = item.add(1);
search = search.add(1);
}
}
None
}
@@ -694,22 +686,33 @@ pub extern "C" fn ptsname(fildes: c_int) -> *mut c_char {
unimplemented!();
}
unsafe fn put_new_env(insert: *mut c_char) {
// XXX: Another problem is that `environ` can be set to any pointer, which means there is a
// chance of a memory leak. But we can check if it was the same as before, like musl does.
if platform::environ == platform::OUR_ENVIRON.as_mut_ptr() {
*platform::OUR_ENVIRON.last_mut().unwrap() = insert;
platform::OUR_ENVIRON.push(core::ptr::null_mut());
// Likely a no-op but is needed due to Stacked Borrows.
platform::environ = platform::OUR_ENVIRON.as_mut_ptr();
} else {
platform::OUR_ENVIRON.clear();
platform::OUR_ENVIRON.extend(platform::environ_iter());
platform::OUR_ENVIRON.push(insert);
platform::OUR_ENVIRON.push(core::ptr::null_mut());
platform::environ = platform::OUR_ENVIRON.as_mut_ptr();
}
}
#[no_mangle]
pub unsafe extern "C" fn putenv(insert: *mut c_char) -> c_int {
assert_ne!(insert, ptr::null_mut(), "putenv(NULL)");
if let Some((i, _)) = find_env(insert) {
//TODO: find out why this crashes: platform::free(platform::inner_environ[i] as *mut c_void);
platform::inner_environ[i] = insert;
// XXX: The POSIX manual states that environment variables can be *set* via the `environ`
// global variable. While we can check if a pointer belongs to our allocator, or check
// `environ` against a vector which we control, it is likely not worth the effort.
platform::environ.add(i).write(insert);
} else {
let i = platform::inner_environ.len() - 1;
assert_eq!(
platform::inner_environ[i],
ptr::null_mut(),
"environ did not end with null"
);
platform::inner_environ[i] = insert;
platform::inner_environ.push(ptr::null_mut());
platform::environ = platform::inner_environ.as_mut_ptr();
put_new_env(insert);
}
0
}
@@ -850,69 +853,45 @@ pub unsafe extern "C" fn seed48(seed16v: *mut c_ushort) -> *mut c_ushort {
rand48::SEED48_XSUBI.as_mut_ptr()
}
unsafe fn copy_kv(existing: *mut c_char, key: *const c_char, value: *const c_char, key_len: usize, value_len: usize) {
core::ptr::copy_nonoverlapping(key, existing, key_len);
core::ptr::write(existing.add(key_len), b'=' as c_char);
core::ptr::copy_nonoverlapping(value, existing.add(key_len + 1), value_len);
}
#[no_mangle]
pub unsafe extern "C" fn setenv(
mut key: *const c_char,
mut value: *const c_char,
overwrite: c_int,
) -> c_int {
let mut key_len = 0;
while *key.offset(key_len) != 0 {
key_len += 1;
}
let mut value_len = 0;
while *value.offset(value_len) != 0 {
value_len += 1;
}
let key_len = strlen(key);
let value_len = strlen(value);
let index = if let Some((i, existing)) = find_env(key) {
if let Some((i, existing)) = find_env(key) {
if overwrite == 0 {
return 0;
}
let mut existing_len = 0;
while *existing.offset(existing_len) != 0 {
existing_len += 1;
}
let existing_len = strlen(existing);
if existing_len >= value_len {
// Reuse existing element's allocation
for i in 0..=value_len {
*existing.offset(i) = *value.offset(i);
}
return 0;
core::ptr::copy_nonoverlapping(value, existing, value_len);
} else {
i
// Reuse platform::environ slot, but allocate a new pointer.
let mut ptr = platform::alloc(key_len as usize + 1 + value_len as usize) as *mut c_char;
copy_kv(ptr, key, value, key_len, value_len);
platform::environ.add(i).write(ptr);
}
} else {
let i = platform::inner_environ.len() - 1;
assert_eq!(
platform::inner_environ[i],
ptr::null_mut(),
"environ did not end with null"
);
platform::inner_environ.push(ptr::null_mut());
platform::environ = platform::inner_environ.as_mut_ptr();
i
};
// Expand platform::environ and allocate a new pointer.
let mut ptr = platform::alloc(key_len as usize + 1 + value_len as usize) as *mut c_char;
copy_kv(ptr, key, value, key_len, value_len);
put_new_env(ptr);
}
//platform::free(platform::inner_environ[index] as *mut c_void);
let mut ptr = platform::alloc(key_len as usize + 1 + value_len as usize) as *mut c_char;
platform::inner_environ[index] = ptr;
while *key != 0 {
*ptr = *key;
ptr = ptr.offset(1);
key = key.offset(1);
}
*ptr = b'=' as c_char;
ptr = ptr.offset(1);
while *value != 0 {
*ptr = *value;
ptr = ptr.offset(1);
value = value.offset(1);
}
*ptr = 0;
0
}
@@ -1174,9 +1153,19 @@ pub extern "C" fn unlockpt(fildes: c_int) -> c_int {
#[no_mangle]
pub unsafe extern "C" fn unsetenv(key: *const c_char) -> c_int {
if let Some((i, _)) = find_env(key) {
// No need to worry about updating the pointer, this does not
// reallocate in any way. And the final null is already shifted back.
platform::inner_environ.remove(i);
if platform::environ == platform::OUR_ENVIRON.as_mut_ptr() {
// No need to worry about updating the pointer, this does not
// reallocate in any way. And the final null is already shifted back.
platform::OUR_ENVIRON.remove(i);
// My UB paranoia.
platform::environ = platform::OUR_ENVIRON.as_mut_ptr();
} else {
platform::OUR_ENVIRON.clear();
platform::OUR_ENVIRON.extend(platform::environ_iter().enumerate().filter(|&(j, _)| j != i).map(|(_, v)| v));
platform::OUR_ENVIRON.push(core::ptr::null_mut());
platform::environ = platform::OUR_ENVIRON.as_mut_ptr();
}
}
0
}
+1 -27
View File
@@ -2,33 +2,7 @@
use crate::platform::types::*;
pub const AT_NULL: usize = 0; /* End of vector */
pub const AT_IGNORE: usize = 1; /* Entry should be ignored */
pub const AT_EXECFD: usize = 2; /* File descriptor of program */
pub const AT_PHDR: usize = 3; /* Program headers for program */
pub const AT_PHENT: usize = 4; /* Size of program header entry */
pub const AT_PHNUM: usize = 5; /* Number of program headers */
pub const AT_PAGESZ: usize = 6; /* System page size */
pub const AT_BASE: usize = 7; /* Base address of interpreter */
pub const AT_FLAGS: usize = 8; /* Flags */
pub const AT_ENTRY: usize = 9; /* Entry point of program */
pub const AT_NOTELF: usize = 10; /* Program is not ELF */
pub const AT_UID: usize = 11; /* Real uid */
pub const AT_EUID: usize = 12; /* Effective uid */
pub const AT_GID: usize = 13; /* Real gid */
pub const AT_EGID: usize = 14; /* Effective gid */
pub const AT_CLKTCK: usize = 17; /* Frequency of times() */
pub const AT_PLATFORM: usize = 15; /* String identifying platform. */
pub const AT_HWCAP: usize = 16; /* Machine-dependent hints about */
pub const AT_FPUCW: usize = 18; /* Used FPU control word. */
pub const AT_DCACHEBSIZE: usize = 19; /* Data cache block size. */
pub const AT_ICACHEBSIZE: usize = 20; /* Instruction cache block size. */
pub const AT_UCACHEBSIZE: usize = 21; /* Unified cache block size. */
pub const AT_IGNOREPPC: usize = 22; /* Entry should be ignored. */
pub const AT_BASE_PLATFORM: usize = 24; /* String identifying real platforms.*/
pub const AT_RANDOM: usize = 25; /* Address of 16 random bytes. */
pub const AT_HWCAP2: usize = 26; /* More machine-dependent hints about*/
pub const AT_EXECFN: usize = 31; /* Filename of executable. */
pub use crate::platform::auxv_defs::*;
#[no_mangle]
pub extern "C" fn getauxval(_t: c_ulong) -> c_ulong {
+8
View File
@@ -472,7 +472,15 @@ impl Linker {
}
fn run_init(&self, objects: &Vec<DSO>) {
use crate::platform::{self, types::*};
for obj in objects.iter().rev() {
if let Some((symbol, true)) = obj.get_sym("__relibc_init_environ") {
unsafe {
symbol.as_ptr().cast::<*mut *mut c_char>().write(platform::environ);
}
}
obj.run_init();
}
}
+1
View File
@@ -124,6 +124,7 @@ pub fn static_init(sp: &'static Stack) {
tcb.masters_len = mem::size_of::<Master>();
tcb.copy_masters().expect_notls("failed to copy TLS master data");
tcb.activate();
}
//TODO: Warning on multiple TLS sections?
+16
View File
@@ -167,6 +167,22 @@ pub extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, ld_entry: usize) ->
(argv, envs, auxv)
};
unsafe {
crate::platform::OUR_ENVIRON = envs.iter().map(|(k, v)| {
let mut var = Vec::with_capacity(k.len() + v.len() + 2);
var.extend(k.as_bytes());
var.push(b'=');
var.extend(v.as_bytes());
var.push(b'\0');
let mut var = var.into_boxed_slice();
let ptr = var.as_mut_ptr();
core::mem::forget(var);
ptr.cast()
}).chain(core::iter::once(core::ptr::null_mut())).collect::<Vec<_>>();
crate::platform::environ = crate::platform::OUR_ENVIRON.as_mut_ptr();
}
let is_manual = if let Some(img_entry) = auxv.get(&AT_ENTRY) {
*img_entry == ld_entry
} else {
+1
View File
@@ -3,6 +3,7 @@
#![allow(non_upper_case_globals)]
#![allow(unused_variables)]
#![feature(allocator_api)]
#![feature(array_chunks)]
#![feature(asm_const)]
#![feature(box_into_pin)]
#![feature(c_variadic)]
+27
View File
@@ -0,0 +1,27 @@
pub const AT_NULL: usize = 0; /* End of vector */
pub const AT_IGNORE: usize = 1; /* Entry should be ignored */
pub const AT_EXECFD: usize = 2; /* File descriptor of program */
pub const AT_PHDR: usize = 3; /* Program headers for program */
pub const AT_PHENT: usize = 4; /* Size of program header entry */
pub const AT_PHNUM: usize = 5; /* Number of program headers */
pub const AT_PAGESZ: usize = 6; /* System page size */
pub const AT_BASE: usize = 7; /* Base address of interpreter */
pub const AT_FLAGS: usize = 8; /* Flags */
pub const AT_ENTRY: usize = 9; /* Entry point of program */
pub const AT_NOTELF: usize = 10; /* Program is not ELF */
pub const AT_UID: usize = 11; /* Real uid */
pub const AT_EUID: usize = 12; /* Effective uid */
pub const AT_GID: usize = 13; /* Real gid */
pub const AT_EGID: usize = 14; /* Effective gid */
pub const AT_CLKTCK: usize = 17; /* Frequency of times() */
pub const AT_PLATFORM: usize = 15; /* String identifying platform. */
pub const AT_HWCAP: usize = 16; /* Machine-dependent hints about */
pub const AT_FPUCW: usize = 18; /* Used FPU control word. */
pub const AT_DCACHEBSIZE: usize = 19; /* Data cache block size. */
pub const AT_ICACHEBSIZE: usize = 20; /* Instruction cache block size. */
pub const AT_UCACHEBSIZE: usize = 21; /* Unified cache block size. */
pub const AT_IGNOREPPC: usize = 22; /* Entry should be ignored. */
pub const AT_BASE_PLATFORM: usize = 24; /* String identifying real platforms.*/
pub const AT_RANDOM: usize = 25; /* Address of 16 random bytes. */
pub const AT_HWCAP2: usize = 26; /* More machine-dependent hints about*/
pub const AT_EXECFN: usize = 31; /* Filename of executable. */
+24 -2
View File
@@ -34,6 +34,12 @@ mod pte;
pub use self::rlb::{Line, RawLineBuffer};
pub mod rlb;
#[cfg(target_os = "linux")]
pub mod auxv_defs;
#[cfg(target_os = "redox")]
pub use redox_exec::auxv_defs;
use self::types::*;
pub mod types;
@@ -54,8 +60,24 @@ pub static mut program_invocation_short_name: *mut c_char = ptr::null_mut();
#[allow(non_upper_case_globals)]
#[no_mangle]
pub static mut environ: *mut *mut c_char = ptr::null_mut();
#[allow(non_upper_case_globals)]
pub static mut inner_environ: Vec<*mut c_char> = Vec::new();
pub static mut OUR_ENVIRON: Vec<*mut c_char> = Vec::new();
pub fn environ_iter() -> impl Iterator<Item = *mut c_char> + 'static {
unsafe {
let mut ptrs = environ;
core::iter::from_fn(move || {
let ptr = ptrs.read();
if ptr.is_null() {
None
} else {
ptrs = ptrs.add(1);
Some(ptr)
}
})
}
}
pub trait WriteByte: fmt::Write {
fn write_u8(&mut self, byte: u8) -> fmt::Result;
-1
View File
@@ -118,7 +118,6 @@ pub unsafe extern "C" fn pte_osThreadCreate(
if stack_base as isize == -1 {
return PTE_OS_GENERAL_FAILURE;
}
ptr::write_bytes(stack_base as *mut u8, 0, stack_size);
let stack_end = stack_base.add(stack_size);
let mut stack = stack_end as *mut usize;
{
+327
View File
@@ -0,0 +1,327 @@
use core::arch::global_asm;
use core::mem::size_of;
use alloc::boxed::Box;
use alloc::vec::Vec;
use syscall::data::Map;
use syscall::flag::{MapFlags, O_CLOEXEC};
use syscall::error::{Error, Result, EINVAL, ENAMETOOLONG};
use syscall::SIGCONT;
use super::extra::{create_set_addr_space_buf, FdGuard};
fn new_context() -> Result<(FdGuard, usize)> {
// Create a new context (fields such as uid/gid will be inherited from the current context).
let fd = FdGuard::new(syscall::open("thisproc:new/open_via_dup", O_CLOEXEC)?);
// Extract pid.
let mut buffer = [0_u8; 64];
let len = syscall::fpath(*fd, &mut buffer)?;
let buffer = buffer.get(..len).ok_or(Error::new(ENAMETOOLONG))?;
let colon_idx = buffer.iter().position(|c| *c == b':').ok_or(Error::new(EINVAL))?;
let slash_idx = buffer.iter().skip(colon_idx).position(|c| *c == b'/').ok_or(Error::new(EINVAL))? + colon_idx;
let pid_bytes = buffer.get(colon_idx + 1..slash_idx).ok_or(Error::new(EINVAL))?;
let pid_str = core::str::from_utf8(pid_bytes).map_err(|_| Error::new(EINVAL))?;
let pid = pid_str.parse::<usize>().map_err(|_| Error::new(EINVAL))?;
Ok((fd, pid))
}
fn copy_str(cur_pid_fd: usize, new_pid_fd: usize, key: &str) -> Result<()> {
let cur_name_fd = FdGuard::new(syscall::dup(cur_pid_fd, key.as_bytes())?);
let new_name_fd = FdGuard::new(syscall::dup(new_pid_fd, key.as_bytes())?);
// TODO: Max path size?
let mut buf = [0_u8; 256];
let len = syscall::read(*cur_name_fd, &mut buf)?;
let buf = buf.get(..len).ok_or(Error::new(ENAMETOOLONG))?;
syscall::write(*new_name_fd, &buf)?;
Ok(())
}
#[cfg(target_arch = "x86_64")]
fn copy_env_regs(cur_pid_fd: usize, new_pid_fd: usize) -> Result<()> {
// Copy environment registers.
{
let cur_env_regs_fd = FdGuard::new(syscall::dup(cur_pid_fd, b"regs/env")?);
let new_env_regs_fd = FdGuard::new(syscall::dup(new_pid_fd, b"regs/env")?);
let mut env_regs = syscall::EnvRegisters::default();
let _ = syscall::read(*cur_env_regs_fd, &mut env_regs)?;
let _ = syscall::write(*new_env_regs_fd, &env_regs)?;
}
Ok(())
}
/// Spawns a new context sharing the same address space as the current one (i.e. a new thread).
pub unsafe fn pte_clone_impl(stack: *mut usize) -> Result<usize> {
let cur_pid_fd = FdGuard::new(syscall::open("thisproc:current/open_via_dup", O_CLOEXEC)?);
let (new_pid_fd, new_pid) = new_context()?;
// Allocate a new signal stack.
{
let sigstack_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"sigstack")?);
const SIGSTACK_SIZE: usize = 1024 * 256;
// TODO: Put sigstack at high addresses?
let target_sigstack = syscall::fmap(!0, &Map { address: 0, flags: MapFlags::PROT_READ | MapFlags::PROT_WRITE | MapFlags::MAP_PRIVATE, offset: 0, size: SIGSTACK_SIZE })? + SIGSTACK_SIZE;
let _ = syscall::write(*sigstack_fd, &usize::to_ne_bytes(target_sigstack))?;
}
copy_str(*cur_pid_fd, *new_pid_fd, "name")?;
copy_str(*cur_pid_fd, *new_pid_fd, "cwd")?;
// Reuse existing address space
{
let cur_addr_space_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"addrspace")?);
let new_addr_space_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-addrspace")?);
let buf = create_set_addr_space_buf(*cur_addr_space_fd, __relibc_internal_pte_clone_ret as usize, stack as usize);
let _ = syscall::write(*new_addr_space_sel_fd, &buf)?;
}
// Reuse file table
{
let cur_filetable_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"filetable")?);
let new_filetable_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-filetable")?);
let _ = syscall::write(*new_filetable_sel_fd, &usize::to_ne_bytes(*cur_filetable_fd))?;
}
// Reuse sigactions (on Linux, CLONE_THREAD requires CLONE_SIGHAND which implies the sigactions
// table is reused).
{
let cur_sigaction_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"sigactions")?);
let new_sigaction_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-sigactions")?);
let _ = syscall::write(*new_sigaction_sel_fd, &usize::to_ne_bytes(*cur_sigaction_fd))?;
}
copy_env_regs(*cur_pid_fd, *new_pid_fd)?;
// Unblock context.
syscall::kill(new_pid, SIGCONT)?;
let _ = syscall::waitpid(new_pid, &mut 0, syscall::WUNTRACED | syscall::WCONTINUED);
Ok(0)
}
/// Spawns a new context which will not share the same address space as the current one. File
/// descriptors from other schemes are reobtained with `dup`, and grants referencing such file
/// descriptors are reobtained through `fmap`. Other mappings are kept but duplicated using CoW.
pub fn fork_impl() -> Result<usize> {
unsafe {
Error::demux(__relibc_internal_fork_wrapper())
}
}
fn fork_inner(initial_rsp: *mut usize) -> Result<usize> {
let (cur_filetable_fd, new_pid_fd, new_pid);
{
let cur_pid_fd = FdGuard::new(syscall::open("thisproc:current/open_via_dup", O_CLOEXEC)?);
(new_pid_fd, new_pid) = new_context()?;
// Do not allocate new signal stack, but copy existing address (all memory will be re-mapped
// CoW later).
{
let cur_sigstack_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"sigstack")?);
let new_sigstack_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"sigstack")?);
let mut sigstack_buf = usize::to_ne_bytes(0);
let _ = syscall::read(*cur_sigstack_fd, &mut sigstack_buf);
let _ = syscall::write(*new_sigstack_fd, &sigstack_buf);
}
copy_str(*cur_pid_fd, *new_pid_fd, "name")?;
copy_str(*cur_pid_fd, *new_pid_fd, "cwd")?;
{
let cur_sigaction_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"sigactions")?);
let new_sigaction_fd = FdGuard::new(syscall::dup(*cur_sigaction_fd, b"copy")?);
let new_sigaction_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-sigactions")?);
let _ = syscall::write(*new_sigaction_sel_fd, &usize::to_ne_bytes(*new_sigaction_fd))?;
}
// Copy existing files into new file table, but do not reuse the same file table (i.e. new
// parent FDs will not show up for the child).
{
cur_filetable_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"filetable")?);
// This must be done before the address space is copied.
unsafe {
initial_rsp.write(*cur_filetable_fd);
initial_rsp.add(1).write(*new_pid_fd);
}
}
// CoW-duplicate address space.
{
let cur_addr_space_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"addrspace")?);
// FIXME: Find mappings which use external file descriptors
let new_addr_space_fd = FdGuard::new(syscall::dup(*cur_addr_space_fd, b"exclusive")?);
let mut buf = vec! [0_u8; 4096];
let mut bytes_read = 0;
loop {
let new_bytes_read = syscall::read(*cur_addr_space_fd, &mut buf[bytes_read..])?;
if new_bytes_read == 0 { break }
bytes_read += new_bytes_read;
}
let bytes = &buf[..bytes_read];
for struct_bytes in bytes.array_chunks::<{size_of::<usize>() * 4}>() {
let mut words = struct_bytes.array_chunks::<{size_of::<usize>()}>().copied().map(usize::from_ne_bytes);
let addr = words.next().unwrap();
let size = words.next().unwrap();
let flags = words.next().unwrap();
let offset = words.next().unwrap();
if flags & 0x8000_0000 == 0 {
continue;
}
let map_flags = MapFlags::from_bits_truncate(flags);
let grant_fd = FdGuard::new(syscall::dup(*cur_addr_space_fd, format!("grant-{:x}", addr).as_bytes())?);
redox_exec::mmap_remote(&new_addr_space_fd, &grant_fd, offset, addr, size, map_flags)?;
}
let new_addr_space_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-addrspace")?);
let buf = create_set_addr_space_buf(*new_addr_space_fd, __relibc_internal_fork_ret as usize, initial_rsp as usize);
let _ = syscall::write(*new_addr_space_sel_fd, &buf)?;
}
copy_env_regs(*cur_pid_fd, *new_pid_fd)?;
}
// Copy the file table. We do this last to ensure that all previously used file descriptors are
// closed. The only exception -- the filetable selection fd and the current filetable fd --
// will be closed by the child process.
{
// TODO: Use cross_scheme_links or something similar to avoid copying the file table in the
// kernel.
let new_filetable_fd = FdGuard::new(syscall::dup(*cur_filetable_fd, b"copy")?);
let new_filetable_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-filetable")?);
let _ = syscall::write(*new_filetable_sel_fd, &usize::to_ne_bytes(*new_filetable_fd));
}
// Unblock context.
syscall::kill(new_pid, SIGCONT)?;
// XXX: Killing with SIGCONT will put (pid, 65536) at key (pid, pgid) into the waitpid of this
// context. This means that if pgid is changed (as it is in ion for example), the pgid message
// in syscall::exit() will not be inserted as the key comparator thinks they're equal as their
// PIDs are. So, we have to call this to clear the waitpid queue to prevent deadlocks.
let _ = syscall::waitpid(new_pid, &mut 0, syscall::WUNTRACED | syscall::WCONTINUED);
Ok(new_pid)
}
#[no_mangle]
unsafe extern "sysv64" fn __relibc_internal_fork_impl(initial_rsp: *mut usize) -> usize {
Error::mux(fork_inner(initial_rsp))
}
#[no_mangle]
unsafe extern "sysv64" fn __relibc_internal_fork_hook(cur_filetable_fd: usize, new_pid_fd: usize) {
let _ = syscall::close(cur_filetable_fd);
let _ = syscall::close(new_pid_fd);
}
#[no_mangle]
core::arch::global_asm!("
.p2align 6
.globl __relibc_internal_fork_wrapper
.type __relibc_internal_fork_wrapper, @function
__relibc_internal_fork_wrapper:
push rbp
mov rbp, rsp
push rbx
push rbp
push r12
push r13
push r14
push r15
sub rsp, 32
stmxcsr [rsp+16]
fnstcw [rsp+24]
mov rdi, rsp
call __relibc_internal_fork_impl
jmp 2f
.size __relibc_internal_fork_wrapper, . - __relibc_internal_fork_wrapper
.p2align 6
.type __relibc_internal_fork_ret, @function
__relibc_internal_fork_ret:
mov rdi, [rsp]
mov rsi, [rsp + 8]
call __relibc_internal_fork_hook
ldmxcsr [rsp+16]
fldcw [rsp+24]
xor rax, rax
.p2align 4
2:
add rsp, 32
pop r15
pop r14
pop r13
pop r12
pop rbp
pop rbx
pop rbp
ret
.size __relibc_internal_fork_ret, . - __relibc_internal_fork_ret
.globl __relibc_internal_pte_clone_ret
.type __relibc_internal_pte_clone_ret, @function
.p2align 6
__relibc_internal_pte_clone_ret:
# Load registers
pop rax
pop rdi
pop rsi
pop rdx
pop rcx
pop r8
pop r9
sub rsp, 8
mov DWORD PTR [rsp], 0x00001F80
ldmxcsr [rsp]
mov WORD PTR [rsp], 0x031F
fldcw [rsp]
add rsp, 8
# Call entry point
call rax
ret
.size __relibc_internal_pte_clone_ret, . - __relibc_internal_pte_clone_ret
");
extern "sysv64" {
fn __relibc_internal_fork_wrapper() -> usize;
fn __relibc_internal_fork_ret();
fn __relibc_internal_pte_clone_ret();
}
+248
View File
@@ -0,0 +1,248 @@
use crate::c_str::{CStr, CString};
use crate::core_io::{BufReader, prelude::*, SeekFrom};
use crate::fs::File;
use crate::header::{fcntl, string::strlen};
use crate::platform::{sys::{S_ISUID, S_ISGID}, types::*};
use syscall::data::Stat;
use syscall::flag::*;
use syscall::error::*;
use redox_exec::{FdGuard, FexecResult};
fn fexec_impl(file: File, path: &[u8], args: &[&[u8]], envs: &[&[u8]], total_args_envs_size: usize, interp_override: Option<redox_exec::InterpOverride>) -> Result<usize> {
let fd = *file;
core::mem::forget(file);
let image_file = FdGuard::new(fd as usize);
let open_via_dup = FdGuard::new(syscall::open("thisproc:current/open_via_dup", 0)?);
let memory = FdGuard::new(syscall::open("memory:", 0)?);
let addrspace_selection_fd = match redox_exec::fexec_impl(image_file, open_via_dup, &memory, path, args.iter().rev(), envs.iter().rev(), total_args_envs_size, interp_override)? {
FexecResult::Normal { addrspace_handle } => addrspace_handle,
FexecResult::Interp { image_file, open_via_dup, path, interp_override: new_interp_override } => {
drop(image_file);
drop(open_via_dup);
drop(memory);
// According to elf(5), PT_INTERP requires that the interpreter path be
// null-terminated. Violating this should therefore give the "format error" ENOEXEC.
let path_cstr = CStr::from_bytes_with_nul(&path).map_err(|_| Error::new(ENOEXEC))?;
return execve(path_cstr, ArgEnv::Parsed { total_args_envs_size, args, envs }, Some(new_interp_override));
}
};
drop(memory);
// Dropping this FD will cause the address space switch.
drop(addrspace_selection_fd);
unreachable!();
}
pub enum ArgEnv<'a> {
C { argv: *const *mut c_char, envp: *const *mut c_char },
Parsed { args: &'a [&'a [u8]], envs: &'a [&'a [u8]], total_args_envs_size: usize },
}
pub fn execve(path: &CStr, arg_env: ArgEnv, interp_override: Option<redox_exec::InterpOverride>) -> Result<usize> {
// NOTE: We must omit O_CLOEXEC and close manually, otherwise it will be closed before we
// have even read it!
let mut image_file = File::open(path, O_RDONLY as c_int).map_err(|_| Error::new(ENOENT))?;
// With execve now being implemented in userspace, we need to check ourselves that this
// file is actually executable. While checking for read permission is unnecessary as the
// scheme will not allow us to read otherwise, the execute bit is completely unenforced. We
// have the permission to mmap executable memory and fill it with the program even if it is
// unset, so the best we can do is check that nothing is executed by accident.
//
// TODO: At some point we might have capabilities limiting the ability to allocate
// executable memory, and in that case we might use the `escalate:` scheme as we already do
// when the binary needs setuid/setgid.
let mut stat = Stat::default();
syscall::fstat(*image_file as usize, &mut stat)?;
let uid = syscall::getuid()?;
let gid = syscall::getuid()?;
let mode = if uid == stat.st_uid as usize {
(stat.st_mode >> 3 * 2) & 0o7
} else if gid == stat.st_gid as usize {
(stat.st_mode >> 3 * 1) & 0o7
} else {
stat.st_mode & 0o7
};
if mode & 0o1 == 0o0 {
return Err(Error::new(EPERM));
}
let wants_setugid = stat.st_mode & ((S_ISUID | S_ISGID) as u16) != 0;
// Count arguments
let mut len = 0;
match arg_env {
ArgEnv::C { argv, .. } => unsafe {
while !(*argv.add(len)).is_null() {
len += 1;
}
}
ArgEnv::Parsed { args, .. } => len = args.len(),
}
let mut args: Vec<&[u8]> = Vec::with_capacity(len);
// Read shebang (for example #!/bin/sh)
let mut _interpreter_path = None;
let is_interpreted = {
let mut read = 0;
let mut shebang = [0; 2];
while read < 2 {
match image_file.read(&mut shebang).map_err(|_| Error::new(ENOEXEC))? {
0 => break,
i => read += i,
}
}
shebang == *b"#!"
};
// Since the fexec implementation is almost fully done in userspace, the kernel can no
// longer set UID/GID accordingly, and this code checking for them before using
// hypothetical interfaces to upgrade UID/GID, can not be trusted. So we ask the
// `escalate:` scheme for help. Note that `escalate:` can be deliberately excluded from the
// scheme namespace to deny privilege escalation (such as su/sudo/doas) for untrusted
// processes.
//
// According to execve(2), Linux and most other UNIXes ignore setuid/setgid for interpreted
// executables and thereby simply keep the privileges as is. For compatibility we do that
// too.
if is_interpreted {
// TODO: Does this support prepending args to the interpreter? E.g.
// #!/usr/bin/env python3
// So, this file is interpreted.
// Then, read the actual interpreter:
let mut interpreter = Vec::new();
BufReader::new(&mut image_file).read_until(b'\n', &mut interpreter).map_err(|_| Error::new(EIO))?;
if interpreter.ends_with(&[b'\n']) {
interpreter.pop().unwrap();
}
let cstring = CString::new(interpreter).map_err(|_| Error::new(ENOEXEC))?;
image_file = File::open(&cstring, O_RDONLY as c_int).map_err(|_| Error::new(ENOENT))?;
// Make sure path is kept alive long enough, and push it to the arguments
_interpreter_path = Some(cstring);
let path_ref = _interpreter_path.as_ref().unwrap();
args.push(path_ref.as_bytes());
} else {
image_file.seek(SeekFrom::Start(0)).map_err(|_| Error::new(EIO))?;
}
let (total_args_envs_size, args, envs): (usize, Vec<_>, Vec<_>) = match arg_env {
ArgEnv::C { mut argv, mut envp } => unsafe {
let mut args_envs_size_without_nul = 0;
// Arguments
while !argv.read().is_null() {
let arg = argv.read();
let len = strlen(arg);
args.push(core::slice::from_raw_parts(arg as *const u8, len));
args_envs_size_without_nul += len;
argv = argv.add(1);
}
// Environment variables
let mut len = 0;
while !envp.add(len).read().is_null() {
len += 1;
}
let mut envs: Vec<&[u8]> = Vec::with_capacity(len);
while !envp.read().is_null() {
let env = envp.read();
let len = strlen(env);
envs.push(core::slice::from_raw_parts(env as *const u8, len));
args_envs_size_without_nul += len;
envp = envp.add(1);
}
(args_envs_size_without_nul + args.len() + envs.len(), args, envs)
}
ArgEnv::Parsed { args: new_args, envs, total_args_envs_size } => {
let prev_size: usize = args.iter().map(|a| a.len()).sum();
args.extend(new_args);
(total_args_envs_size + prev_size, args, Vec::from(envs))
}
};
// Close all O_CLOEXEC file descriptors. TODO: close_range?
{
// NOTE: This approach of implementing O_CLOEXEC will not work in multithreaded
// scenarios. While execve() is undefined according to POSIX if there exist sibling
// threads, it could still be allowed by keeping certain file descriptors and instead
// set the active file table.
let files_fd = File::new(syscall::open("thisproc:current/filetable", O_RDONLY)? as c_int);
for line in BufReader::new(files_fd).lines() {
let line = match line {
Ok(l) => l,
Err(_) => break,
};
let fd = match line.parse::<usize>() {
Ok(f) => f,
Err(_) => continue,
};
let flags = syscall::fcntl(fd, F_GETFD, 0)?;
if flags & O_CLOEXEC == O_CLOEXEC {
let _ = syscall::close(fd);
}
}
}
if !is_interpreted && wants_setugid {
// Make sure the last file descriptor not covered by O_CLOEXEC is not leaked.
drop(image_file);
// We are now going to invoke `escalate:` rather than loading the program ourselves.
let escalate_fd = FdGuard::new(syscall::open("escalate:", O_WRONLY)?);
// First, we write the path.
//
// TODO: For improved security, use a hypothetical SYS_DUP_FORWARD syscall to give the
// scheme our file descriptor. It can check through the kernel-overwritten stat.st_dev
// field that it pertains to a "trusted" scheme (i.e. of at least the privilege the
// new uid/gid has), although for now only root can open schemes. Passing a file
// descriptor and not a path will allow escalated to run in a limited namespace.
//
// TODO: Plus, at this point fexecve is not implemented (but specified in
// POSIX.1-2008), and to avoid bad syscalls such as fpath, passing a file descriptor
// would be better.
let _ = syscall::write(*escalate_fd, path.to_bytes());
// Second, we write the flattened args and envs with NUL characters separating
// individual items. This can be copied directly into the new executable's memory.
let _ = syscall::write(*escalate_fd, &flatten_with_nul(args))?;
let _ = syscall::write(*escalate_fd, &flatten_with_nul(envs))?;
// Closing will notify the scheme, and from that point we will no longer have control
// over this process (unless it fails). We do this manually since drop cannot handle
// errors.
let fd = *escalate_fd as usize;
core::mem::forget(escalate_fd);
syscall::close(fd)?;
unreachable!()
} else {
fexec_impl(image_file, path.to_bytes(), &args, &envs, total_args_envs_size, interp_override)
}
}
fn flatten_with_nul<T>(iter: impl IntoIterator<Item = T>) -> Box<[u8]> where T: AsRef<[u8]> {
let mut vec = Vec::new();
for item in iter {
vec.extend(item.as_ref());
vec.push(b'\0');
}
vec.into_boxed_slice()
}
+2 -61
View File
@@ -1,5 +1,4 @@
use core::{ptr, slice};
use core::arch::global_asm;
use core::{mem::size_of, ptr, slice};
use crate::platform::{sys::e, types::*};
@@ -49,62 +48,4 @@ pub unsafe extern "C" fn redox_physunmap(virtual_address: *mut c_void) -> c_int
e(syscall::physunmap(virtual_address as usize)) as c_int
}
extern "C" {
pub fn pte_clone_inner(stack: usize) -> usize;
}
#[cfg(target_arch = "x86_64")]
global_asm!("
.globl pte_clone_inner
.type pte_clone_inner, @function
pte_clone_inner:
# Move the 1st argument `stack` of this function into the second argument to clone.
mov rsi, rdi
mov rax, {SYS_CLONE}
mov rdi, {flags}
# Call clone syscall
syscall
# Check if child or parent
test rax, rax
jnz 2f
# Load registers
pop rax
pop rdi
pop rsi
pop rdx
pop rcx
pop r8
pop r9
# Call entry point
call rax
# Exit
mov rax, 1
xor rdi, rdi
syscall
# Invalid instruction on failure to exit
ud2
# Return PID if parent
2:
ret
.size pte_clone_inner, . - pte_clone_inner
",
flags = const(
syscall::CLONE_VM.bits()
| syscall::CLONE_FS.bits()
| syscall::CLONE_FILES.bits()
| syscall::CLONE_SIGHAND.bits()
| syscall::CLONE_STACK.bits()
),
SYS_CLONE = const(syscall::SYS_CLONE),
);
pub use redox_exec::{create_set_addr_space_buf, FdGuard};
+19 -141
View File
@@ -14,10 +14,11 @@ use crate::{
dirent::dirent,
errno::{EINVAL, EIO, ENOMEM, EPERM, ERANGE},
fcntl,
string::strlen,
sys_mman::{MAP_ANONYMOUS, PROT_READ, PROT_WRITE},
sys_random,
sys_resource::{rlimit, RLIM_INFINITY},
sys_stat::stat,
sys_stat::{stat, S_ISGID, S_ISUID},
sys_statvfs::statvfs,
sys_time::{timeval, timezone},
sys_utsname::{utsname, UTSLENGTH},
@@ -33,7 +34,14 @@ use super::{errno, types::*, Pal, Read};
static mut BRK_CUR: *mut c_void = ptr::null_mut();
static mut BRK_END: *mut c_void = ptr::null_mut();
const PAGE_SIZE: usize = 4096;
fn round_up_to_page_size(val: usize) -> usize {
(val + PAGE_SIZE - 1) / PAGE_SIZE * PAGE_SIZE
}
mod clone;
mod epoll;
mod exec;
mod extra;
mod ptrace;
mod signal;
@@ -205,140 +213,10 @@ impl Pal for Sys {
unsafe fn execve(
path: &CStr,
mut argv: *const *mut c_char,
mut envp: *const *mut c_char,
argv: *const *mut c_char,
envp: *const *mut c_char,
) -> c_int {
let mut file = match File::open(path, fcntl::O_RDONLY | fcntl::O_CLOEXEC) {
Ok(file) => file,
Err(_) => return -1,
};
let fd = *file as usize;
// Count arguments
let mut len = 0;
while !(*argv.offset(len)).is_null() {
len += 1;
}
let mut args: Vec<[usize; 2]> = Vec::with_capacity(len as usize);
// Read shebang (for example #!/bin/sh)
let interpreter = {
let mut reader = BufReader::new(&mut file);
let mut shebang = [0; 2];
let mut read = 0;
while read < 2 {
match reader.read(&mut shebang) {
Ok(0) => break,
Ok(i) => read += i,
Err(_) => return -1,
}
}
if &shebang == b"#!" {
// So, this file is interpreted.
// That means the actual file descriptor passed to `fexec` won't be this file.
// So we need to check ourselves that this file is actually be executable.
let mut stat = redox_stat::default();
if e(syscall::fstat(fd, &mut stat)) == !0 {
return -1;
}
let uid = e(syscall::getuid());
if uid == !0 {
return -1;
}
let gid = e(syscall::getuid());
if gid == !0 {
return -1;
}
let mode = if uid == stat.st_uid as usize {
(stat.st_mode >> 3 * 2) & 0o7
} else if gid == stat.st_gid as usize {
(stat.st_mode >> 3 * 1) & 0o7
} else {
stat.st_mode & 0o7
};
if mode & 0o1 == 0o0 {
errno = EPERM;
return -1;
}
// Then, read the actual interpreter:
let mut interpreter = Vec::new();
match reader.read_until(b'\n', &mut interpreter) {
Err(_) => return -1,
Ok(_) => {
if interpreter.ends_with(&[b'\n']) {
interpreter.pop().unwrap();
}
// TODO: Returning the interpreter here is actually a
// hack. Preferrably we should reassign `file =`
// directly from here. Just wait until NLL comes
// around...
Some(interpreter)
}
}
} else {
None
}
};
let mut _interpreter_path = None;
if let Some(interpreter) = interpreter {
let cstring = match CString::new(interpreter) {
Ok(cstring) => cstring,
Err(_) => return -1,
};
file = match File::open(&cstring, fcntl::O_RDONLY | fcntl::O_CLOEXEC) {
Ok(file) => file,
Err(_) => return -1,
};
// Make sure path is kept alive long enough, and push it to the arguments
_interpreter_path = Some(cstring);
let path_ref = _interpreter_path.as_ref().unwrap();
args.push([path_ref.as_ptr() as usize, path_ref.to_bytes().len()]);
} else {
if file.seek(SeekFrom::Start(0)).is_err() {
return -1;
}
}
// Arguments
while !(*argv).is_null() {
let arg = *argv;
let mut len = 0;
while *arg.offset(len) != 0 {
len += 1;
}
args.push([arg as usize, len as usize]);
argv = argv.offset(1);
}
// Environment variables
let mut len = 0;
while !(*envp.offset(len)).is_null() {
len += 1;
}
let mut envs: Vec<[usize; 2]> = Vec::with_capacity(len as usize);
while !(*envp).is_null() {
let env = *envp;
let mut len = 0;
while *env.offset(len) != 0 {
len += 1;
}
envs.push([env as usize, len as usize]);
envp = envp.offset(1);
}
e(syscall::fexec(*file as usize, &args, &envs)) as c_int
e(self::exec::execve(path, self::exec::ArgEnv::C { argv, envp }, None)) as c_int
}
fn fchdir(fd: c_int) -> c_int {
@@ -375,7 +253,7 @@ impl Pal for Sys {
}
fn fork() -> pid_t {
e(unsafe { syscall::clone(syscall::CloneFlags::empty()) }) as pid_t
e(clone::fork_impl()) as pid_t
}
fn fstat(fildes: c_int, buf: *mut stat) -> c_int {
@@ -607,7 +485,7 @@ impl Pal for Sys {
}
fn getpagesize() -> usize {
4096
PAGE_SIZE
}
fn getpgid(pid: pid_t) -> pid_t {
@@ -754,7 +632,7 @@ impl Pal for Sys {
) -> *mut c_void {
let map = Map {
offset: off as usize,
size: len,
size: round_up_to_page_size(len),
flags: syscall::MapFlags::from_bits_truncate(
((prot as usize) << 16) | ((flags as usize) & 0xFFFF),
),
@@ -771,7 +649,7 @@ impl Pal for Sys {
unsafe fn mprotect(addr: *mut c_void, len: usize, prot: c_int) -> c_int {
e(syscall::mprotect(
addr as usize,
len,
round_up_to_page_size(len),
syscall::MapFlags::from_bits((prot as usize) << 16)
.expect("mprotect: invalid bit pattern"),
)) as c_int
@@ -783,7 +661,7 @@ impl Pal for Sys {
/* TODO
e(syscall::msync(
addr as usize,
len,
round_up_to_page_size(len),
flags
)) as c_int
*/
@@ -800,7 +678,7 @@ impl Pal for Sys {
}
unsafe fn munmap(addr: *mut c_void, len: usize) -> c_int {
if e(syscall::funmap(addr as usize, len)) == !0 {
if e(syscall::funmap(addr as usize, round_up_to_page_size(len))) == !0 {
return !0;
}
0
@@ -858,7 +736,7 @@ impl Pal for Sys {
#[cfg(target_arch = "x86_64")]
unsafe fn pte_clone(stack: *mut usize) -> pid_t {
e(syscall::Error::demux(extra::pte_clone_inner(stack as usize))) as pid_t
e(clone::pte_clone_impl(stack)) as pid_t
}
fn read(fd: c_int, buf: &mut [u8]) -> ssize_t {
+14
View File
@@ -0,0 +1,14 @@
[package]
name = "redox-exec"
authors = ["4lDO2 <4lDO2@protonmail.com>"]
version = "0.1.0"
edition = "2021"
license = "MIT"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
redox_syscall = "0.3"
# TODO: Update
goblin = { version = "0.0.21", default-features = false, features = ["elf32", "elf64", "endian_fd"] }
plain = "0.2"
+412
View File
@@ -0,0 +1,412 @@
#![no_std]
#![feature(array_chunks, map_first_last)]
extern crate alloc;
use core::mem::size_of;
use alloc::{
boxed::Box,
collections::BTreeMap,
vec,
};
use syscall::{
error::*,
flag::{MapFlags, SEEK_SET},
};
#[cfg(target_arch = "x86_64")]
const PAGE_SIZE: usize = 4096;
pub enum FexecResult {
Normal { addrspace_handle: FdGuard },
Interp { path: Box<[u8]>, image_file: FdGuard, open_via_dup: FdGuard, interp_override: InterpOverride },
}
pub struct InterpOverride {
phs: Box<[u8]>,
at_entry: usize,
at_phnum: usize,
at_phent: usize,
name: Box<[u8]>,
tree: BTreeMap<usize, usize>,
}
pub fn fexec_impl<A, E>(image_file: FdGuard, open_via_dup: FdGuard, memory_scheme_fd: &FdGuard, path: &[u8], args: A, envs: E, total_args_envs_size: usize, mut interp_override: Option<InterpOverride>) -> Result<FexecResult>
where
A: IntoIterator,
E: IntoIterator,
A::Item: AsRef<[u8]>,
E::Item: AsRef<[u8]>,
{
use goblin::elf64::{header::Header, program_header::program_header64::{ProgramHeader, PT_LOAD, PT_INTERP, PF_W, PF_X}};
// Here, we do the minimum part of loading an application, which is what the kernel used to do.
// We load the executable into memory (albeit at different offsets in this executable), fix
// some misalignments, and then execute the SYS_EXEC syscall to replace the program memory
// entirely.
let mut header_bytes = [0_u8; size_of::<Header>()];
read_all(*image_file, Some(0), &mut header_bytes)?;
let header = Header::from_bytes(&header_bytes);
let grants_fd = {
let current_addrspace_fd = FdGuard::new(syscall::dup(*open_via_dup, b"addrspace")?);
FdGuard::new(syscall::dup(*current_addrspace_fd, b"empty")?)
};
let memory_fd = FdGuard::new(syscall::dup(*grants_fd, b"mem")?);
// Never allow more than 1 MiB of program headers.
const MAX_PH_SIZE: usize = 1024 * 1024;
let phentsize = u64::from(header.e_phentsize) as usize;
let phnum = u64::from(header.e_phnum) as usize;
let pheaders_size = phentsize.saturating_mul(phnum).saturating_add(size_of::<Header>());
if pheaders_size > MAX_PH_SIZE {
return Err(Error::new(E2BIG));
}
let mut phs_raw = vec! [0_u8; pheaders_size];
phs_raw[..size_of::<Header>()].copy_from_slice(&header_bytes);
let phs = &mut phs_raw[size_of::<Header>()..];
// TODO: Remove clone, but this would require more as_refs and as_muts
let mut tree = interp_override.as_mut().map_or_else(|| {
core::iter::once((0, PAGE_SIZE)).collect::<BTreeMap<_, _>>()
}, |o| core::mem::take(&mut o.tree));
const BUFSZ: usize = 1024 * 256;
let mut buf = vec! [0_u8; BUFSZ];
read_all(*image_file as usize, Some(header.e_phoff), phs).map_err(|_| Error::new(EIO))?;
for ph_idx in 0..phnum {
let ph_bytes = &phs[ph_idx * phentsize..(ph_idx + 1) * phentsize];
let segment: &ProgramHeader = plain::from_bytes(ph_bytes).map_err(|_| Error::new(EINVAL))?;
let mut flags = syscall::PROT_READ;
// W ^ X. If it is executable, do not allow it to be writable, even if requested
if segment.p_flags & PF_X == PF_X {
flags |= syscall::PROT_EXEC;
} else if segment.p_flags & PF_W == PF_W {
flags |= syscall::PROT_WRITE;
}
let voff = segment.p_vaddr as usize % PAGE_SIZE;
let vaddr = segment.p_vaddr as usize - voff;
let size =
(segment.p_memsz as usize + voff + PAGE_SIZE - 1) / PAGE_SIZE * PAGE_SIZE;
if segment.p_filesz > segment.p_memsz {
return Err(Error::new(ENOEXEC));
}
#[forbid(unreachable_patterns)]
match segment.p_type {
// PT_INTERP must come before any PT_LOAD, so we don't have to iterate twice.
PT_INTERP => {
let mut interp = vec! [0_u8; segment.p_filesz as usize];
read_all(*image_file as usize, Some(segment.p_offset), &mut interp)?;
return Ok(FexecResult::Interp {
path: interp.into_boxed_slice(),
image_file,
open_via_dup,
interp_override: InterpOverride {
at_entry: header.e_entry as usize,
at_phnum: phnum,
at_phent: phentsize,
phs: phs_raw.into_boxed_slice(),
name: path.into(),
tree,
}
});
}
PT_LOAD => {
allocate_remote(&grants_fd, memory_scheme_fd, vaddr, size, syscall::PROT_READ | syscall::PROT_WRITE)?;
syscall::lseek(*image_file as usize, segment.p_offset as isize, SEEK_SET).map_err(|_| Error::new(EIO))?;
syscall::lseek(*memory_fd, segment.p_vaddr as isize, SEEK_SET).map_err(|_| Error::new(EIO))?;
for size in core::iter::repeat(buf.len()).take((segment.p_filesz as usize) / buf.len()).chain(Some((segment.p_filesz as usize) % buf.len())) {
read_all(*image_file as usize, None, &mut buf[..size]).map_err(|_| Error::new(EIO))?;
let _ = syscall::write(*memory_fd, &buf[..size]).map_err(|_| Error::new(EIO))?;
}
mprotect_remote(&grants_fd, vaddr, size, flags)?;
if !tree.range(..=vaddr).next_back().filter(|(start, size)| **start + **size > vaddr).is_some() {
tree.insert(vaddr, size);
}
}
_ => continue,
}
}
// Setup a stack starting from the very end of the address space, and then growing downwards.
const STACK_TOP: usize = 1 << 47;
const STACK_SIZE: usize = 1024 * 1024;
allocate_remote(&grants_fd, memory_scheme_fd, STACK_TOP - STACK_SIZE, STACK_SIZE, MapFlags::PROT_READ | MapFlags::PROT_WRITE)?;
tree.insert(STACK_TOP - STACK_SIZE, STACK_SIZE);
let mut sp = STACK_TOP - 256;
let mut push = |word: usize| {
sp -= size_of::<usize>();
write_all(*memory_fd, Some(sp as u64), &usize::to_ne_bytes(word))
};
let pheaders_to_convey = if let Some(ref r#override) = interp_override {
&*r#override.phs
} else {
&*phs_raw
};
let pheaders_size_aligned = (pheaders_to_convey.len()+PAGE_SIZE-1)/PAGE_SIZE*PAGE_SIZE;
let pheaders = find_free_target_addr(&tree, pheaders_size_aligned).ok_or(Error::new(ENOMEM))?;
tree.insert(pheaders, pheaders_size_aligned);
allocate_remote(&grants_fd, memory_scheme_fd, pheaders, pheaders_size_aligned, MapFlags::PROT_READ | MapFlags::PROT_WRITE)?;
write_all(*memory_fd, Some(pheaders as u64), &pheaders_to_convey)?;
mprotect_remote(&grants_fd, pheaders, pheaders_size_aligned, MapFlags::PROT_READ)?;
push(0)?;
push(AT_NULL)?;
push(header.e_entry as usize)?;
if let Some(ref r#override) = interp_override {
push(AT_BASE)?;
push(r#override.at_entry)?;
}
push(AT_ENTRY)?;
push(pheaders + size_of::<Header>())?;
push(AT_PHDR)?;
push(interp_override.as_ref().map_or(header.e_phnum as usize, |o| o.at_phnum))?;
push(AT_PHNUM)?;
push(interp_override.as_ref().map_or(header.e_phentsize as usize, |o| o.at_phent))?;
push(AT_PHENT)?;
let args_envs_size_aligned = (total_args_envs_size+PAGE_SIZE-1)/PAGE_SIZE*PAGE_SIZE;
let target_args_env_address = find_free_target_addr(&tree, args_envs_size_aligned).ok_or(Error::new(ENOMEM))?;
allocate_remote(&grants_fd, memory_scheme_fd, target_args_env_address, args_envs_size_aligned, MapFlags::PROT_READ | MapFlags::PROT_WRITE)?;
tree.insert(target_args_env_address, args_envs_size_aligned);
let mut offset = 0;
let mut argc = 0;
{
let mut append = |source_slice: &[u8]| {
let address = target_args_env_address + offset;
write_all(*memory_fd, Some(address as u64), source_slice)?;
offset += source_slice.len() + 1;
Ok(address)
};
push(0)?;
for env in envs {
push(append(env.as_ref())?)?;
}
push(0)?;
for arg in args {
push(append(arg.as_ref())?)?;
argc += 1;
}
}
push(argc)?;
unsafe { deactivate_tcb(*open_via_dup)?; }
{
let current_sigaction_fd = FdGuard::new(syscall::dup(*open_via_dup, b"sigactions")?);
let empty_sigaction_fd = FdGuard::new(syscall::dup(*current_sigaction_fd, b"empty")?);
let sigaction_selection_fd = FdGuard::new(syscall::dup(*open_via_dup, b"current-sigactions")?);
let _ = syscall::write(*sigaction_selection_fd, &usize::to_ne_bytes(*empty_sigaction_fd))?;
}
// TODO: Restore old name if exec failed?
if let Ok(name_fd) = syscall::dup(*open_via_dup, b"name").map(FdGuard::new) {
let _ = syscall::write(*name_fd, interp_override.as_ref().map_or(path, |o| &o.name));
}
if interp_override.is_some() {
let mmap_min_fd = FdGuard::new(syscall::dup(*grants_fd, b"mmap-min-addr")?);
let last_addr = tree.iter().rev().nth(1).map_or(0, |(off, len)| *off + *len);
let aligned_last_addr = (last_addr + PAGE_SIZE - 1) / PAGE_SIZE * PAGE_SIZE;
let _ = syscall::write(*mmap_min_fd, &usize::to_ne_bytes(aligned_last_addr));
}
let addrspace_selection_fd = FdGuard::new(syscall::dup(*open_via_dup, b"current-addrspace")?);
let _ = syscall::write(*addrspace_selection_fd, &create_set_addr_space_buf(*grants_fd, header.e_entry as usize, sp));
Ok(FexecResult::Normal { addrspace_handle: addrspace_selection_fd })
}
fn write_usizes<const N: usize>(fd: &FdGuard, usizes: [usize; N]) -> Result<()> {
let _ = syscall::write(**fd, unsafe { plain::as_bytes(&usizes) });
Ok(())
}
fn allocate_remote(addrspace_fd: &FdGuard, memory_scheme_fd: &FdGuard, dst_addr: usize, len: usize, flags: MapFlags) -> Result<()> {
mmap_remote(addrspace_fd, memory_scheme_fd, 0, dst_addr, len, flags)
}
pub fn mmap_remote(addrspace_fd: &FdGuard, fd: &FdGuard, offset: usize, dst_addr: usize, len: usize, flags: MapFlags) -> Result<()> {
write_usizes(addrspace_fd, [
// op
syscall::flag::ADDRSPACE_OP_MMAP,
// fd
**fd,
// "offset"
offset,
// address
dst_addr,
// size
len,
// flags
(flags | MapFlags::MAP_FIXED_NOREPLACE).bits(),
])
}
pub fn mprotect_remote(addrspace_fd: &FdGuard, addr: usize, len: usize, flags: MapFlags) -> Result<()> {
write_usizes(addrspace_fd, [
// op
syscall::flag::ADDRSPACE_OP_MPROTECT,
// address
addr,
// size
len,
// flags
flags.bits(),
])
}
pub fn munmap_remote(addrspace_fd: &FdGuard, addr: usize, len: usize) -> Result<()> {
write_usizes(addrspace_fd, [
// op
syscall::flag::ADDRSPACE_OP_MUNMAP,
// address
addr,
// size
len,
])
}
pub fn munmap_transfer(src: &FdGuard, dst: &FdGuard, src_addr: usize, dst_addr: usize, len: usize, flags: MapFlags) -> Result<()> {
write_usizes(dst, [
// op
syscall::flag::ADDRSPACE_OP_TRANSFER,
// fd
**src,
// "offset" (source address)
src_addr,
// address
dst_addr,
// size
len,
// flags
(flags | MapFlags::MAP_FIXED_NOREPLACE).bits(),
])
}
fn read_all(fd: usize, offset: Option<u64>, buf: &mut [u8]) -> Result<()> {
if let Some(offset) = offset {
syscall::lseek(fd, offset as isize, SEEK_SET)?;
}
let mut total_bytes_read = 0;
while total_bytes_read < buf.len() {
total_bytes_read += match syscall::read(fd, &mut buf[total_bytes_read..])? {
0 => return Err(Error::new(ENOEXEC)),
bytes_read => bytes_read,
}
}
Ok(())
}
fn write_all(fd: usize, offset: Option<u64>, buf: &[u8]) -> Result<()> {
if let Some(offset) = offset {
syscall::lseek(fd, offset as isize, SEEK_SET)?;
}
let mut total_bytes_written = 0;
while total_bytes_written < buf.len() {
total_bytes_written += match syscall::write(fd, &buf[total_bytes_written..])? {
0 => return Err(Error::new(EIO)),
bytes_written => bytes_written,
}
}
Ok(())
}
// TODO: With the introduction of remote mmaps, remove this and let the kernel handle address
// allocation.
fn find_free_target_addr(tree: &BTreeMap<usize, usize>, size: usize) -> Option<usize> {
let mut iterator = tree.iter().peekable();
// Ignore the space between zero and the first region, to avoid null pointers.
while let Some((cur_address, entry_size)) = iterator.next() {
let end = *cur_address + entry_size;
if let Some((next_address, _)) = iterator.peek() {
if **next_address - end > size {
return Some(end);
}
}
// No need to check last entry, since the stack will always be put at the highest
// possible address.
}
None
}
/// Deactive TLS, used before exec() on Redox to not trick target executable into thinking TLS
/// is already initialized as if it was a thread.
#[cfg(all(target_os = "redox", target_arch = "x86_64"))]
pub unsafe fn deactivate_tcb(open_via_dup: usize) -> Result<()> {
let mut env = syscall::EnvRegisters::default();
let file = FdGuard::new(syscall::dup(open_via_dup, b"regs/env")?);
env.fsbase = 0;
env.gsbase = 0;
let _ = syscall::write(*file, &mut env)?;
Ok(())
}
pub struct FdGuard {
fd: usize,
taken: bool,
}
impl FdGuard {
pub fn new(fd: usize) -> Self {
Self {
fd, taken: false,
}
}
pub fn take(&mut self) -> usize {
self.taken = true;
self.fd
}
}
impl core::ops::Deref for FdGuard {
type Target = usize;
fn deref(&self) -> &Self::Target {
&self.fd
}
}
impl Drop for FdGuard {
fn drop(&mut self) {
if !self.taken {
let _ = syscall::close(self.fd);
}
}
}
pub fn create_set_addr_space_buf(space: usize, ip: usize, sp: usize) -> [u8; size_of::<usize>() * 3] {
let mut buf = [0_u8; 3 * size_of::<usize>()];
let mut chunks = buf.array_chunks_mut::<{size_of::<usize>()}>();
*chunks.next().unwrap() = usize::to_ne_bytes(space);
*chunks.next().unwrap() = usize::to_ne_bytes(sp);
*chunks.next().unwrap() = usize::to_ne_bytes(ip);
buf
}
#[path = "../../../auxv_defs.rs"]
pub mod auxv_defs;
use auxv_defs::*;
+39 -8
View File
@@ -67,7 +67,16 @@ static INIT_ARRAY: [extern "C" fn(); 1] = [init_array];
static mut init_complete: bool = false;
#[used]
#[no_mangle]
static mut __relibc_init_environ: *mut *mut c_char = ptr::null_mut();
fn alloc_init() {
unsafe {
if init_complete {
return;
}
}
unsafe {
if let Some(tcb) = ld_so::tcb::Tcb::current() {
if tcb.mspace != 0 {
@@ -96,6 +105,12 @@ extern "C" fn init_array() {
alloc_init();
io_init();
unsafe {
if platform::environ.is_null() {
platform::environ = __relibc_init_environ;
}
}
extern "C" {
fn pthread_init();
}
@@ -112,6 +127,15 @@ fn io_init() {
stdio::stderr = stdio::default_stderr.get();
}
}
fn setup_sigstack() {
use syscall::{Map, MapFlags};
const SIGSTACK_SIZE: usize = 1024 * 256;
let sigstack = unsafe { syscall::fmap(!0, &Map { address: 0, offset: 0, flags: MapFlags::MAP_PRIVATE | MapFlags::PROT_READ | MapFlags::PROT_WRITE, size: SIGSTACK_SIZE }) }.expect("failed to allocate sigstack") + SIGSTACK_SIZE;
let fd = syscall::open("thisproc:current/sigstack", syscall::O_WRONLY | syscall::O_CLOEXEC).expect("failed to open thisproc:current/sigstack");
syscall::write(fd, &usize::to_ne_bytes(sigstack)).expect("failed to write to thisproc:current/sigstack");
let _ = syscall::close(fd);
}
#[inline(never)]
#[no_mangle]
@@ -146,15 +170,22 @@ pub unsafe extern "C" fn relibc_start(sp: &'static Stack) -> ! {
platform::program_invocation_name = *arg;
platform::program_invocation_short_name = libgen::basename(*arg);
}
// Set up envp
let envp = sp.envp();
let mut len = 0;
while !(*envp.add(len)).is_null() {
len += 1;
// We check for NULL here since ld.so might already have initialized it for us, and we don't
// want to overwrite it if constructors in .init_array of dependency libraries have called
// setenv.
if platform::environ.is_null() {
// Set up envp
let envp = sp.envp();
let mut len = 0;
while !(*envp.add(len)).is_null() {
len += 1;
}
platform::OUR_ENVIRON = copy_string_array(envp, len);
platform::environ = platform::OUR_ENVIRON.as_mut_ptr();
}
platform::inner_environ = copy_string_array(envp, len);
platform::environ = platform::inner_environ.as_mut_ptr();
// Setup signal stack, otherwise we cannot handle any signals besides SIG_IGN/SIG_DFL behavior.
setup_sigstack();
init_array();