Skip to content

Commit a9f59ab

Browse files
author
Trung
committed
Support dynamic guest identity (UID/GID) configuration via
environment variables Allow users to dynamically configure the simulated guest UID and GID at runtime via environment variables, for example: ELFUSE_GUEST_UID and ELFUSE_GUEST_GID. If these variables are present in the host process environment, elfuse should parse them and override the default guest UID/GID.
1 parent c708a39 commit a9f59ab

6 files changed

Lines changed: 171 additions & 22 deletions

File tree

Makefile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,14 @@ $(BUILD_DIR)/test-fork-ipc-protocol-host: \
151151
@echo " LD $@"
152152
$(Q)$(CC) $(CFLAGS) -o $@ $^
153153

154+
## Build the identity override host test (native macOS binary)
155+
$(BUILD_DIR)/test-identity-override-host: \
156+
$(BUILD_DIR)/test-identity-override-host.o \
157+
$(BUILD_DIR)/syscall/proc-identity.o | $(BUILD_DIR)
158+
@echo " LD $@"
159+
$(Q)$(CC) $(CFLAGS) -o $@ $^
160+
161+
154162
## Build the proctitle argv-tail regression test (native macOS binary)
155163
# Links against the project-built proctitle.o so the exact in-tree code is
156164
# exercised; no HVF entitlement is needed because the test only manipulates

mk/tests.mk

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
test-matrix test-matrix-elfuse-aarch64 test-matrix-qemu-aarch64 \
1111
test-full test-multi-vcpu test-rwx test-sysroot-rename \
1212
test-case-collision test-case-collision-fallback test-getdents64-overlong \
13-
test-sysroot-create-paths test-fork-ipc-protocol-host \
13+
test-sysroot-create-paths test-fork-ipc-protocol-host test-identity-override-host \
1414
test-proctitle-host test-proctitle-low-stack \
1515
test-sysroot-procfs-exec test-timeout-disable test-fuse-alpine \
1616
test-sysroot-nofollow test-sysroot-chdir perf
@@ -38,12 +38,15 @@ endef
3838
## Run the unit test suite plus busybox applet validation
3939
check: $(ELFUSE_BIN) $(TEST_DEPS) check-syscall-coverage \
4040
$(BUILD_DIR)/test-tlbi-encoder-host \
41-
$(BUILD_DIR)/test-fork-ipc-protocol-host
41+
$(BUILD_DIR)/test-fork-ipc-protocol-host \
42+
$(BUILD_DIR)/test-identity-override-host
4243
@bash tests/driver.sh -e $(ELFUSE_BIN) -d $(TEST_DIR) -v
4344
@printf "\n$(BLUE)━━━ TLBI RVAE1IS encoder unit test ━━━$(RESET)\n"
4445
@$(BUILD_DIR)/test-tlbi-encoder-host
4546
@printf "\n$(BLUE)━━━ fork IPC protocol identity unit test ━━━$(RESET)\n"
4647
@$(BUILD_DIR)/test-fork-ipc-protocol-host
48+
@printf "\n$(BLUE)━━━ identity override unit test ━━━$(RESET)\n"
49+
@$(BUILD_DIR)/test-identity-override-host
4750
@printf "\n$(BLUE)━━━ proctitle argv-tail regression ━━━$(RESET)\n"
4851
@$(MAKE) --no-print-directory test-proctitle-host
4952
@printf "\n$(BLUE)━━━ proctitle low-stack regression ━━━$(RESET)\n"

src/core/stack.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include "core/stack.h"
1919
#include "syscall/abi.h" /* GUEST_UID, GUEST_GID */
20+
#include "syscall/proc.h"
2021

2122
/* Linux aarch64 HWCAP bits (from asm/hwcap.h). Only the bits the VZ-sanitized
2223
* ID registers actually advertise are listed here; HWCAP bits left out (e.g.,
@@ -284,10 +285,10 @@ uint64_t build_linux_stack(guest_t *g,
284285
AUX(AT_PHENT, elf_info->phentsize);
285286
AUX(AT_PHNUM, elf_info->phnum);
286287
AUX(AT_ENTRY, elf_info->entry + elf_load_base);
287-
AUX(AT_UID, GUEST_UID);
288-
AUX(AT_EUID, GUEST_UID);
289-
AUX(AT_GID, GUEST_GID);
290-
AUX(AT_EGID, GUEST_GID);
288+
AUX(AT_UID, proc_get_uid());
289+
AUX(AT_EUID, proc_get_euid());
290+
AUX(AT_GID, proc_get_gid());
291+
AUX(AT_EGID, proc_get_egid());
291292
/* Bionic's __libc_init_AT_SECURE aborts when AT_SECURE is absent. elfuse
292293
* never elevates privileges, so AT_SECURE is always 0.
293294
*/

