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
13 changes: 12 additions & 1 deletion src/syscall/proc-identity.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

#include "syscall/abi.h"
#include "core/shim-globals.h"
#include "runtime/thread.h"
#include "syscall/proc-identity.h"
#include "syscall/proc.h"

Expand Down Expand Up @@ -223,10 +224,20 @@ void proc_set_nice(int32_t val)
emu_nice = val;
}

bool proc_pid_alive(int pid)
{
if (pid == 0 || pid == (int) guest_pid)
return true;
return thread_tid_alive((int64_t) pid) != 0;
}

int64_t proc_sys_setpriority(int which, int who, int prio)
{
if (which != 0)
return -LINUX_EINVAL;
/* emu_nice is process-global. Reject non-self task IDs until nice is
* tracked per task rather than silently changing every thread's value.
*/
if (who != 0 && who != (int) guest_pid)
return -LINUX_ESRCH;
if (prio < -20)
Expand All @@ -241,7 +252,7 @@ int64_t proc_sys_getpriority(int which, int who)
{
if (which != 0)
return -LINUX_EINVAL;
if (who != 0 && who != (int) guest_pid)
if (!proc_pid_alive(who))
return -LINUX_ESRCH;
return 20 - emu_nice;
}
Expand Down
8 changes: 6 additions & 2 deletions src/syscall/proc.h
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,13 @@ void proc_set_ids(uint32_t uid,
/* Emulated nice value for setpriority/getpriority coherence. */
int32_t proc_get_nice(void);
void proc_set_nice(int32_t val);
bool proc_pid_alive(int pid);

/* setpriority/getpriority: only PRIO_PROCESS for self. setpriority clamps prio
* to [-20, 19]. getpriority returns 20-nice (always > 0 per Linux convention).
/* setpriority/getpriority: only PRIO_PROCESS is modeled. getpriority accepts
* any live task ID but reports the process-global emulated nice value.
* setpriority is kept self-only until elfuse tracks per-task nice state.
* setpriority clamps prio to [-20, 19]. getpriority returns 20-nice (always
* > 0 per Linux convention).
*/
int64_t proc_sys_setpriority(int which, int who, int prio);
int64_t proc_sys_getpriority(int which, int who);
Expand Down
31 changes: 8 additions & 23 deletions src/syscall/sys.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,6 @@ _Static_assert(offsetof(struct rusage, ru_maxrss) ==
offsetof(linux_rusage_t, ru_maxrss),
"ru_maxrss offset must stay aligned for fast translation");

/* Defined below in the scheduler-policy section; forward-declared so
* sys_sched_getaffinity (which sits above the policy stubs) can share the same
* per-thread TID gate.
*/
static bool sched_pid_alive(int pid);

static void groups_init_cached_linux_groups(void)
{
gid_t groups[64];
Expand Down Expand Up @@ -281,7 +275,7 @@ int64_t sys_sched_getaffinity(guest_t *g,
*/
if (pid < 0)
return -LINUX_EINVAL;
if (!sched_pid_alive(pid))
if (!proc_pid_alive(pid))
return -LINUX_ESRCH;
if (size < 8)
return -LINUX_EINVAL;
Expand Down Expand Up @@ -321,8 +315,8 @@ int64_t sys_sched_getaffinity(guest_t *g,
* elfuse models a single SCHED_OTHER thread group. Linux scheduler syscalls are
* per-thread: the pid argument is actually a TID, and a worker calling
* sched_getscheduler(gettid()) must reach its own thread entry, not just the
* thread-group leader. Live TIDs are matched via thread_tid_alive(); pid 0
* means "the calling thread" and is always accepted.
* thread-group leader. Live TIDs are matched via proc_pid_alive(); pid 0 means
* "the calling thread" and is always accepted.
*
* Any policy transition away from SCHED_OTHER is rejected unless the stub can
* model it faithfully. Callers branch on the apparent policy, and silently
Expand All @@ -339,15 +333,6 @@ int64_t sys_sched_getaffinity(guest_t *g,
* Getters that only write back leave EFAULT for the final copy_to_user step,
* matching the kernel's order.
*/
static bool sched_pid_alive(int pid)
{
if (pid == 0)
return true;
if (pid == (int) proc_get_pid())
return true;
return thread_tid_alive((int64_t) pid) != 0;
}

/* Validate a sched_param for the named policy.
*
* Returns 0 if accepted by the stub, or a negative Linux errno. EPERM is
Expand Down Expand Up @@ -390,7 +375,7 @@ int64_t sys_sched_getscheduler(int pid)
{
if (pid < 0)
return -LINUX_EINVAL;
if (!sched_pid_alive(pid))
if (!proc_pid_alive(pid))
return -LINUX_ESRCH;
return LINUX_SCHED_NORMAL;
}
Expand All @@ -399,7 +384,7 @@ int64_t sys_sched_getparam(guest_t *g, int pid, uint64_t param_gva)
{
if (pid < 0 || param_gva == 0)
return -LINUX_EINVAL;
if (!sched_pid_alive(pid))
if (!proc_pid_alive(pid))
return -LINUX_ESRCH;
linux_sched_param_t param = {0};
if (guest_write_small(g, param_gva, &param, sizeof(param)) < 0)
Expand All @@ -417,7 +402,7 @@ int64_t sys_sched_setscheduler(guest_t *g,
linux_sched_param_t param;
if (guest_read_small(g, param_gva, &param, sizeof(param)) < 0)
return -LINUX_EFAULT;
if (!sched_pid_alive(pid))
if (!proc_pid_alive(pid))
return -LINUX_ESRCH;
return sched_check_policy_param(policy, param.sched_priority);
}
Expand All @@ -429,7 +414,7 @@ int64_t sys_sched_setparam(guest_t *g, int pid, uint64_t param_gva)
linux_sched_param_t param;
if (guest_read_small(g, param_gva, &param, sizeof(param)) < 0)
return -LINUX_EFAULT;
if (!sched_pid_alive(pid))
if (!proc_pid_alive(pid))
return -LINUX_ESRCH;
/* Current policy is SCHED_OTHER, so only priority 0 is valid. Any other
* value mirrors the kernel's EINVAL for non-RT priority changes.
Expand Down Expand Up @@ -475,7 +460,7 @@ int64_t sys_sched_rr_get_interval(guest_t *g, int pid, uint64_t ts_gva)
{
if (pid < 0)
return -LINUX_EINVAL;
if (!sched_pid_alive(pid))
if (!proc_pid_alive(pid))
return -LINUX_ESRCH;
if (ts_gva == 0)
return -LINUX_EFAULT;
Expand Down
140 changes: 140 additions & 0 deletions tests/test-credentials.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,97 @@
#define __NR_geteuid 175
#define __NR_getgid 176
#define __NR_setgroups 159
#define __NR_sched_yield 124
#define __NR_clock_gettime 113

#define CLOCK_MONOTONIC 1

#define CLONE_VM 0x00000100UL
#define CLONE_FS 0x00000200UL
#define CLONE_FILES 0x00000400UL
#define CLONE_SIGHAND 0x00000800UL
#define CLONE_THREAD 0x00010000UL
#define CLONE_SYSVSEM 0x00040000UL
#define CLONE_PARENT_SETTID 0x00100000UL
#define CLONE_CHILD_CLEARTID 0x00200000UL
#define CLONE_DETACHED 0x00400000UL

typedef struct {
long tv_sec;
long tv_nsec;
} test_timespec_t;

static volatile int prio_child_tid = 0;
static volatile int prio_child_ready = 0;
static volatile int prio_child_release = 0;
static int prio_dead_tid = 0;
static char prio_child_stack[8192] __attribute__((aligned(16)));

static long monotonic_ms(void)
{
test_timespec_t ts = {0};
long rc = raw_syscall2(__NR_clock_gettime, CLOCK_MONOTONIC, (long) &ts);
if (rc != 0)
return -1;
return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
}

static void priority_child_work(void)
{
prio_child_ready = 1;
raw_futex_wake((int *) &prio_child_ready, 1);

while (prio_child_release == 0)
raw_futex_wait((int *) &prio_child_release, 0);

raw_exit(0);
}

static long spawn_priority_child(void)
{
prio_child_tid = 0;
prio_child_ready = 0;
prio_child_release = 0;

unsigned long flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |
CLONE_THREAD | CLONE_SYSVSEM | CLONE_PARENT_SETTID |
CLONE_CHILD_CLEARTID | CLONE_DETACHED;
void *stack_top = prio_child_stack + sizeof(prio_child_stack);
long ret =
raw_clone(flags, stack_top, (int *) &prio_child_tid, /* parent_tid */
0, /* tls */
(int *) &prio_child_tid); /* child_tid */
if (ret == 0) {
priority_child_work();
__builtin_unreachable();
}
return ret;
}

static void release_priority_child(void)
{
prio_child_release = 1;
raw_futex_wake((int *) &prio_child_release, 1);
while (prio_child_tid != 0)
raw_futex_wait_cleartid((int *) &prio_child_tid, prio_child_tid);
}

static long wait_for_dead_priority_tid(int tid)
{
long start_ms = monotonic_ms();
long prio = 0;

for (;;) {
prio = raw_syscall2(__NR_getpriority, 0, tid);
if (prio == -3)
return prio;
raw_syscall0(__NR_sched_yield);

long now_ms = monotonic_ms();
if (start_ms >= 0 && now_ms >= 0 && now_ms - start_ms > 2000)
return prio;
}
}

int main(void)
{
Expand Down Expand Up @@ -284,6 +375,55 @@ int main(void)
raw_syscall3(__NR_setpriority, 0, 0, 0);
}

TEST("getpriority live thread TID");
{
long ret = spawn_priority_child();
if (ret < 0) {
FAIL("clone failed");
} else {
while (prio_child_ready == 0)
raw_futex_wait((int *) &prio_child_ready, 0);

long prio = raw_syscall2(__NR_getpriority, 0, prio_child_tid);
EXPECT_TRUE(prio == 20, "getpriority(live tid) mismatch");
release_priority_child();
}
}

TEST("setpriority live thread TID rejected");
{
long ret = spawn_priority_child();
if (ret < 0) {
FAIL("clone failed");
} else {
while (prio_child_ready == 0)
raw_futex_wait((int *) &prio_child_ready, 0);

raw_syscall3(__NR_setpriority, 0, 0, 0);
long baseline_prio = raw_syscall2(__NR_getpriority, 0, 0);
int tid = prio_child_tid;
long rc = raw_syscall3(__NR_setpriority, 0, tid, 5);
long self_prio = raw_syscall2(__NR_getpriority, 0, 0);
long tid_prio = raw_syscall2(__NR_getpriority, 0, tid);
EXPECT_TRUE(rc == -3 && self_prio == baseline_prio &&
tid_prio == baseline_prio,
"setpriority(live tid) changed global nice");
release_priority_child();
prio_dead_tid = tid;
}
raw_syscall3(__NR_setpriority, 0, 0, 0);
}

TEST("getpriority dead thread TID");
{
if (prio_dead_tid == 0) {
FAIL("no dead tid from setpriority test");
} else {
long dead_prio = wait_for_dead_priority_tid(prio_dead_tid);
EXPECT_TRUE(dead_prio == -3, "getpriority(dead tid) succeeded");
}
}

/* sched_setaffinity: mask with CPU 0 should succeed */
TEST("sched_setaffinity(CPU0)");
{
Expand Down
7 changes: 7 additions & 0 deletions tests/test-identity-override-host.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ void shim_globals_publish_pgsid(guest_t *g, int64_t pgid, int64_t sid)
(void) sid;
}

int thread_tid_alive(int64_t tid);
int thread_tid_alive(int64_t tid)
{
(void) tid;
return 0;
}

int main(void)
{
/* Test 1: Fallback case (fakeroot disabled) */
Expand Down
Loading