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 README.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ with

- x86_64
- aarch64
- riscv64 (Only the trap mode and the seccomp mode are available)

## License

Expand Down
2 changes: 2 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ host-kernel calls. `mmap` and `epoll_*` are deliberately excluded
because they need W^X enforcement, shadow validation, or FD-table
gating that the fast-path cannot perform.

On **riscv64**, the rewrite mode is not available now.

Each site is classified as WRAPPER (simple `syscall; ret` pattern,
eligible for inline virtualized return: getpid=1, gettid=1, getppid=0)
or COMPLEX (result consumed internally by helpers like `raise()` that
Expand Down
5 changes: 5 additions & 0 deletions mk/toolchain.mk
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ CFLAGS += -Wno-unused-parameter
CFLAGS += -Iinclude -Isrc
LDFLAGS += -Wl,-z,noexecstack -Wl,-z,separate-code

# Disable link relaxation of riscv64 architecture to prevent long link time
ifeq ($(ARCH),riscv64)
LDFLAGS += -Wl,--no-relax
endif

# Build mode from Kconfig (fallback to BUILD= for unconfigured builds)
ifeq ($(CONFIG_BUILD_RELEASE),y)
CFLAGS += -O2 -DNDEBUG
Expand Down
1 change: 1 addition & 0 deletions scripts/alpine-sha256.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
55ea3e5a7c2c35e6268c5dcbb8e45a9cd5b0e372e7b4e798499a526834f7ed90 alpine-minirootfs-3.21.0-x86_64.tar.gz
f31202c4070c4ef7de9e157e1bd01cb4da3a2150035d74ea5372c5e86f1efac1 alpine-minirootfs-3.21.0-aarch64.tar.gz
b2c5ed2be586aebd2da5dd13dbc96bc8cc41b72e517d0726dfbbb0a9810e66d6 alpine-minirootfs-3.21.0-riscv64.tar.gz
2 changes: 2 additions & 0 deletions src/dispatch-misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,8 @@ struct kbox_dispatch forward_uname(const struct kbox_syscall_request *req,
snprintf(uts.machine, sizeof(uts.machine), "x86_64");
#elif defined(__aarch64__)
snprintf(uts.machine, sizeof(uts.machine), "aarch64");
#elif (defined(__riscv) && __riscv_xlen == 64)
snprintf(uts.machine, sizeof(uts.machine), "riscv64");
#else
snprintf(uts.machine, sizeof(uts.machine), "unknown");
#endif
Expand Down
2 changes: 1 addition & 1 deletion src/elf.c
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ int kbox_build_elf_load_plan(const unsigned char *buf,
p_memsz = read_le64(buf + off + P_MEMSZ_OFF);
p_align = read_le64(buf + off + P_ALIGN_OFF);

if (p_filesz > p_memsz)
if (p_memsz && p_filesz > p_memsz)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Looks like a drive-by fix that is not mentioned in the commit message?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This is because only those PT_LOAD sections which has non-zero p_memsz will be loaded into the memory. However, the p_filesz > p_memsz will be checked for every section. Thus, those sections with p_memsz (,such as .riscv.attributes,) will make it fail. This checks whether the p_memsz is zero before comparing the p_filesz and the p_memsz.

I have added this to the commit message.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I'm not an expert in the ELF parser area, but based on reading the commit message, I have the following questions:

  1. Is this a riscv specific issue, or has it always existed? From the new commit message, it isn't immediately clear to me why this problem is unique to riscv.

  2. It looks very much like this commit is doing two separate things:
    a) Fixing a bug
    b) Adding support for a new feature
    Should these be split into separate commits? If not, could you please explain the reasoning behind keeping them together?

Copy link
Copy Markdown
Collaborator Author

@rota1001 rota1001 Apr 14, 2026

Choose a reason for hiding this comment

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

Is this a riscv specific issue, or has it always existed?

It is not a riscv specific issue. However, so far, it has only been triggered by riscv busybox.

The things that kbox_build_elf_load_plan do are the following two things:

  • Parse flags in non-LOAD sections
  • Find out all the LOAD sections, and output their informations (such as base and size)

All the non-LOAD sections are filtered out with the following check[1]:

if (p_type != PT_LOAD || p_memsz == 0)
    continue;

However, all the sections (,including non-LOAD sections,) will be checked with the following if statement[2]:

if (p_filesz > p_memsz)
    return -1;

Thus, if there is any non-LOAD sections with p_memsz equals to 0 and p_filesz bigger than 0, the kbox_build_elf_load_plan will fail. The reason why this error was not found is this kind of section was only found in riscv so far.

It looks very much like this commit is doing two separate things

OK, I separated them in rebased commits.

[1] https://github.com/sysprog21/kbox/blob/main/src/elf.c#L452C9-L453C22
[2] https://github.com/sysprog21/kbox/blob/main/src/elf.c#L420


Here are some additional descriptions about ELF sections.
A possible question is:

Why there are some non-LOAD sections whose p_memsz are non-zero

This is because they will actually be loaded into the memory. Using the following commands can see the informations about sections:

$ readelf -l busybox
  ...
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000000040 0x0000000000000040
                 0x0000000000000230 0x0000000000000230  R      0x8
 ...
  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x00000000000c59a0 0x00000000000c59a0  R E    0x1000

It is found that a section with type PHDR is a non-LOAD section with memsz greater than 0, and its range is totally included by the LOAD section below it. This is the reason why the PHDR section should be loaded into the memory but it was skipped during kbox_build_elf_load_plan.

return -1;

/* Validate that p_offset + p_filesz does not overflow. We do
Expand Down
4 changes: 2 additions & 2 deletions src/image.c
Original file line number Diff line number Diff line change
Expand Up @@ -490,8 +490,8 @@ static const struct kbox_host_nrs *select_host_nrs(void)
{
#if defined(__x86_64__)
return &HOST_NRS_X86_64;
#elif defined(__aarch64__)
return &HOST_NRS_AARCH64;
#elif defined(__aarch64__) || (defined(__riscv) && (__riscv_xlen == 64))
return &HOST_NRS_GENERIC;
#else
return NULL;
#endif
Expand Down
3 changes: 3 additions & 0 deletions src/loader-entry.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ static int machine_to_entry_arch(uint16_t machine,
case 0xb7:
*arch_out = KBOX_LOADER_ENTRY_ARCH_AARCH64;
return 0;
case 0xf3:
*arch_out = KBOX_LOADER_ENTRY_ARCH_RISCV64;
return 0;
default:
return -1;
}
Expand Down
1 change: 1 addition & 0 deletions src/loader-entry.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
enum kbox_loader_entry_arch {
KBOX_LOADER_ENTRY_ARCH_X86_64,
KBOX_LOADER_ENTRY_ARCH_AARCH64,
KBOX_LOADER_ENTRY_ARCH_RISCV64
};

struct kbox_loader_entry_state {
Expand Down
18 changes: 18 additions & 0 deletions src/loader-transfer.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,24 @@ kbox_loader_transfer_to_guest(const struct kbox_loader_transfer_state *state)
: "r"(sp), "r"(x0), "r"(x1), "r"(x2), "r"(x3), "r"(x4), "r"(x5),
"r"(x16)
: "memory");
#elif defined(__riscv) && (__riscv_xlen == 64)
if (state->arch != KBOX_LOADER_ENTRY_ARCH_RISCV64)
__builtin_trap();
register uint64_t a0 __asm__("a0") = state->regs[0];
register uint64_t a1 __asm__("a1") = state->regs[1];
register uint64_t a2 __asm__("a2") = state->regs[2];
register uint64_t a3 __asm__("a3") = state->regs[3];
register uint64_t a4 __asm__("a4") = state->regs[4];
register uint64_t a5 __asm__("a5") = state->regs[5];
register uint64_t t0 __asm__("t0") = state->pc;
uint64_t sp = state->sp;

__asm__ volatile(
"mv sp, %0\n\t"
"jr t0\n\t"
:
: "r"(sp), "r"(a0), "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5), "r"(t0)
: "memory");
#else
(void) state;
__builtin_trap();
Expand Down
6 changes: 3 additions & 3 deletions src/rewrite.c
Original file line number Diff line number Diff line change
Expand Up @@ -608,10 +608,10 @@ static int encode_aarch64_virtual_procinfo_patch(
if (aarch64_prev_insn_syscall_nr(image, image_len, site_off, &nr) < 0)
return -1;

if (nr == (uint32_t) HOST_NRS_AARCH64.getpid ||
nr == (uint32_t) HOST_NRS_AARCH64.gettid) {
if (nr == (uint32_t) HOST_NRS_GENERIC.getpid ||
nr == (uint32_t) HOST_NRS_GENERIC.gettid) {
value = 1;
} else if (nr == (uint32_t) HOST_NRS_AARCH64.getppid) {
} else if (nr == (uint32_t) HOST_NRS_GENERIC.getppid) {
value = 0;
} else {
return -1;
Expand Down
2 changes: 1 addition & 1 deletion src/seccomp-bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ static const int deny_nrs[] = {
153, /* vhangup */
};