src/runtime/procemu.c

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2547,17 +2547,18 @@ int proc_intercept_open(const guest_t *g,
25472547
"Tgid:\t%lld\n"
25482548
"Pid:\t%lld\n"
25492549
"PPid:\t%lld\n"
2550-
"Uid:\t%d\t%d\t%d\t%d\n"
2551-
"Gid:\t%d\t%d\t%d\t%d\n"
2550+
"Uid:\t%u\t%u\t%u\t%u\n"
2551+
"Gid:\t%u\t%u\t%u\t%u\n"
25522552
"VmPeak:\t%llu kB\n"
25532553
"VmSize:\t%llu kB\n"
25542554
"VmRSS:\t%llu kB\n"
25552555
"Threads:\t%d\n",
25562556
name, (long long) proc_get_pid(), (long long) proc_get_pid(),
2557-
(long long) proc_get_ppid(), GUEST_UID, GUEST_UID, GUEST_UID,
2558-
GUEST_UID, GUEST_GID, GUEST_GID, GUEST_GID, GUEST_GID,
2559-
(unsigned long long) vm_size_kb, (unsigned long long) vm_size_kb,
2560-
(unsigned long long) vm_rss_kb, threads);
2557+
(long long) proc_get_ppid(), proc_get_uid(), proc_get_euid(),
2558+
proc_get_suid(), proc_get_euid(), proc_get_gid(), proc_get_egid(),
2559+
proc_get_sgid(), proc_get_egid(), (unsigned long long) vm_size_kb,
2560+
(unsigned long long) vm_size_kb, (unsigned long long) vm_rss_kb,
2561+
threads);
25612562
}
25622563

25632564
/* /proc/self/limits -> resource limits from prlimit64 cache */
@@ -2656,12 +2657,13 @@ int proc_intercept_open(const guest_t *g,
26562657
"Tgid:\t%lld\n"
26572658
"Pid:\t%ld\n"
26582659
"PPid:\t%lld\n"
2659-
"Uid:\t%d\t%d\t%d\t%d\n"
2660-
"Gid:\t%d\t%d\t%d\t%d\n"
2660+
"Uid:\t%u\t%u\t%u\t%u\n"
2661+
"Gid:\t%u\t%u\t%u\t%u\n"
26612662
"Threads:\t%d\n",
26622663
proc_comm_name(), (long long) proc_get_pid(), tid,
2663-
(long long) proc_get_ppid(), GUEST_UID, GUEST_UID, GUEST_UID,
2664-
GUEST_UID, GUEST_GID, GUEST_GID, GUEST_GID, GUEST_GID,
2664+
(long long) proc_get_ppid(), proc_get_uid(), proc_get_euid(),
2665+
proc_get_suid(), proc_get_euid(), proc_get_gid(),
2666+
proc_get_egid(), proc_get_sgid(), proc_get_egid(),
26652667
thread_active_count());
26662668
}
26672669

src/syscall/proc-identity.c

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
#include <stdatomic.h>
88
#include <stdbool.h>
9+
#include <stdlib.h>
10+
#include <errno.h>
911
#include <pthread.h>
1012

