Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/glibc/lind_syscall/lind_syscall.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,5 @@ int copy_data_between_cages(uint64_t thiscage, uint64_t targetcage,
uint64_t destaddr, uint64_t destcage,
uint64_t len, uint64_t copytype);

int copy_handler_table_to_cage(uint64_t srccage, uint64_t targetcage);
#endif // _LIND_SYSCALL_H
4 changes: 2 additions & 2 deletions src/rawposix/src/sys_calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,9 @@ pub extern "C" fn fork_syscall(
// syscall implementation
threei::copy_handler_table_to_cage(
UNUSED_ARG,
child_cageid,
UNUSED_ARG,
parent_cageid,
UNUSED_ID,
child_cageid,
UNUSED_ARG,
UNUSED_ID,
UNUSED_ARG,
Expand Down
11 changes: 5 additions & 6 deletions src/threei/src/handler_table/dashmap_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,8 +234,6 @@ pub fn register_handler_impl(
/// Actual implementation of copy_handler_table_to_cage.
/// See comments in threei.rs for details.
pub fn copy_handler_table_to_cage_impl(srccage: u64, targetcage: u64) -> u64 {
// Clone the source call map snapshot so we don't hold a long-lived reference
// while mutating the destination. DashMap::clone clones the structure and keys/values.
let src_snapshot: CallnumMap = if let Some(src_entry) = HANDLERTABLE.get(&srccage) {
let snap = src_entry.value().clone();
drop(src_entry);
Expand All @@ -248,10 +246,12 @@ pub fn copy_handler_table_to_cage_impl(srccage: u64, targetcage: u64) -> u64 {
return threei_const::ELINDAPIABORTED;
};

let dst_call_map_ref = HANDLERTABLE.entry(targetcage).or_insert_with(DashMap::new);
// Overwrite the entire target handler table.
HANDLERTABLE.insert(targetcage, DashMap::new());

let dst_call_map_ref = HANDLERTABLE.get(&targetcage).unwrap();
let dst_call_map: &CallnumMap = &*dst_call_map_ref;

// Copy without overwriting existing destination handlers.
for src_call_entry in src_snapshot.iter() {
let callnum = *src_call_entry.key();
let src_target_map: &TargetCageMap = src_call_entry.value();
Expand All @@ -263,8 +263,7 @@ pub fn copy_handler_table_to_cage_impl(srccage: u64, targetcage: u64) -> u64 {
let handlefunccage = *src_target_entry.key();
let addr = *src_target_entry.value();

// Insert only if absent, preserving any existing destination mapping.
dst_target_map.entry(handlefunccage).or_insert(addr);
dst_target_map.insert(handlefunccage, addr);
}
}

Expand Down
5 changes: 3 additions & 2 deletions src/threei/src/handler_table/hashmap_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,9 +226,10 @@ pub fn copy_handler_table_to_cage_impl(srccage: u64, targetcage: u64) -> u64 {
let mut handler_table = HANDLERTABLE.lock().unwrap();

// If srccage has a handler table, clones its contents into targetcage.
// Does not overwrite any existing handlers in the target.
// Overwrites any existing handlers in the target.
if let Some(src_entry) = handler_table.get(&srccage).cloned() {
let target_entry = handler_table.entry(targetcage).or_insert_with(HashMap::new);
handler_table.insert(targetcage, HashMap::new()); // overwrite whole target
let target_entry = handler_table.get_mut(&targetcage).unwrap();
for (callnum, callnum_map) in src_entry {
let target_callnum_map = target_entry.entry(callnum).or_insert_with(HashMap::new);
for (handlefunc, handlefunccage) in callnum_map {
Expand Down
8 changes: 4 additions & 4 deletions src/threei/src/threei.rs
Original file line number Diff line number Diff line change
Expand Up @@ -329,18 +329,18 @@ pub fn register_handler(
/// interposable.
///
/// ## Arguments:
/// - targetcage: The ID of the cage receiving the copied handler mappings.
/// - srccage: The ID of the cage whose handler mappings are being copied.
/// - targetcage: The ID of the cage receiving the copied handler mappings.
///
/// ## Returns:
/// - 0 on success.
/// - `ELINDESRCH` if either source or target cage is in the EXITING state.
/// - `ELINDAPIABORTED` if srccage has no existing handler table.
pub fn copy_handler_table_to_cage(
_callnum: u64,
targetcage: u64,
_thiscage: u64,
_targetcage: u64,
srccage: u64,
_arg1cage: u64,
targetcage: u64,
_arg2: u64,
_arg2cage: u64,
_arg3: u64,
Expand Down
70 changes: 70 additions & 0 deletions tests/grate-tests/simple-tests/copy-handler-table.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#include <assert.h>
#include <lind_syscall.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

#define EXPECTED_EUID 123

int main(void) {
int parent_euid = geteuid();
if (parent_euid != EXPECTED_EUID) {
fprintf(stderr,
"[Cage|copy-handler-table] FAIL: parent expected "
"geteuid=%d, got %d\n",
EXPECTED_EUID, parent_euid);
assert(0);
}

pid_t pid = fork();
if (pid < 0) {
perror("fork failed");
assert(0);
}

if (pid == 0) {
int child_euid = geteuid();
if (child_euid != EXPECTED_EUID) {
fprintf(stderr,
"[Cage|copy-handler-table] FAIL: child "
"expected inherited geteuid=%d, got %d\n",
EXPECTED_EUID, child_euid);
assert(0);
}

int ret = copy_handler_table_to_cage(1, getpid());
if (ret != 0) {
fprintf(stderr,
"[Cage|copy-handler-table] FAIL: "
"copy_handler_table_to_cage returned %d\n",
ret);
assert(0);
}

child_euid = geteuid();
if (child_euid == EXPECTED_EUID) {
fprintf(stderr,
"[Cage|copy-handler-table] FAIL: geteuid still "
"returned inherited handler value %d after "
"table overwrite\n",
child_euid);
assert(0);
}

printf("[Cage|copy-handler-table] PASS: child inherited "
"handler, then overwrite changed geteuid to %d\n",
child_euid);
return 0;
}

int status = 0;
pid_t waited_pid = waitpid(pid, &status, 0);
assert(waited_pid == pid);
assert(WIFEXITED(status));
assert(WEXITSTATUS(status) == 0);

printf("[Cage|copy-handler-table] PASS: parent=%d child_exit=%d\n",
parent_euid, WEXITSTATUS(status));
return 0;
}
79 changes: 79 additions & 0 deletions tests/grate-tests/simple-tests/copy-handler-table_grate.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#include <assert.h>
#include <errno.h>
#include <lind_syscall.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#define GETEUID_SYSCALL_NUM 107
#define EXPECTED_EUID 123

int pass_fptr_to_wt(uint64_t fn_ptr_uint, uint64_t cageid, uint64_t arg1,
uint64_t arg1cage, uint64_t arg2, uint64_t arg2cage,
uint64_t arg3, uint64_t arg3cage, uint64_t arg4,
uint64_t arg4cage, uint64_t arg5, uint64_t arg5cage,
uint64_t arg6, uint64_t arg6cage) {
if (fn_ptr_uint == 0) {
fprintf(stderr, "[Grate|copy-handler-table] Invalid function ptr\n");
assert(0);
}

int (*fn)(uint64_t) = (int (*)(uint64_t))(uintptr_t)fn_ptr_uint;
return fn(cageid);
}

int geteuid_grate(uint64_t cageid) {
printf("[Grate|copy-handler-table] geteuid handler invoked for cage %llu\n",
cageid);
return EXPECTED_EUID;
}

int main(int argc, char *argv[]) {
if (argc < 2) {
fprintf(stderr, "Usage: %s <cage_file>\n", argv[0]);
assert(0);
}

int grateid = getpid();

pid_t pid = fork();
if (pid < 0) {
perror("fork failed");
assert(0);
} else if (pid == 0) {
int cageid = getpid();
uint64_t fn_ptr_addr = (uint64_t)(uintptr_t)&geteuid_grate;

printf("[Grate|copy-handler-table] Registering geteuid handler for cage %d in grate %d\n",
cageid, grateid);

int ret = register_handler(cageid, GETEUID_SYSCALL_NUM, grateid, fn_ptr_addr);
if (ret != 0) {
fprintf(stderr,
"[Grate|copy-handler-table] FAIL: register_handler returned %d\n",
ret);
assert(0);
}

if (execv(argv[1], &argv[1]) == -1) {
perror("execv failed");
assert(0);
}
}

int status = 0;
while (wait(&status) > 0) {
if (status != 0) {
fprintf(stderr,
"[Grate|copy-handler-table] FAIL: child exited with status %d\n",
status);
assert(0);
}
}

printf("[Grate|copy-handler-table] PASS\n");
return 0;
}