Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
cmake_minimum_required(VERSION 3.20)
project(test-uid-gid-re-setters C)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)

# Group C: setreuid / setregid 两参数 setter (real + effective)
add_executable(test-uid-gid-re-setters
src/main.c
src/setreuid.c
src/setregid.c
src/saved_id_semantics.c
src/boundary.c
src/matrix.c
src/procfs_visibility.c
src/nptl_sync.c
src/core_dump_inhibit.c
src/errno_preservation.c)
target_link_libraries(test-uid-gid-re-setters PRIVATE pthread)

target_include_directories(test-uid-gid-re-setters PRIVATE src)
target_compile_options(test-uid-gid-re-setters PRIVATE -Wall -Wextra -Werror)
install(TARGETS test-uid-gid-re-setters RUNTIME DESTINATION usr/bin/starry-test-suit)
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#define _GNU_SOURCE
#include "test_framework.h"

#include <errno.h>
#include <stdio.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

/* boundary — setreuid/setregid 边界 + 异常输入。
*
* 边界值:
* - (-1, -1) sentinel — 已在 setreuid (a) 测;这里测 raw syscall 直传 u32::MAX
* - (uid, -1) / (-1, uid) — 单参数 NOCHG
* - root setreuid(u32::MAX-1, u32::MAX-1) — 极大值,root 接受
* - 错误输入:raw syscall 传无效组合
*/

static int waitpid_safely(pid_t pid, int *status)
{
pid_t r = waitpid(pid, status, 0);
return r == pid ? 0 : -1;
}

/* raw syscall 直传 u32::MAX (NOCHG) — kernel 应识别为 NOCHG */
static void boundary_raw_u32max_nochg(void)
{
uid_t r0, e0, s0;
getresuid(&r0, &e0, &s0);
long rc = syscall(SYS_setreuid, (uid_t)-1, (uid_t)-1);
CHECK(rc == 0, "boundary (a) raw setreuid(u32::MAX, u32::MAX) -> 0");
uid_t r1, e1, s1;
getresuid(&r1, &e1, &s1);
CHECK(r0 == r1 && e0 == e1 && s0 == s1, "boundary (a) cred unchanged after NOCHG sentinel");
}

/* root: 极大值 setreuid 应成功(CAP_SETUID 时无 EINVAL)*/
static void boundary_root_extreme_values(void)
{
if (getuid() != 0) {
printf(" boundary (b) skip: not root\n");
return;
}
pid_t pid = fork();

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.

小建议(非阻塞):boundary_unpriv_extreme_eperm()boundary_root_extreme_values()pid < 0(fork 失败)时缺少错误处理,当前只检查 pid > 0。建议在 if (pid > 0) 前加 if (pid < 0) { CHECK(0, "fork failed"); return; }

if (pid == 0) {
/* 0xFFFFFFFE = u32::MAX - 1,避免与 NOCHG 混淆 */
if (setreuid(0xFFFFFFFE, 0xFFFFFFFE) != 0) _exit(1);
uid_t r, e, s;
if (getresuid(&r, &e, &s) != 0) _exit(2);
if (r == 0xFFFFFFFE && e == 0xFFFFFFFE) _exit(0);
_exit(3);
}
if (pid > 0) {
int status;
if (waitpid_safely(pid, &status) == 0) {
CHECK(WIFEXITED(status) && WEXITSTATUS(status) == 0,
"boundary (b) root setreuid(u32::MAX-1) accepted (no EINVAL)");
}
}
}

/* unpriv: setreuid(u32::MAX-1, -1) 应 EPERM (不在自身 ID 集) */
static void boundary_unpriv_extreme_eperm(void)
{
if (getuid() != 0) {
printf(" boundary (c) skip\n");
return;
}
pid_t pid = fork();
if (pid == 0) {
if (setresuid(1000, 1000, 1000) != 0) _exit(99);
errno = 0;
int rc = setreuid(0xFFFFFFFE, (uid_t)-1);
if (rc == -1 && errno == EPERM) _exit(0);
_exit(1);
}
if (pid > 0) {
int status;
if (waitpid_safely(pid, &status) == 0) {
CHECK(WIFEXITED(status) && WEXITSTATUS(status) == 0,
"boundary (c) unpriv setreuid(u32::MAX-1, -1) -> -1 EPERM");
}
}
}