1113
#include "syscall/abi.h"
@@ -28,12 +30,47 @@ void proc_identity_init(void)
2830
{
2931
guest_pid = 1;
3032
parent_pid = 0;
31-
emu_uid = GUEST_UID;
32-
emu_euid = GUEST_UID;
33-
emu_suid = GUEST_UID;
34-
emu_gid = GUEST_GID;
35-
emu_egid = GUEST_GID;
36-
emu_sgid = GUEST_GID;
33+
34+
uint32_t uid = GUEST_UID;
35+
const char *env_uid = getenv("ELFUSE_GUEST_UID");
36+
if (env_uid && *env_uid != '\0') {
37+
char *endptr = NULL;
38+
errno = 0;
39+
unsigned long val = strtoul(env_uid, &endptr, 10);
40+
const char *p = env_uid;
41+
while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\v' ||
42+
*p == '\f' || *p == '\r') {
43+
p++;
44+
}
45+
if (p != endptr && errno != ERANGE && *endptr == '\0' &&
46+
val <= UINT32_MAX && *p >= '0' && *p <= '9') {
47+
uid = (uint32_t) val;
48+
}
49+
}
50+
51+
uint32_t gid = GUEST_GID;
52+
const char *env_gid = getenv("ELFUSE_GUEST_GID");
53+
if (env_gid && *env_gid != '\0') {
54+
char *endptr = NULL;
55+
errno = 0;
56+
unsigned long val = strtoul(env_gid, &endptr, 10);
57+
const char *p = env_gid;
58+
while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\v' ||
59+
*p == '\f' || *p == '\r') {
60+
p++;
61+
}
62+
if (p != endptr && errno != ERANGE && *endptr == '\0' &&
63+
val <= UINT32_MAX && *p >= '0' && *p <= '9') {
64+
gid = (uint32_t) val;
65+
}
66+
}
67+
68+
emu_uid = uid;
69+
emu_euid = uid;
70+
emu_suid = uid;
71+
emu_gid = gid;
72+
emu_egid = gid;
73+
emu_sgid = gid;
3774
emu_nice = 0;
3875
guest_sid = 1;
3976
guest_pgid = 1;
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/* Host-side unit test for ELFUSE_GUEST_UID / ELFUSE_GUEST_GID environment
2+
* overrides.
3+
*
4+
* Copyright 2026 elfuse contributors
5+
* SPDX-License-Identifier: Apache-2.0
6+
*/
7+
8+
#include <stdio.h>
9+
#include <stdlib.h>
10+
#include <assert.h>
11+
12+
#include "syscall/proc.h"
13+
#include "syscall/proc-identity.h"
14+
#include "syscall/abi.h"
15+
16+
/* Mock shim_globals_publish_pgsid to avoid linking the entire guest shim
17+
* subsystem */
18+
void shim_globals_publish_pgsid(guest_t *g, int64_t pgid, int64_t sid);
19+
void shim_globals_publish_pgsid(guest_t *g, int64_t pgid, int64_t sid)
20+
{
21+
(void) g;
22+
(void) pgid;
23+
(void) sid;
24+
}
25+
26+
int main(void)
27+
{
28+
// Test 1: Fallback case (no env vars)
29+
unsetenv("ELFUSE_GUEST_UID");
30+
unsetenv("ELFUSE_GUEST_GID");
31+
proc_identity_init();
32+
33+
assert(proc_get_uid() == GUEST_UID);
34+
assert(proc_get_euid() == GUEST_UID);
35+
assert(proc_get_suid() == GUEST_UID);
36+
assert(proc_get_gid() == GUEST_GID);
37+
assert(proc_get_egid() == GUEST_GID);
38+
assert(proc_get_sgid() == GUEST_GID);
39+
40+
// Test 2: Override case
41+
setenv("ELFUSE_GUEST_UID", "2000", 1);
42+
setenv("ELFUSE_GUEST_GID", "3000", 1);
43+
proc_identity_init();
44+
45+
assert(proc_get_uid() == 2000);
46+
assert(proc_get_euid() == 2000);
47+
assert(proc_get_suid() == 2000);
48+
assert(proc_get_gid() == 3000);
49+
assert(proc_get_egid() == 3000);
50+
assert(proc_get_sgid() == 3000);
51+
52+
// Test 3: Override only UID
53+
setenv("ELFUSE_GUEST_UID", "4000", 1);
54+
unsetenv("ELFUSE_GUEST_GID");
55+
proc_identity_init();
56+
57+
assert(proc_get_uid() == 4000);
58+
assert(proc_get_euid() == 4000);
59+
assert(proc_get_suid() == 4000);
60+
assert(proc_get_gid() == GUEST_GID);
61+
assert(proc_get_egid() == GUEST_GID);
62+
assert(proc_get_sgid() == GUEST_GID);
63+
64+
// Test 4: Override only GID
65+
unsetenv("ELFUSE_GUEST_UID");
66+
setenv("ELFUSE_GUEST_GID", "5000", 1);
67+
proc_identity_init();
68+
69+
assert(proc_get_uid() == GUEST_UID);
70+
assert(proc_get_euid() == GUEST_UID);
71+
assert(proc_get_suid() == GUEST_UID);
72+
assert(proc_get_gid() == 5000);
73+
assert(proc_get_egid() == 5000);
74+
assert(proc_get_sgid() == 5000);
75+
76+
// Test 5: Invalid values should be ignored (fall back to default)
77+
setenv("ELFUSE_GUEST_UID", "-10", 1);
78+
setenv("ELFUSE_GUEST_GID", "abc", 1);
79+
proc_identity_init();
80+
assert(proc_get_uid() == GUEST_UID);
81+
assert(proc_get_gid() == GUEST_GID);
82+
83+
setenv("ELFUSE_GUEST_UID", "5000000000", 1); // overflows uint32_t
84+
setenv("ELFUSE_GUEST_GID", "", 1);
85+
proc_identity_init();
86+
assert(proc_get_uid() == GUEST_UID);
87+
assert(proc_get_gid() == GUEST_GID);
88+
89+
// Test 6: Dynamic values above INT_MAX but <= UINT32_MAX
90+
setenv("ELFUSE_GUEST_UID", "4294967294", 1); // valid uint32 > INT_MAX
91+
setenv("ELFUSE_GUEST_GID", "4294967295", 1); // valid uint32 (UINT32_MAX)
92+
proc_identity_init();
93+
assert(proc_get_uid() == 4294967294);
94+
assert(proc_get_gid() == 4294967295);
95+
96+
printf("test-identity-override-host: PASS\n");
97+
return 0;
98+
}

0 commit comments

Comments
 (0)