Skip to content
Closed
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
36 changes: 27 additions & 9 deletions src/core/elf.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* segments, and copies them into guest memory.
*/

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Expand All @@ -19,41 +20,46 @@
#include "debug/log.h"
#include "utils.h"

int elf_load(const char *path, elf_info_t *info)
static int elf_load_impl(const char *path, elf_info_t *info, bool quiet)
{
memset(info, 0, sizeof(*info));

FILE *f = fopen(path, "rb");
if (!f) {
perror(path);
if (!quiet)
perror(path);
return -1;
}

elf64_ehdr_t ehdr;
if (fread(&ehdr, sizeof(ehdr), 1, f) != 1) {
log_error("%s: failed to read ELF header", path);
if (!quiet)
log_error("%s: failed to read ELF header", path);
fclose(f);
return -1;
}

/* Reject non-ELF inputs before interpreting the rest of the header. */
if (ehdr.e_ident[0] != ELFMAG0 || ehdr.e_ident[1] != ELFMAG1 ||
ehdr.e_ident[2] != ELFMAG2 || ehdr.e_ident[3] != ELFMAG3) {
log_error("%s: not an ELF file", path);
if (!quiet)
log_error("%s: not an ELF file", path);
fclose(f);
return -1;
}

/* elfuse only implements the 64-bit Linux ABI. */
if (ehdr.e_ident[EI_CLASS] != ELFCLASS64) {
log_error("%s: not a 64-bit ELF", path);
if (!quiet)
log_error("%s: not a 64-bit ELF", path);
fclose(f);
return -1;
}

/* aarch64-linux user binaries are little-endian in the supported mode. */
if (ehdr.e_ident[EI_DATA] != ELFDATA2LSB) {
log_error("%s: not little-endian", path);
if (!quiet)
log_error("%s: not little-endian", path);
fclose(f);
return -1;
}
Expand All @@ -62,8 +68,9 @@ int elf_load(const char *path, elf_info_t *info)
* diagnostic instead of a generic parse failure.
*/
if (ehdr.e_machine != EM_AARCH64 && ehdr.e_machine != EM_X86_64) {
log_error("%s: unsupported architecture (e_machine=%u)", path,
ehdr.e_machine);
if (!quiet)
log_error("%s: unsupported architecture (e_machine=%u)", path,
ehdr.e_machine);
fclose(f);
return -1;
}
Expand All @@ -72,7 +79,8 @@ int elf_load(const char *path, elf_info_t *info)
* the load base that keeps them away from elfuse's reserved regions.
*/
if (ehdr.e_type != ET_EXEC && ehdr.e_type != ET_DYN) {
log_error("%s: not an executable (e_type=%u)", path, ehdr.e_type);
if (!quiet)
log_error("%s: not an executable (e_type=%u)", path, ehdr.e_type);
fclose(f);
return -1;
}
Expand Down Expand Up @@ -204,6 +212,16 @@ int elf_load(const char *path, elf_info_t *info)
return 0;
}

int elf_load(const char *path, elf_info_t *info)
{
return elf_load_impl(path, info, false);
}

int elf_load_quiet(const char *path, elf_info_t *info)
{
return elf_load_impl(path, info, true);
}