int boundary_run(void)
{
printf("\n----- boundary -----\n");
boundary_raw_u32max_nochg();
boundary_root_extreme_values();
boundary_unpriv_extreme_eperm();
printf(" ----- boundary: %d pass, %d fail -----\n", __pass, __fail);
return __fail;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/* core_dump_inhibit.c — setreuid 改 euid → dumpable=0 (man N2).
*
* man 2 setreuid (引用自 setuid 共享 NOTES 隐含):
* "If euid is different from the old effective UID, the process will be
* forbidden from leaving core dumps."
*
* 3 case (a-c).
*/

#define _GNU_SOURCE
#include "test_framework.h"

#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

static void cd_baseline(void)
{
/* 测什么/怎么测/期望/为什么: dumpable 启动 1, prctl(PR_GET_DUMPABLE) 返 1. */
int rc = prctl(PR_GET_DUMPABLE);
if (rc < 0 && errno == EINVAL) {
printf(" KNOWN-STARRY-LIMITATION | core_dump (a) PR_GET_DUMPABLE 不支持\n");
return;
}
CHECK(rc == 1, "core_dump (a) baseline dumpable == 1");
}

/* (b) setreuid 不改 euid → dumpable 不变 */
static void cd_setreuid_no_euid_change(void)
{
/* 测什么: setreuid(-1,-1) NOCHG 不动 euid → dumpable 仍 1.
* 怎么测: fork → child setreuid(-1,-1) → 验 dumpable.
* 期望: 1. */
pid_t pid = fork();
if (pid == 0) {
if (setreuid((uid_t)-1, (uid_t)-1) != 0) _exit(99);
int rc = prctl(PR_GET_DUMPABLE);
if (rc < 0 && errno == EINVAL) _exit(20);
if (rc == 1) _exit(0);
_exit(1);
}
int status;
waitpid(pid, &status, 0);
int ec = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
if (ec == 0) CHECK(1, "core_dump (b) setreuid(-1,-1) NOCHG → dumpable 仍 1");
else if (ec == 20) printf(" KNOWN-STARRY-LIMITATION | core_dump (b) PR_GET_DUMPABLE 不支持\n");
else CHECK(0, "core_dump (b) failed");
}

/* (c) setreuid(1000, 2000) 改 euid → dumpable=0 */
static void cd_setreuid_changes_euid(void)
{
/* 测什么: setreuid 改 euid (0→2000) → dumpable=0.
* 怎么测: root fork → child setreuid(1000, 2000) → 验 dumpable.
* 期望: 0 (禁 core dump). */
if (getuid() != 0) { printf(" core_dump (c) skip\n"); return; }
pid_t pid = fork();
if (pid == 0) {
if (setreuid(1000, 2000) != 0) _exit(99);
int rc = prctl(PR_GET_DUMPABLE);
if (rc < 0 && errno == EINVAL) _exit(20);
if (rc == 0) _exit(0);
if (rc == 1) _exit(21);
_exit(1);
}
int status;
waitpid(pid, &status, 0);
int ec = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
if (ec == 0) CHECK(1, "core_dump (c) setreuid(1k,2k) 改 euid → dumpable=0");
else if (ec == 20) printf(" KNOWN-STARRY-LIMITATION | core_dump (c) PR_GET_DUMPABLE\n");
else if (ec == 21) printf(" KNOWN-STARRY-LIMITATION | core_dump (c) starry 未禁 dumpable\n");
else CHECK(0, "core_dump (c) failed");
}

int core_dump_inhibit_run(void)
{
printf("\n----- core_dump_inhibit (man N2) -----\n");
cd_baseline();
cd_setreuid_no_euid_change();
cd_setreuid_changes_euid();
printf(" ----- core_dump_inhibit: %d pass, %d fail -----\n", __pass, __fail);
return __fail;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/* errno_preservation.c — setreuid/setregid 成功路径 errno 不被误改 (Group C Round 8).
*
* man 2 setreuid §"RETURN VALUE":
* "On success, zero is returned. On error, -1 is returned, and errno is
* set to indicate the error."
*
* Linux 实测: 成功路径 errno 不动. 验 starry sys_setreuid/setregid 在
* success path 不误改 user errno (常见 starry vm_write/cred update 后误设
* errno 残值的 bug 类型).
*/

#define _GNU_SOURCE
#include "test_framework.h"

#include <errno.h>
#include <stdio.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>

static void setreuid_nochg_errno_preserved(void)
{
/* 测什么/怎么测/期望/为什么: setreuid(-1,-1) NOCHG 路径成功 + errno 不动. */
errno = 4242;
int rc = setreuid((uid_t)-1, (uid_t)-1);
int err = errno;
CHECK(rc == 0, "errno_preserve (a) setreuid(-1,-1) -> 0");
CHECK(err == 4242, "errno_preserve (a) setreuid(-1,-1) 不改 errno (still 4242)");
errno = 0;
}

static void setregid_nochg_errno_preserved(void)
{
errno = 5555;
int rc = setregid((gid_t)-1, (gid_t)-1);
int err = errno;
CHECK(rc == 0, "errno_preserve (b) setregid(-1,-1) -> 0");
CHECK(err == 5555, "errno_preserve (b) setregid(-1,-1) 不改 errno (still 5555)");
errno = 0;
}

static void setreuid_self_errno_preserved(void)
{
/* 测什么: setreuid(self, self) 实际写 cred 路径 errno 不动. */
uid_t u = getuid();
uid_t e = geteuid();
errno = 6666;
int rc = setreuid(u, e);
int err = errno;
CHECK(rc == 0, "errno_preserve (c) setreuid(self,self) -> 0");
CHECK(err == 6666, "errno_preserve (c) setreuid(self,self) 不改 errno (still 6666)");
errno = 0;
}

static void setregid_self_errno_preserved(void)
{
gid_t g = getgid();
gid_t e = getegid();
errno = 7777;
int rc = setregid(g, e);
int err = errno;
CHECK(rc == 0, "errno_preserve (d) setregid(self,self) -> 0");
CHECK(err == 7777, "errno_preserve (d) setregid(self,self) 不改 errno (still 7777)");
errno = 0;
}

static void raw_setreuid_success_errno_preserved(void)
{
/* 测什么: raw syscall 路径 errno 不动 (与 libc 路径分离测). */
errno = 1111;
long rc = syscall(SYS_setreuid, getuid(), geteuid());
int err = errno;
CHECK(rc == 0, "errno_preserve (e) raw setreuid(self,self) -> 0");
CHECK(err == 1111, "errno_preserve (e) raw setreuid(self,self) 不改 errno (still 1111)");
errno = 0;
}

int errno_preservation_run(void)
{
printf("\n----- errno_preservation (Round 8) -----\n");
setreuid_nochg_errno_preserved();
setregid_nochg_errno_preserved();
setreuid_self_errno_preserved();
setregid_self_errno_preserved();
raw_setreuid_success_errno_preserved();
printf(" ----- errno_preservation: %d pass, %d fail -----\n", __pass, __fail);
return __fail;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#define _GNU_SOURCE
#include "test_framework.h"

#include <stdio.h>
#include <unistd.h>

/*
* test-uid-gid-re-setters —— setreuid / setregid 地毯式全覆盖
*
* Group C of uid/gid 5 分组测例方案。
*
* man 2 setreuid §"DESCRIPTION":
* "setreuid() sets real and effective user IDs of the calling process.
* Supplying a value of -1 for either the real or effective user ID
* forces the system to leave that ID unchanged."
* "Unprivileged processes may only set the effective user ID to the real
* user ID, the effective user ID, or the saved set-user-ID."
* "Unprivileged users may only set the real user ID to the real user ID
* or the effective user ID."
*
* man §"saved set-user-ID auto-update":
* "If the real user ID is set (i.e., ruid is not -1) or the effective user
* ID is set to a value not equal to the previous real user ID, the saved
* set-user-ID will be set to the new effective user ID."
*
* 测试自包含 — root 与 unprivileged 双路径;设计核心是 saved-set-uid 的
* 自动更新规则(最容易被实现错过的语义)。
*/

int setreuid_run(void);
int setregid_run(void);
int saved_id_semantics_run(void);
int boundary_run(void);
int matrix_run(void);
int procfs_visibility_run(void);
int nptl_sync_run(void);
int core_dump_inhibit_run(void);
int errno_preservation_run(void);

int main(void)
{
TEST_START("uid/gid re-setters: setreuid + setregid 地毯式(9 模块: setreuid + setregid + saved_id_semantics + boundary + matrix + procfs_visibility + nptl_sync + core_dump_inhibit + errno_preservation)");

(void)__pass;
(void)__fail;

int total_fail = 0;
int rc;

#define COLLECT(label, call) do { \
rc = (call); \
if (rc < 0 || rc > 1000000) { \
printf(" %-22s : <suspect rc=%d, treat as 1 hard failure>\n", label, rc); \
rc = 1; \
} else { \
printf(" %-22s : %d fail\n", label, rc); \
} \
total_fail += rc; \
} while (0)

printf("================================================\n");

COLLECT("setreuid", setreuid_run());
COLLECT("setregid", setregid_run());
COLLECT("saved_id_semantics", saved_id_semantics_run());
COLLECT("boundary", boundary_run());
COLLECT("matrix(man-first)", matrix_run());
COLLECT("procfs_visibility", procfs_visibility_run());
COLLECT("nptl_sync(V1)", nptl_sync_run());
COLLECT("core_dump_inhibit(N2)", core_dump_inhibit_run());
COLLECT("errno_preservation", errno_preservation_run());

#undef COLLECT

printf(" -------------------------------------------\n");
printf(" TOTAL: %d fail | RESULT: %s\n",
total_fail, total_fail == 0 ? "ALL PASS" : "HAS FAILURES");
printf("================================================\n\n");

return total_fail > 0 ? 1 : 0;
}
Loading