#elif defined(__aarch64__)
#elif defined(__aarch64__) || (defined(__riscv) && __riscv_xlen == 64)
static const int deny_nrs[] = {
/* Seccomp manipulation */
277, /* seccomp */
Expand Down
2 changes: 2 additions & 0 deletions src/seccomp-defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
#define KBOX_AUDIT_ARCH_CURRENT 0xc000003eU
#elif defined(__aarch64__)
#define KBOX_AUDIT_ARCH_CURRENT 0xc00000b7U
#elif defined(__riscv) && __riscv_xlen == 64
#define KBOX_AUDIT_ARCH_CURRENT 0xc00000f3U
#else
#error "unsupported architecture"
#endif
Expand Down
8 changes: 4 additions & 4 deletions src/seccomp-dispatch.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,28 +33,28 @@
#ifndef __NR_pidfd_open
#if defined(__x86_64__)
#define __NR_pidfd_open 434
#elif defined(__aarch64__)
#elif defined(__aarch64__) || (defined(__riscv) && (__riscv_xlen == 64))
#define __NR_pidfd_open 434
#endif
#endif
#ifndef __NR_pidfd_getfd
#if defined(__x86_64__)
#define __NR_pidfd_getfd 438
#elif defined(__aarch64__)
#elif defined(__aarch64__) || (defined(__riscv) && (__riscv_xlen == 64))
#define __NR_pidfd_getfd 438
#endif
#endif
#ifndef __NR_faccessat2
#if defined(__x86_64__)
#define __NR_faccessat2 439
#elif defined(__aarch64__)
#elif defined(__aarch64__) || (defined(__riscv) && (__riscv_xlen == 64))
#define __NR_faccessat2 439
#endif
#endif
#ifndef __NR_statx
#if defined(__x86_64__)
#define __NR_statx 332
#elif defined(__aarch64__)
#elif defined(__aarch64__) || (defined(__riscv) && (__riscv_xlen == 64))
#define __NR_statx 291
#endif
#endif
Expand Down
4 changes: 2 additions & 2 deletions src/seccomp-supervisor.c
Original file line number Diff line number Diff line change
Expand Up @@ -482,8 +482,8 @@ int kbox_run_supervisor(const struct kbox_sysnrs *sysnrs,
/* Architecture-specific host syscall numbers for the BPF filter. */
#if defined(__x86_64__)
const struct kbox_host_nrs *host_nrs = &HOST_NRS_X86_64;
#elif defined(__aarch64__)
const struct kbox_host_nrs *host_nrs = &HOST_NRS_AARCH64;
#elif defined(__aarch64__) || (defined(__riscv) && __riscv_xlen == 64)
const struct kbox_host_nrs *host_nrs = &HOST_NRS_GENERIC;
#else
#error "Unsupported architecture"
#endif
Expand Down
2 changes: 1 addition & 1 deletion src/syscall-nr.c
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ const struct kbox_host_nrs HOST_NRS_X86_64 = {
.readlink = 89,
};

const struct kbox_host_nrs HOST_NRS_AARCH64 = {
const struct kbox_host_nrs HOST_NRS_GENERIC = {
.openat = 56,
.openat2 = 437,
.open = -1,
Expand Down
2 changes: 1 addition & 1 deletion src/syscall-nr.h
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ struct kbox_host_nrs {
};

extern const struct kbox_host_nrs HOST_NRS_X86_64;
extern const struct kbox_host_nrs HOST_NRS_AARCH64;
extern const struct kbox_host_nrs HOST_NRS_GENERIC;

const struct kbox_sysnrs *detect_sysnrs(void);
const char *syscall_name_from_nr(const struct kbox_host_nrs *h, int nr);
Expand Down
Loading
Loading