int elf_map_segments(const elf_info_t *info,
const char *path,
void *guest_base,
Expand Down
1 change: 1 addition & 0 deletions src/core/elf.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ typedef struct {
* Returns 0 on success, -1 on failure. Does NOT copy to guest yet.
*/
int elf_load(const char *path, elf_info_t *info);
int elf_load_quiet(const char *path, elf_info_t *info);

/* Copy ELF segments into guest memory. Call after elf_load() and guest_init().
* Also copies program headers into guest memory for AT_PHDR. load_base is added
Expand Down
13 changes: 13 additions & 0 deletions src/core/guest.c
Original file line number Diff line number Diff line change
Expand Up @@ -1661,6 +1661,19 @@ int guest_get_used_regions(const guest_t *g,
n++;
}

/* Interpreter high block. The dynamic linker stores process-global state
* such as __stack_chk_guard in its high mapping just above interp_base.
* Fork children that take the region-copy path must inherit those bytes;
* otherwise libc's post-fork canary check observes zeroed guard storage
* and aborts before the child can exec.
*/
if (n < max && g->interp_base > 0 &&

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hardcoded BLOCK_2MIB at interp_base may over- or under-copy a future dynamic linker. Track the interpreter's actual load_min..load_max in elf_resolve_interp and emit a region rounded from that. The comment about __stack_chk_guard is also misleading: that symbol normally lives in libc, not ld.so.

g->interp_base <= g->guest_size - BLOCK_2MIB) {
out[n].offset = g->interp_base;
out[n].size = BLOCK_2MIB;
n++;
}

/* ELF + brk region: from elf_load_min (set by ELF loader) to brk_current.
* The lower bound is the actual ELF load address, not ELF_DEFAULT_BASE:
* ET_EXECs linked below 0x400000 (e.g. at 0x200000) have segments below the
Expand Down
14 changes: 7 additions & 7 deletions src/core/stack.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
#include <sys/random.h>

#include "core/stack.h"
#include "syscall/abi.h" /* GUEST_UID, GUEST_GID */
#include "syscall/proc.h"

/* Linux aarch64 HWCAP bits (from asm/hwcap.h). Only the bits the VZ-sanitized
* ID registers actually advertise are listed here; HWCAP bits left out (e.g.,
Expand Down Expand Up @@ -284,12 +284,12 @@ uint64_t build_linux_stack(guest_t *g,
AUX(AT_PHENT, elf_info->phentsize);
AUX(AT_PHNUM, elf_info->phnum);
AUX(AT_ENTRY, elf_info->entry + elf_load_base);
AUX(AT_UID, GUEST_UID);
AUX(AT_EUID, GUEST_UID);
AUX(AT_GID, GUEST_GID);
AUX(AT_EGID, GUEST_GID);
/* Bionic's __libc_init_AT_SECURE aborts when AT_SECURE is absent. elfuse
* never elevates privileges, so AT_SECURE is always 0.
AUX(AT_UID, proc_get_uid());
AUX(AT_EUID, proc_get_euid());
AUX(AT_GID, proc_get_gid());
AUX(AT_EGID, proc_get_egid());
/* Bionic's __libc_init_AT_SECURE aborts when AT_SECURE is absent.
* elfuse never elevates privileges, so AT_SECURE is always 0.
*/
AUX(AT_SECURE, 0);
AUX(AT_HWCAP2, query_hwcap2());
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/fork-state.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ int fork_ipc_read_all(int fd, void *buf, size_t len)
* message comfortably below that limit and stream large fd sets in multiple
* chunks.
*/
#define FORK_IPC_FD_CHUNK 120
#define FORK_IPC_FD_CHUNK 32

int fork_ipc_send_fds(int sock, const int *fds, int count)
{
Expand Down
28 changes: 24 additions & 4 deletions src/runtime/fork-state.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,30 @@
/* Magic values for IPC frame delimiters */
#define IPC_MAGIC_HEADER 0x454C464BU /* "ELFK" */
#define IPC_MAGIC_SENTINEL 0x454C4F4BU /* "ELOK" */
/* Bumped to 11 when regions_tracker_stale was added to process state so forked
* children preserve mprotect fast-path correctness.
/* Bumped to 13 when pointer-authentication key registers and the remaining
* EL0 TLS registers were added so forked children and clone-created vCPUs
* resume with the same userspace CPU context as the parent. New Ubuntu arm64
* userspace can use PAC in libc and TLS-adjacent state during fork return.
*
* Bumped to 12 when clone_flags/child_tid_gva were added so fork-process
* children can apply CLONE_CHILD_SETTID/CLEARTID inside their own snapshot.
*
* Bumped to 11 when regions_tracker_stale was added to process state so
* forked children preserve mprotect fast-path correctness.
*
* Bumped to 10 when the rosetta placement / kbuf / ttbr1 tuple was added so a
* rosetta-aware child rejects an older parent's header instead of trying to
* interpret unknown trailing fields.
*/
#define IPC_VERSION 11
#define IPC_VERSION 13

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IPC_VERSION jumps 11 -> 13, skipping wire value 12. The magic-mismatch check still catches old children, but the gap reads like a rebase artifact. Either renumber to 12 or note in the comment that version 12 was rolled into the same release.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IPC_VERSION was removed in #93
Don't touch this portion.


typedef struct {
uint64_t apiakeylo_el1, apiakeyhi_el1;
uint64_t apibkeylo_el1, apibkeyhi_el1;
uint64_t apdakeylo_el1, apdakeyhi_el1;
uint64_t apdbkeylo_el1, apdbkeyhi_el1;
uint64_t apgakeylo_el1, apgakeyhi_el1;
} ipc_pauth_keys_t;

typedef struct {
uint32_t magic;
Expand Down Expand Up @@ -60,6 +76,8 @@ typedef struct {
uint64_t rosetta_entry;
uint64_t kbuf_gpa;
uint64_t ttbr1;
uint64_t clone_flags;
uint64_t child_tid_gva;
} ipc_header_t;

typedef struct {
Expand All @@ -74,8 +92,10 @@ typedef struct {
* access faults.
*/
uint64_t ttbr1_el1;
uint64_t sctlr_el1, tcr_el1, mair_el1, cpacr_el1, tpidr_el0, sp_el1;
uint64_t sctlr_el1, tcr_el1, mair_el1, cpacr_el1;
uint64_t tpidr_el0, tpidrro_el0, tpidr2_el0, sp_el1;
uint64_t x[31];
ipc_pauth_keys_t pauth_keys;
vcpu_simd_state_t simd_state;
} ipc_registers_t;

Expand Down
Loading
Loading