Skip to content

test(syscall): test-uid-gid-re-setters 915 PASS#727

Merged
ZR233 merged 1 commit into
rcore-os:devfrom
Lfan-ke:test-uid-gid-re-setters
May 22, 2026
Merged

test(syscall): test-uid-gid-re-setters 915 PASS#727
ZR233 merged 1 commit into
rcore-os:devfrom
Lfan-ke:test-uid-gid-re-setters

Conversation

@Lfan-ke

@Lfan-ke Lfan-ke commented May 17, 2026

Copy link
Copy Markdown
Contributor

test(syscall): test-uid-gid-re-setters 915 PASS

分支用途

Group C: setreuid / setregid 两参 (real + effective) setter syscall 的 man-first 地毯式测试, 本地 915 PASS。对应 #727

测试覆盖 (每文件每函数)

测试位置: test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-re-setters/c/src/

模块总览 (9 模块 / 40 个 static void case 函数 / main.c 串联): setreuid / setregid / saved_id_semantics / boundary / matrix / procfs_visibility / nptl_sync / core_dump_inhibit / errno_preservation

3.1 c/src/setreuid.c (7 case + 1 helper)

  • waitpid_safely() helper
  • setreuid_both_nochg_idempotent() — setreuid((uid_t)-1,(uid_t)-1) 不改 r/e/s 三字段 (man §DESCRIPTION sentinel -1)
  • setreuid_ruid_self() — setreuid(self,-1) 后 real == 原 real
  • setreuid_euid_self() — setreuid(-1,self) 后 effective == 原 effective
  • setreuid_root_arbitrary() — root setreuid(r,e) 任意值成功
  • setreuid_unpriv_eperm() — 非特权越权 (非 {ruid,euid,suid}) → EPERM (man §ERRORS EPERM)
  • setreuid_raw_matches_libc() — 裸 syscall 与 libc 一致 (rc + 三字段)
  • setreuid_keeps_supp_groups()Linux invariant (man 未显式但 kernel 行为强制): root → setgroups([7,8,9]) → setreuid(1000,1000) drop → getgroups 应仍 [7,8,9]; host gcc 实测 PASS, 防 starry sys_setreuid 错误清 cred.group_info 回归

3.2 c/src/setregid.c (7 case + 1 helper)

  • waitpid_safely() helper
  • setregid_both_nochg() / setregid_rgid_self() / setregid_egid_self() / setregid_root_arbitrary() / setregid_unpriv_eperm() / setregid_raw_matches_libc() / setregid_keeps_supp_groups() — 镜像 setreuid 测试 (含 (g) supp groups invariant)

3.3 c/src/saved_id_semantics.c (6 case + 1 helper)

  • waitpid_safely() helper
  • saved_id_ruid_set_updates_suid() — ruid != -1 → suid := new euid (D5 规则触发) (man §DESCRIPTION)
  • saved_id_euid_set_to_nonruid_updates_suid() — euid 改成 ≠ 旧 ruid → suid 更新
  • saved_id_both_set_root() — root 同时改 r 和 e, suid 更新到新 euid
  • saved_id_both_nochg_no_change() — (-1,-1) → r/e/s 全无变化
  • saved_id_d5_reverse_corner() — D5 反向 corner: euid := 旧 ruid 时 saved 不应被更新
  • saved_id_d5_reverse_corner_gid() — D5 corner GID 侧

3.4 c/src/boundary.c (3 case + 1 helper)

  • waitpid_safely() helper
  • boundary_raw_u32max_nochg() — 裸 syscall 传 (uid_t)-1 sentinel 走 nochg 路径
  • boundary_root_extreme_values() — root 极值 0/1000/65535/65536/ (u32max-1) (man §HISTORY 16→32-bit)
  • boundary_unpriv_extreme_eperm() — 非特权传极值 → EPERM

3.5 c/src/matrix.c (1 case + 4 helper)

  • setup_state() / read_uid_cred() / read_gid_cred() / waitpid_safely() helper
  • matrix_one() — (syscall × state × ruid × euid × mode) 笛卡尔单格 — 验 rc/errno + 后续 r/e/s 三槽 + D5 saved 规则

3.6 c/src/procfs_visibility.c (5 case + 2 helper)

  • waitpid_safely_pv() / parse_proc_id_line() helper
  • proc_uid_after_setreuid_full() — setreuid(r,e) 全改 → /proc Uid: 三字段一致 (proc(5) §Uid + man §DESCRIPTION)
  • proc_uid_after_setreuid_nochg_r() — setreuid(-1,e) → procfs real 不变、e 同步、s 按 D5
  • proc_uid_after_setreuid_nochg_same() — setreuid(r,r) same-value → procfs 三字段一致
  • proc_gid_after_setregid_full() — setregid 全改 → /proc Gid: 行同步
  • proc_compound_setre() — 组合调用 (setreuid 再 setregid) 后 procfs 三字段全一致

3.7 c/src/nptl_sync.c (3 case)

  • nptl_setreuid_main_to_pthread() — 主线程 setreuid → 子 pthread getresuid 看到相同 cred (man §VERSIONS C library/kernel differences)
  • nptl_setregid_pthread_to_main()
  • nptl_concurrent_setreuid()

3.8 c/src/core_dump_inhibit.c (3 case)

  • cd_baseline() — 未改动时 PR_GET_DUMPABLE == 1 (man §NOTES)
  • cd_setreuid_no_euid_change() — euid 未变 → dumpable 不变
  • cd_setreuid_changes_euid() — euid 变化 → PR_GET_DUMPABLE → 0

3.9 c/src/errno_preservation.c (5 case)

  • setreuid_nochg_errno_preserved() / setregid_nochg_errno_preserved() / setreuid_self_errno_preserved() / setregid_self_errno_preserved() / raw_setreuid_success_errno_preserved()

3.10 c/src/main.c (聚合 entry point, 非测点)

按顺序 COLLECT 9 个模块, 每模块 *_run()__fail, main 汇总 TOTAL: N fail。

相关 syscall man 摘录 (核心段)

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."
  • "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." (D5 规则)

setreuid §ERRORS:

  • EINVAL — One or more of the target user or group IDs is not valid in this user namespace.
  • EPERM — The calling process is not privileged and a change other than (i) swapping effective with real, (ii) setting one to the value of the other, (iii) setting effective to saved set-user-ID was specified.

setreuid §HISTORY: glibc setreuid/setregid wrapper transparently deals with 16-bit/32-bit variations across kernel versions。

发现的 bug (本测试过程中暴露的 starry vs Linux 差异)

本 test PR 在 starry kernel 上跑时, 暴露以下 starry-vs-Linux 行为差异, 已各自登记为上游 issue, 单文件 C 复现程序在 fork repo 的 bug-starry-* 分支内单独维护 (路径 test-suit/.../bugfix/bug-starry-<name>/c/src/main.c):

上述 bug 由 #717 (fix-uid-gid-bugs, local 修) 与 #718 (fix-uid-gid-deep, cross-subsystem) 这两个 fix PR 分别托管 — fix PR 同步把 bug-starry-* 注册进 4 arch bugfix toml 矩阵, CI 由 FAIL → PASS 转化作为修复验收。

CI / 验证

#727 当前 bucket: pass=18 fail=0 skipping=30

Matrix case 数学

matrix.c — 1536 case → 缩减到 864 (aarch64 timeout)

原公式: 8 ruid × 8 euid × 6 state × 2 mode × 2 syscall = 1536 缩减后: 6 ruid × 6 euid × 6 state × 2 mode × 2 syscall = 864 (commit 5248f83fe)

  • ruid 6: 0 / 1000 / 2000 / 65535 / NOCHG / boundary
  • euid 6: 同 ruid (枚举全笛卡尔积)
  • state 6: root_unmod / 3id_distinct / unpriv / mid_priv / after_setuid / NOCHG_baseline
  • mode 2: libc / raw
  • syscall 2: setreuid / setregid

为什么 (原) 这么多: man D5 saved 规则的真假组合依赖 (ruid, euid, old.ruid) 三元组关系 → 必须全笛卡尔积才能覆盖; ruid × euid 笛卡尔积是验 D5 规则的最小覆盖单元; × 6 state: EPERM 决策依赖 old cred 起点。

为什么缩减: emulated aarch64 上 fork-heavy 矩阵 1536 case 接近 5 min, 大概率 CI 超时 (timeout=600s)。缩减 8→6 保留关键 boundary (0/NOCHG/16-bit max), 减少笛卡尔积规模到 ~56%。边界覆盖仍完整。

每 case 4-5 assertion ≈ 3500-4500 PASS (864 base)。

复现命令

切换分支:

cd <repo-root>
git checkout test-uid-gid-re-setters

本地 Linux (host, WSL2):

cd <repo-root>/test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-re-setters/c
rm -rf build && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug && make
sudo ./test-uid-gid-re-setters

starry qemu (4 arch):

source .starry-env.sh && cargo starry test qemu --arch x86_64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch aarch64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch riscv64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch loongarch64 -c syscall

引用

Signed-off-by: Leo Cheng chengkelfan@qq.com

@Lfan-ke
Lfan-ke requested a review from ZCShou as a code owner May 17, 2026 23:52
Lfan-ke added a commit to Lfan-ke/tgoskits that referenced this pull request May 18, 2026
## 分支用途

Group B: `setuid` / `setgid` 两个直接 setter syscall 的 man-first 地毯式
测试, 本地 449 PASS。对应 rcore-os#726。

## 测试覆盖 (每文件每函数)

测试位置: `test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-direct-setters/c/src/`

模块总览 (10 模块 / 46 个 static void case 函数 / main.c 串联):
`setuid` / `setgid` / `cross_root_unprivileged` / `boundary` / `matrix` /
`procfs_visibility` / `nptl_sync` / `core_dump_inhibit` / `uid32_no_trunc` /
`errno_preservation`。

### 3.1 c/src/setuid.c (10 case + 1 helper)

- `waitpid_safely()` helper — 循环 waitpid 处理 EINTR
- `setuid_idempotent_to_self()` — setuid(geteuid()) 后三字段 (r,e,s) 不变
  (man §DESCRIPTION POSIX_SAVED_IDS)
- `setuid_root_sets_all_three()` — root setuid(target) → r/e/s 全被设为
  target (man §DESCRIPTION root path)
- `setuid_returns_zero_on_success()` — 成功 rc == 0 (不只是 ≥0)
- `setuid_raw_syscall_matches_libc()` — 裸 syscall 与 libc 行为一致
- `setuid_unprivileged_eperm_in_child()` — 非特权 setuid 在 fork 子 →
  EPERM (man §ERRORS EPERM)
- `setuid_raw_vs_libc_failed_path()` — EPERM 失败路径下 raw rc/errno 与
  libc 严格相同
- `setuid_irreversibility()` — root setuid(non-zero) drop 后无法
  setuid(0) 回去 (man §DESCRIPTION 不可逆)
- `setuid_fork_child_independent()` — fork 子 setuid 改动不污染父 cred
- `setuid_self_euid_no_flush()` — 非特权 setuid(euid) 不清除 saved-set-uid
- `setuid_einval_uid_minus_one()` — uid=(uid_t)-1 在非特权下 → EPERM/
  EINVAL (软容忍 starry bug B)

### 3.2 c/src/setgid.c (10 case + 1 helper)

- `waitpid_safely()` helper
- `setgid_idempotent_to_self()` — setgid(getegid()) 三字段不变
- `setgid_root_sets_all_three()` — root setgid(target) → r/e/s GID 全置
- `setgid_returns_zero_on_success()`
- `setgid_raw_syscall_matches_libc()`
- `setgid_unprivileged_eperm_in_child()` — 非特权 setgid(任意) → EPERM
- `setgid_raw_vs_libc_failed_path()`
- `setgid_alone_not_irreversible_caps_intact()` — 仅 setgid drop 不丢
  CAP_SETUID, 仍可 setuid
- `setgid_irreversible_after_setuid_drop()` — setuid drop 后 setgid 不可
  逆 (CAP_SETGID 丢)
- `setgid_fork_child_independent()`
- `setgid_self_egid_no_change()` — setgid(egid) 不动 saved-set-gid

### 3.3 c/src/cross_root_unprivileged.c (3 case + 1 helper)

- `waitpid_safely()` helper
- `root_setuid_arbitrary_updates_all()` — root setuid(任意值) → r/e/s
  全跟
- `unprivileged_setuid_self_succeeds()` — 非特权 setuid(self) 成功且字段
  不变
- `unprivileged_setuid_arbitrary_eperm()` — 非特权 setuid(非 {r,e,s}) →
  EPERM

### 3.4 c/src/boundary.c (3 case)

- `setuid_boundary_values_root()` — root 路径下边界 uid:
  0/1/65535/65536/0x7FFFFFFE 全 OK
- `setuid_u32max_sentinel_unprivileged()` — 非特权 setuid((uid_t)-1)
  sentinel → EPERM/EINVAL (man §ERRORS EINVAL)
- `setuid_raw_syscall_returns_negative_errno()` — 裸 syscall 失败时 rc
  为 `-EPERM` (不是 -1+errno)

### 3.5 c/src/matrix.c (1 case + 4 helper)

- `read_uid_cred()` / `read_gid_cred()` / `setup_state()` /
  `waitpid_safely()` helper
- `matrix_one()` — (syscall × state × input × mode) 笛卡尔单格 — 验
  rc/errno + getresuid 后续三槽

### 3.6 c/src/procfs_visibility.c (5 case + 2 helper)

- `waitpid_safely_pv()` / `parse_proc_id_line()` helper
- `procfs_uid_baseline_root()` — 未改动时 /proc Uid: == (uid,euid,suid)
  一致 (proc(5) §Uid)
- `procfs_uid_after_setuid_root()` — root setuid 后 procfs 三字段同步
- `procfs_uid_after_setuid_drop()` — drop 后 procfs Uid: 三字段一致更新
- `procfs_gid_after_setgid()` — setgid 后 procfs Gid: 行同步
- `procfs_uid_after_setresuid()` — setresuid 后 procfs 三字段精确同步

### 3.7 c/src/nptl_sync.c (3 case)

- `nptl_setuid_main_visible_to_pthread()` — 主线程 setuid → 子 pthread
  getresuid 看到相同 cred (man §VERSIONS C library/kernel differences)
- `nptl_setgid_pthread_visible_to_main()`
- `nptl_concurrent_setuid_converges()`

### 3.8 c/src/core_dump_inhibit.c (3 case + 1 helper)

- `waitpid_safely_cd()` helper
- `core_dump_baseline()` — 未改动时 PR_GET_DUMPABLE == 1 (man §NOTES)
- `core_dump_setuid_no_euid_change()` — setuid(euid) euid 未变 →
  dumpable 不变
- `core_dump_setuid_changes_euid()` — setuid 改变 euid → PR_GET_DUMPABLE
  → 0 (禁 core)

### 3.9 c/src/uid32_no_trunc.c (3 case + 1 helper)

- `waitpid_safely()` helper
- `setuid_32bit_no_truncate()` — child setuid 大 UID (>16-bit) 后
  getresuid 不被截 (man §HISTORY 16→32-bit)
- `setgid_32bit_no_truncate()`
- `setuid_32bit_via_raw_syscall()`

### 3.10 c/src/errno_preservation.c (5 case)

- `setuid_self_errno_preserved()` / `setgid_self_errno_preserved()` /
  `setuid_root_errno_preserved()` / `setgid_root_errno_preserved()` /
  `raw_setuid_success_errno_preserved()` — 成功路径不动预置 errno

### 3.11 c/src/main.c (聚合 entry point, 非测点)

按顺序 COLLECT 10 个模块, 每模块 `*_run()` 返 `__fail`, main 汇总
TOTAL: N fail 收尾。

## 相关 syscall man 摘录 (核心段)

`setuid` §DESCRIPTION: "setuid() sets the effective user ID of the
calling process. If the calling process is privileged (more precisely:
if the process has the CAP_SETUID capability in its user namespace),
the real UID and saved set-user-ID are also set."

`setuid` §ERRORS:
- EAGAIN — temporary failure allocating kernel data structures
- EINVAL — The user ID specified in uid is not valid in this user
  namespace
- EPERM — The user is not privileged and uid does not match the real
  UID or saved set-user-ID of the calling process

`setuid` §NOTES: "If uid is different from the old effective UID, the
process will be forbidden from leaving core dumps."

`setuid` §VERSIONS (C library/kernel differences): NPTL wrapper 使用
signal-based 技术保证 process-wide thread cred 同步。

`setgid` §ERRORS: EINVAL (gid 不合法) / EPERM (无 CAP_SETGID 且 gid 不
匹配 real GID 或 saved set-group-ID)。

## 发现的 bug (本测试过程中暴露的 starry vs Linux 差异)

本 test PR 在 starry kernel 上跑时, 暴露以下 starry-vs-Linux 行为差异,
已各自登记为上游 issue, 单文件 C 复现程序在 fork repo 的 `bug-starry-*`
分支内单独维护 (路径 `test-suit/.../bugfix/bug-starry-<name>/c/src/main.c`):

- rcore-os#713 — `setuid((uid_t)-1)` 在 starry 上被错误接受
  (Linux 应返 EINVAL, 由 uid_valid 检查阻断)
- rcore-os#714 — `/proc/self/status` 在 starry loongarch64 上
  vm_write EFAULT (Uid:/Gid:/Groups: 三行均受影响)
- rcore-os#715 — `setgroups(N, list)` 之后 `/proc/self/status`
  的 Groups: 行不立即同步 (starry race)
- rcore-os#716 — apk + curl 组合在 starry loongarch64 上
  600s 超时 ~90% flake (与本测试矩阵 stress 路径相关)

上述 bug 由 rcore-os#717 (fix-uid-gid-bugs, local 修) 与
rcore-os#718 (fix-uid-gid-deep, cross-subsystem) 这两个 fix PR
分别托管 — fix PR 同步把 bug-starry-* 注册进 4 arch bugfix toml 矩阵,
CI 由 FAIL → PASS 转化作为修复验收。

## CI / 验证

rcore-os#726 当前 bucket: `pass=18 fail=0 skipping=30`。

## Matrix case 数学

### `matrix.c` — 448 base case (×3-5 assertion ≈ 1500-2000 PASS)

公式: `2 syscall × 7 state × 16 input × 2 mode = 448`

- syscall 2: `setuid` / `setgid`
- state 7: `root_unmod` / `after_setresuid(0,1000,2000)` /
  `after_setresuid(0,0,1000)` / `unpriv(1000)` /
  `unpriv_3id(1000,2000,3000)` / `after_setgid(1000)` / `mixed_uid_gid`
- input 16: 0 / 1 / 100 / 999 / 1000 / 2000 / 3000 / 65535 / 65536 /
  100001 / 0x7FFFFFFE / NOCHG((uid_t)-1) / current_uid / current_euid /
  current_suid / boundary
- mode 2: libc / raw

为什么这么多维度:
- input 16 值: man §ERRORS EINVAL 触发只在 `(uid_t)-1`; EPERM 触发依赖
  input 是否在 {uid, euid, suid}; root 接受任意值 — 16 值覆盖 small /
  large / boundary (16-bit / 32-bit) / sentinel / self-set / cross-set
- state 7: 同一 `setuid(input)` 在不同 cred 起点上行为不同 — 覆盖 man
  §EPERM 决策树各分支
- mode 2: libc/raw 分离测验 ABI 一致性

每 case 3-5 assertion: rc / errno / 后续 getresuid r/e/s 三槽各验 /
fsuid procfs 检查 → 单 case 平均 4 assert → 总 ~1800 PASS。

## 复现命令

切换分支:
```bash
cd <repo-root>
git checkout test-uid-gid-direct-setters
```

本地 Linux (host, WSL2):
```bash
cd <repo-root>/test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-direct-setters/c
rm -rf build && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug && make
sudo ./test-uid-gid-direct-setters
```

starry qemu (4 arch):
```bash
source .starry-env.sh && cargo starry test qemu --arch x86_64      -c syscall
source .starry-env.sh && cargo starry test qemu --arch aarch64     -c syscall
source .starry-env.sh && cargo starry test qemu --arch riscv64     -c syscall
source .starry-env.sh && cargo starry test qemu --arch loongarch64 -c syscall
```

## 引用

- 相关 PR: stacked 前 Group A (rcore-os#725); stacked 后 Group C/D/E
  (rcore-os#727..rcore-os#729); 衍生 bug-* rcore-os#713 (setuid/setgid no uid_valid),
  fix rcore-os#717 (fix-uid-gid-bugs) 修复该 bug

Signed-off-by: Leo Cheng <chengkelfanke@gmail.com>
Lfan-ke added a commit to Lfan-ke/tgoskits that referenced this pull request May 18, 2026
## 分支用途

Group C: `setreuid` / `setregid` 两参 (real + effective) setter syscall
的 man-first 地毯式测试, 本地 1562 PASS。对应 rcore-os#727。

## 测试覆盖 (每文件每函数)

测试位置: `test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-re-setters/c/src/`

模块总览 (9 模块 / 38 个 static void case 函数 / main.c 串联):
`setreuid` / `setregid` / `saved_id_semantics` / `boundary` / `matrix` /
`procfs_visibility` / `nptl_sync` / `core_dump_inhibit` /
`errno_preservation`。

### 3.1 c/src/setreuid.c (6 case + 1 helper)

- `waitpid_safely()` helper
- `setreuid_both_nochg_idempotent()` — setreuid((uid_t)-1,(uid_t)-1) 不改
  r/e/s 三字段 (man §DESCRIPTION sentinel -1)
- `setreuid_ruid_self()` — setreuid(self,-1) 后 real == 原 real
- `setreuid_euid_self()` — setreuid(-1,self) 后 effective == 原 effective
- `setreuid_root_arbitrary()` — root setreuid(r,e) 任意值成功
- `setreuid_unpriv_eperm()` — 非特权越权 (非 {ruid,euid,suid}) → EPERM
  (man §ERRORS EPERM)
- `setreuid_raw_matches_libc()` — 裸 syscall 与 libc 一致 (rc + 三字段)

### 3.2 c/src/setregid.c (6 case + 1 helper)

- `waitpid_safely()` helper
- `setregid_both_nochg()` / `setregid_rgid_self()` / `setregid_egid_self()`
  / `setregid_root_arbitrary()` / `setregid_unpriv_eperm()` /
  `setregid_raw_matches_libc()` — 镜像 setreuid 测试

### 3.3 c/src/saved_id_semantics.c (6 case + 1 helper)

- `waitpid_safely()` helper
- `saved_id_ruid_set_updates_suid()` — ruid != -1 → suid := new euid
  (D5 规则触发) (man §DESCRIPTION)
- `saved_id_euid_set_to_nonruid_updates_suid()` — euid 改成 ≠ 旧 ruid →
  suid 更新
- `saved_id_both_set_root()` — root 同时改 r 和 e, suid 更新到新 euid
- `saved_id_both_nochg_no_change()` — (-1,-1) → r/e/s 全无变化
- `saved_id_d5_reverse_corner()` — D5 反向 corner: euid := 旧 ruid 时
  saved 不应被更新
- `saved_id_d5_reverse_corner_gid()` — D5 corner GID 侧

### 3.4 c/src/boundary.c (3 case + 1 helper)

- `waitpid_safely()` helper
- `boundary_raw_u32max_nochg()` — 裸 syscall 传 (uid_t)-1 sentinel 走
  nochg 路径
- `boundary_root_extreme_values()` — root 极值 0/1000/65535/65536/
  (u32max-1) (man §HISTORY 16→32-bit)
- `boundary_unpriv_extreme_eperm()` — 非特权传极值 → EPERM

### 3.5 c/src/matrix.c (1 case + 4 helper)

- `setup_state()` / `read_uid_cred()` / `read_gid_cred()` /
  `waitpid_safely()` helper
- `matrix_one()` — (syscall × state × ruid × euid × mode) 笛卡尔单格 —
  验 rc/errno + 后续 r/e/s 三槽 + D5 saved 规则

### 3.6 c/src/procfs_visibility.c (5 case + 2 helper)

- `waitpid_safely_pv()` / `parse_proc_id_line()` helper
- `proc_uid_after_setreuid_full()` — setreuid(r,e) 全改 → /proc Uid:
  三字段一致 (proc(5) §Uid + man §DESCRIPTION)
- `proc_uid_after_setreuid_nochg_r()` — setreuid(-1,e) → procfs real
  不变、e 同步、s 按 D5
- `proc_uid_after_setreuid_nochg_same()` — setreuid(r,r) same-value →
  procfs 三字段一致
- `proc_gid_after_setregid_full()` — setregid 全改 → /proc Gid: 行同步
- `proc_compound_setre()` — 组合调用 (setreuid 再 setregid) 后 procfs
  三字段全一致

### 3.7 c/src/nptl_sync.c (3 case)

- `nptl_setreuid_main_to_pthread()` — 主线程 setreuid → 子 pthread
  getresuid 看到相同 cred (man §VERSIONS C library/kernel differences)
- `nptl_setregid_pthread_to_main()`
- `nptl_concurrent_setreuid()`

### 3.8 c/src/core_dump_inhibit.c (3 case)

- `cd_baseline()` — 未改动时 PR_GET_DUMPABLE == 1 (man §NOTES)
- `cd_setreuid_no_euid_change()` — euid 未变 → dumpable 不变
- `cd_setreuid_changes_euid()` — euid 变化 → PR_GET_DUMPABLE → 0

### 3.9 c/src/errno_preservation.c (5 case)

- `setreuid_nochg_errno_preserved()` / `setregid_nochg_errno_preserved()`
  / `setreuid_self_errno_preserved()` /
  `setregid_self_errno_preserved()` /
  `raw_setreuid_success_errno_preserved()`

### 3.10 c/src/main.c (聚合 entry point, 非测点)

按顺序 COLLECT 9 个模块, 每模块 `*_run()` 返 `__fail`, main 汇总
TOTAL: N fail。

## 相关 syscall man 摘录 (核心段)

`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."
- "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." (D5 规则)

`setreuid` §ERRORS:
- EINVAL — One or more of the target user or group IDs is not valid in
  this user namespace.
- EPERM — The calling process is not privileged and a change other than
  (i) swapping effective with real, (ii) setting one to the value of the
  other, (iii) setting effective to saved set-user-ID was specified.

`setreuid` §HISTORY: glibc setreuid/setregid wrapper transparently deals
with 16-bit/32-bit variations across kernel versions。

## 发现的 bug (本测试过程中暴露的 starry vs Linux 差异)

本 test PR 在 starry kernel 上跑时, 暴露以下 starry-vs-Linux 行为差异,
已各自登记为上游 issue, 单文件 C 复现程序在 fork repo 的 `bug-starry-*`
分支内单独维护 (路径 `test-suit/.../bugfix/bug-starry-<name>/c/src/main.c`):

- rcore-os#713 — `setuid((uid_t)-1)` 在 starry 上被错误接受
  (Linux 应返 EINVAL, 由 uid_valid 检查阻断)
- rcore-os#714 — `/proc/self/status` 在 starry loongarch64 上
  vm_write EFAULT (Uid:/Gid:/Groups: 三行均受影响)
- rcore-os#715 — `setgroups(N, list)` 之后 `/proc/self/status`
  的 Groups: 行不立即同步 (starry race)
- rcore-os#716 — apk + curl 组合在 starry loongarch64 上
  600s 超时 ~90% flake (与本测试矩阵 stress 路径相关)

上述 bug 由 rcore-os#717 (fix-uid-gid-bugs, local 修) 与
rcore-os#718 (fix-uid-gid-deep, cross-subsystem) 这两个 fix PR
分别托管 — fix PR 同步把 bug-starry-* 注册进 4 arch bugfix toml 矩阵,
CI 由 FAIL → PASS 转化作为修复验收。

## CI / 验证

rcore-os#727 当前 bucket: `pass=18 fail=0 skipping=30`。

## Matrix case 数学

### `matrix.c` — 1536 case → 缩减到 864 (aarch64 timeout)

原公式: `8 ruid × 8 euid × 6 state × 2 mode × 2 syscall = 1536`
缩减后: `6 ruid × 6 euid × 6 state × 2 mode × 2 syscall = 864`
(commit `5248f83fe`)

- ruid 6: `0 / 1000 / 2000 / 65535 / NOCHG / boundary`
- euid 6: 同 ruid (枚举全笛卡尔积)
- state 6: `root_unmod / 3id_distinct / unpriv / mid_priv /
  after_setuid / NOCHG_baseline`
- mode 2: libc / raw
- syscall 2: setreuid / setregid

为什么 (原) 这么多: man D5 saved 规则的真假组合依赖 (ruid, euid, old.ruid)
三元组关系 → 必须全笛卡尔积才能覆盖; ruid × euid 笛卡尔积是验 D5 规则的
最小覆盖单元; × 6 state: EPERM 决策依赖 old cred 起点。

为什么缩减: emulated aarch64 上 fork-heavy 矩阵 1536 case 接近 5 min,
大概率 CI 超时 (timeout=600s)。缩减 8→6 保留关键 boundary
(0/NOCHG/16-bit max), 减少笛卡尔积规模到 ~56%。边界覆盖仍完整。

每 case 4-5 assertion ≈ 3500-4500 PASS (864 base)。

## 复现命令

切换分支:
```bash
cd <repo-root>
git checkout test-uid-gid-re-setters
```

本地 Linux (host, WSL2):
```bash
cd <repo-root>/test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-re-setters/c
rm -rf build && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug && make
sudo ./test-uid-gid-re-setters
```

starry qemu (4 arch):
```bash
source .starry-env.sh && cargo starry test qemu --arch x86_64      -c syscall
source .starry-env.sh && cargo starry test qemu --arch aarch64     -c syscall
source .starry-env.sh && cargo starry test qemu --arch riscv64     -c syscall
source .starry-env.sh && cargo starry test qemu --arch loongarch64 -c syscall
```

## 引用

- 相关 PR: stacked 前 Group A/B (rcore-os#725..rcore-os#726); stacked 后 Group D/E
  (rcore-os#728..rcore-os#729); 衍生 fix rcore-os#718 (fix-uid-gid-deep) PR_SET/GET_DUMPABLE
  支撑 core_dump_inhibit 模块

Signed-off-by: Leo Cheng <chengkelfanke@gmail.com>
@Lfan-ke
Lfan-ke force-pushed the test-uid-gid-re-setters branch from bafc0f6 to 29c20ad Compare May 18, 2026 00:03
Lfan-ke added a commit to Lfan-ke/tgoskits that referenced this pull request May 18, 2026
## 分支用途

Group D: `setresuid` / `setresgid` 三参 (real + effective + saved)
setter syscall 的 man-first 地毯式测试, 本地 3022 PASS。对应 rcore-os#728。

## 测试覆盖 (每文件每函数)

测试位置: `test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-res-setters/c/src/`

模块总览 (10 模块 / 43 个 static void case 函数 / main.c 串联):
`setresuid` / `setresgid` / `three_arg_independence` / `boundary` /
`fsuid_follow` / `procfs_visibility` / `nptl_sync` / `core_dump_inhibit` /
`matrix` / `errno_preservation`。

### 3.1 c/src/setresuid.c (6 case + 1 helper)

- `waitpid_safely()` helper
- `setresuid_all_nochg()` — setresuid(-1,-1,-1) 三字段不变
  (man §DESCRIPTION sentinel -1)
- `setresuid_self_self_self()` — setresuid(self,self,self) 三字段不变
- `setresuid_root_arbitrary_three_values()` — root setresuid(r,e,s) 三个
  任意不同值都成功 (man §DESCRIPTION root)
- `setresuid_unpriv_within_set_ok()` — 非特权改到当前 {r,e,s} 集合内 → OK
- `setresuid_unpriv_outside_set_eperm()` — 非特权超出 {r,e,s} 集合 → EPERM
  (man §ERRORS EPERM)
- `setresuid_raw_matches_libc()` — 裸 syscall 与 libc 一致

### 3.2 c/src/setresgid.c (6 case + 1 helper)

- `waitpid_safely()` helper
- `setresgid_all_nochg()` / `setresgid_self_self_self()` /
  `setresgid_root_arbitrary_three_values()` /
  `setresgid_unpriv_within_set_ok()` /
  `setresgid_unpriv_outside_set_eperm()` /
  `setresgid_raw_matches_libc()`

### 3.3 c/src/three_arg_independence.c (7 case + 2 helper)

- `waitpid_safely()` helper / `test_combo()` helper
- `independence_root_only_r()` — root setresuid(NEW,-1,-1) 只改 r, e/s
  保持 (man §DESCRIPTION 三参独立)
- `independence_root_only_s()` — root setresuid(-1,-1,NEW) 只改 s
- `independence_root_only_e()` — root setresuid(-1,NEW,-1) 只改 e
- `independence_all_three_distinct()` — r/e/s 三参传不同值 → 三槽各自
  独立赋值
- `independence_all_nochg_no_op()` — root (-1,-1,-1) 无操作不变
- `independence_all_nochg_no_op_gid()` — GID 侧 (-1,-1,-1) 无操作
- `independence_all_nochg_unpriv_ok()` — 非特权 (-1,-1,-1) 也 OK
  (不触发 EPERM)

### 3.4 c/src/boundary.c (3 case + 1 helper)

- `waitpid_safely()` helper
- `boundary_raw_all_u32max()` — 裸 syscall 三参全 (uid_t)-1 sentinel →
  nochg 路径
- `boundary_root_extreme_uids()` — root 极值 0/65535/65536/(u32max-1)
  (man §HISTORY 16→32-bit)
- `boundary_unpriv_one_outside_eperm()` — 非特权一个字段出集合 → EPERM

### 3.5 c/src/fsuid_follow.c (3 case + 1 helper)

- `waitpid_safely2()` helper
- `fsuid_follows_euid_after_setresuid()` — setresuid 改 euid → fsuid 自动
  跟随 (verify via setfsuid(-1) 读回) (man §DESCRIPTION fsuid follow euid)
- `fsgid_follows_egid_after_setresgid()` — setresgid 改 egid → fsgid 自动
  跟随
- `fsuid_unchanged_when_euid_unchanged()` — euid 未变 → fsuid 也不变

### 3.6 c/src/matrix.c (1 case + 4 helper)

- `setup_state()` / `read_uid()` / `read_gid()` / `waitpid_safely()`
  helper
- `matrix_one()` — (syscall × state × ruid × euid × suid × mode) 笛卡尔
  单格 — 验 rc/errno + 三槽 + fsuid 跟随

### 3.7 c/src/procfs_visibility.c (6 case + 2 helper)

- `waitpid_safely_pv()` / `parse_proc_id_line()` helper
- `proc_uid_baseline()` — 未改动时 /proc Uid: == (uid,euid,suid)
- `proc_uid_after_setresuid()` — setresuid 后 /proc Uid: 三字段精确同步
- `proc_uid_after_setresuid_nochg()` — setresuid(-1,-1,-1) 后 procfs 不变
- `proc_gid_after_setresgid()` — setresgid 后 /proc Gid: 同步
- `proc_compound_setres()` — 复合调用后 procfs 全一致
- `proc_gid_baseline()` — /proc Gid: 基线一致

### 3.8 c/src/nptl_sync.c (3 case)

- `nptl_setresuid_main_to_pthread()` — 主线程 setresuid → 子 pthread
  getresuid 看到相同 cred (man §VERSIONS C library/kernel differences)
- `nptl_setresgid_pthread_to_main()`
- `nptl_concurrent_setresuid()` — 并发 setresuid 最终收敛

### 3.9 c/src/core_dump_inhibit.c (3 case)

- `cd_baseline()` — PR_GET_DUMPABLE == 1 (man 2 prctl §PR_GET_DUMPABLE)
- `cd_setresuid_nochg()` — setresuid 三参 nochg → dumpable 不变
- `cd_setresuid_changes()` — setresuid 改 euid → PR_GET_DUMPABLE → 0

### 3.10 c/src/errno_preservation.c (5 case)

- `setresuid_nochg_errno_preserved()` /
  `setresgid_nochg_errno_preserved()` /
  `setresuid_self_errno_preserved()` /
  `setresgid_self_errno_preserved()` /
  `raw_setresuid_success_errno_preserved()`

### 3.11 c/src/main.c (聚合 entry point, 非测点)

按顺序 COLLECT 10 个模块, 每模块 `*_run()` 返 `__fail`, main 汇总
TOTAL: N fail。

## 相关 syscall man 摘录 (核心段)

`setresuid` §DESCRIPTION:
- "setresuid() sets the real user ID, the effective user ID, and the
  saved set-user-ID of the calling process."
- "An unprivileged process may change its real UID, effective UID, and
  saved set-user-ID, each to one of: the current real UID, the current
  effective UID, or the current saved set-user-ID."
- "A privileged process (on Linux, one having the CAP_SETUID capability)
  may set its real UID, effective UID, and saved set-user-ID to
  arbitrary values."
- "If one of the arguments equals -1, the corresponding value is not
  changed."
- "Regardless of what changes are made to the real UID, effective UID,
  and saved set-user-ID, the filesystem UID is always set to the same
  value as the (possibly new) effective UID."

`setresuid` §ERRORS:
- EINVAL — One or more of the target user or group IDs is not valid in
  this user namespace.
- EPERM — The calling process is not privileged and tried to change the
  IDs to values that are not permitted.

`setresuid` §HISTORY: Linux 2.1.44, glibc 2.3.2. HP-UX, FreeBSD. glibc
wrapper transparently deals with 16-bit/32-bit variations。

## 发现的 bug (本测试过程中暴露的 starry vs Linux 差异)

本 test PR 在 starry kernel 上跑时, 暴露以下 starry-vs-Linux 行为差异,
已各自登记为上游 issue, 单文件 C 复现程序在 fork repo 的 `bug-starry-*`
分支内单独维护 (路径 `test-suit/.../bugfix/bug-starry-<name>/c/src/main.c`):

- rcore-os#713 — `setuid((uid_t)-1)` 在 starry 上被错误接受
  (Linux 应返 EINVAL, 由 uid_valid 检查阻断)
- rcore-os#714 — `/proc/self/status` 在 starry loongarch64 上
  vm_write EFAULT (Uid:/Gid:/Groups: 三行均受影响)
- rcore-os#715 — `setgroups(N, list)` 之后 `/proc/self/status`
  的 Groups: 行不立即同步 (starry race)
- rcore-os#716 — apk + curl 组合在 starry loongarch64 上
  600s 超时 ~90% flake (与本测试矩阵 stress 路径相关)

上述 bug 由 rcore-os#717 (fix-uid-gid-bugs, local 修) 与
rcore-os#718 (fix-uid-gid-deep, cross-subsystem) 这两个 fix PR
分别托管 — fix PR 同步把 bug-starry-* 注册进 4 arch bugfix toml 矩阵,
CI 由 FAIL → PASS 转化作为修复验收。

## CI / 验证

rcore-os#728 当前 bucket: `pass=18 fail=0 skipping=30`。

## Matrix case 数学

### `matrix.c` — 3000 case (Group D 最大规模)

公式: `5 ruid × 5 euid × 5 suid × 6 state × 2 mode × 2 syscall = 3000`

- ruid 5: `0 / 1000 / 2000 / NOCHG / boundary`
- euid 5: 同 ruid
- suid 5: 同 ruid
- state 6: `root_unmod / 3id_distinct / unpriv / mid_priv /
  after_setresuid / NOCHG_baseline`
- mode 2: libc / raw
- syscall 2: setresuid / setresgid

为什么这么多维度:
- `setresuid(ruid, euid, suid)` 是三参数独立接口 — 与 setreuid (2 参 +
  隐式 saved 更新) 不同, setres* 每参各自独立, 必须 5^3=125 个三元组
  覆盖全部组合空间
- unpriv 每槽位 in_set 检查独立, 需 5^3 组合验
- fsuid 必须验每个 (ruid, euid, suid) 组合下都跟新 euid

5^3 不能再缩: 缩到 4^3=64 会漏掉 NOCHG/boundary corner; 5^3=125 是
最小覆盖。

每 case 4-5 assertion ≈ 12000-15000 PASS (含 fsuid procfs 验证)。

## 复现命令

切换分支:
```bash
cd <repo-root>
git checkout test-uid-gid-res-setters
```

本地 Linux (host, WSL2):
```bash
cd <repo-root>/test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-res-setters/c
rm -rf build && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug && make
sudo ./test-uid-gid-res-setters
```

starry qemu (4 arch):
```bash
source .starry-env.sh && cargo starry test qemu --arch x86_64      -c syscall
source .starry-env.sh && cargo starry test qemu --arch aarch64     -c syscall
source .starry-env.sh && cargo starry test qemu --arch riscv64     -c syscall
source .starry-env.sh && cargo starry test qemu --arch loongarch64 -c syscall
```

## 引用

- 相关 PR: stacked 前 Group A/B/C (rcore-os#725..rcore-os#727); stacked 后 Group E
  (rcore-os#729); fsuid_follow 模块依赖 fix rcore-os#717 (fix-uid-gid-bugs) 新增
  setfsuid/setfsgid 实现

Signed-off-by: Leo Cheng <chengkelfanke@gmail.com>
Lfan-ke added a commit to Lfan-ke/tgoskits that referenced this pull request May 18, 2026
## 分支用途

Group B: `setuid` / `setgid` 两个直接 setter syscall 的 man-first 地毯式
测试, 本地 449 PASS。对应 rcore-os#726。

## 测试覆盖 (每文件每函数)

测试位置: `test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-direct-setters/c/src/`

模块总览 (10 模块 / 46 个 static void case 函数 / main.c 串联):
`setuid` / `setgid` / `cross_root_unprivileged` / `boundary` / `matrix` /
`procfs_visibility` / `nptl_sync` / `core_dump_inhibit` / `uid32_no_trunc` /
`errno_preservation`。

### 3.1 c/src/setuid.c (10 case + 1 helper)

- `waitpid_safely()` helper — 循环 waitpid 处理 EINTR
- `setuid_idempotent_to_self()` — setuid(geteuid()) 后三字段 (r,e,s) 不变
  (man §DESCRIPTION POSIX_SAVED_IDS)
- `setuid_root_sets_all_three()` — root setuid(target) → r/e/s 全被设为
  target (man §DESCRIPTION root path)
- `setuid_returns_zero_on_success()` — 成功 rc == 0 (不只是 ≥0)
- `setuid_raw_syscall_matches_libc()` — 裸 syscall 与 libc 行为一致
- `setuid_unprivileged_eperm_in_child()` — 非特权 setuid 在 fork 子 →
  EPERM (man §ERRORS EPERM)
- `setuid_raw_vs_libc_failed_path()` — EPERM 失败路径下 raw rc/errno 与
  libc 严格相同
- `setuid_irreversibility()` — root setuid(non-zero) drop 后无法
  setuid(0) 回去 (man §DESCRIPTION 不可逆)
- `setuid_fork_child_independent()` — fork 子 setuid 改动不污染父 cred
- `setuid_self_euid_no_flush()` — 非特权 setuid(euid) 不清除 saved-set-uid
- `setuid_einval_uid_minus_one()` — uid=(uid_t)-1 在非特权下 → EPERM/
  EINVAL (软容忍 starry bug B)

### 3.2 c/src/setgid.c (10 case + 1 helper)

- `waitpid_safely()` helper
- `setgid_idempotent_to_self()` — setgid(getegid()) 三字段不变
- `setgid_root_sets_all_three()` — root setgid(target) → r/e/s GID 全置
- `setgid_returns_zero_on_success()`
- `setgid_raw_syscall_matches_libc()`
- `setgid_unprivileged_eperm_in_child()` — 非特权 setgid(任意) → EPERM
- `setgid_raw_vs_libc_failed_path()`
- `setgid_alone_not_irreversible_caps_intact()` — 仅 setgid drop 不丢
  CAP_SETUID, 仍可 setuid
- `setgid_irreversible_after_setuid_drop()` — setuid drop 后 setgid 不可
  逆 (CAP_SETGID 丢)
- `setgid_fork_child_independent()`
- `setgid_self_egid_no_change()` — setgid(egid) 不动 saved-set-gid

### 3.3 c/src/cross_root_unprivileged.c (3 case + 1 helper)

- `waitpid_safely()` helper
- `root_setuid_arbitrary_updates_all()` — root setuid(任意值) → r/e/s
  全跟
- `unprivileged_setuid_self_succeeds()` — 非特权 setuid(self) 成功且字段
  不变
- `unprivileged_setuid_arbitrary_eperm()` — 非特权 setuid(非 {r,e,s}) →
  EPERM

### 3.4 c/src/boundary.c (3 case)

- `setuid_boundary_values_root()` — root 路径下边界 uid:
  0/1/65535/65536/0x7FFFFFFE 全 OK
- `setuid_u32max_sentinel_unprivileged()` — 非特权 setuid((uid_t)-1)
  sentinel → EPERM/EINVAL (man §ERRORS EINVAL)
- `setuid_raw_syscall_returns_negative_errno()` — 裸 syscall 失败时 rc
  为 `-EPERM` (不是 -1+errno)

### 3.5 c/src/matrix.c (1 case + 4 helper)

- `read_uid_cred()` / `read_gid_cred()` / `setup_state()` /
  `waitpid_safely()` helper
- `matrix_one()` — (syscall × state × input × mode) 笛卡尔单格 — 验
  rc/errno + getresuid 后续三槽

### 3.6 c/src/procfs_visibility.c (5 case + 2 helper)

- `waitpid_safely_pv()` / `parse_proc_id_line()` helper
- `procfs_uid_baseline_root()` — 未改动时 /proc Uid: == (uid,euid,suid)
  一致 (proc(5) §Uid)
- `procfs_uid_after_setuid_root()` — root setuid 后 procfs 三字段同步
- `procfs_uid_after_setuid_drop()` — drop 后 procfs Uid: 三字段一致更新
- `procfs_gid_after_setgid()` — setgid 后 procfs Gid: 行同步
- `procfs_uid_after_setresuid()` — setresuid 后 procfs 三字段精确同步

### 3.7 c/src/nptl_sync.c (3 case)

- `nptl_setuid_main_visible_to_pthread()` — 主线程 setuid → 子 pthread
  getresuid 看到相同 cred (man §VERSIONS C library/kernel differences)
- `nptl_setgid_pthread_visible_to_main()`
- `nptl_concurrent_setuid_converges()`

### 3.8 c/src/core_dump_inhibit.c (3 case + 1 helper)

- `waitpid_safely_cd()` helper
- `core_dump_baseline()` — 未改动时 PR_GET_DUMPABLE == 1 (man §NOTES)
- `core_dump_setuid_no_euid_change()` — setuid(euid) euid 未变 →
  dumpable 不变
- `core_dump_setuid_changes_euid()` — setuid 改变 euid → PR_GET_DUMPABLE
  → 0 (禁 core)

### 3.9 c/src/uid32_no_trunc.c (3 case + 1 helper)

- `waitpid_safely()` helper
- `setuid_32bit_no_truncate()` — child setuid 大 UID (>16-bit) 后
  getresuid 不被截 (man §HISTORY 16→32-bit)
- `setgid_32bit_no_truncate()`
- `setuid_32bit_via_raw_syscall()`

### 3.10 c/src/errno_preservation.c (5 case)

- `setuid_self_errno_preserved()` / `setgid_self_errno_preserved()` /
  `setuid_root_errno_preserved()` / `setgid_root_errno_preserved()` /
  `raw_setuid_success_errno_preserved()` — 成功路径不动预置 errno

### 3.11 c/src/main.c (聚合 entry point, 非测点)

按顺序 COLLECT 10 个模块, 每模块 `*_run()` 返 `__fail`, main 汇总
TOTAL: N fail 收尾。

## 相关 syscall man 摘录 (核心段)

`setuid` §DESCRIPTION: "setuid() sets the effective user ID of the
calling process. If the calling process is privileged (more precisely:
if the process has the CAP_SETUID capability in its user namespace),
the real UID and saved set-user-ID are also set."

`setuid` §ERRORS:
- EAGAIN — temporary failure allocating kernel data structures
- EINVAL — The user ID specified in uid is not valid in this user
  namespace
- EPERM — The user is not privileged and uid does not match the real
  UID or saved set-user-ID of the calling process

`setuid` §NOTES: "If uid is different from the old effective UID, the
process will be forbidden from leaving core dumps."

`setuid` §VERSIONS (C library/kernel differences): NPTL wrapper 使用
signal-based 技术保证 process-wide thread cred 同步。

`setgid` §ERRORS: EINVAL (gid 不合法) / EPERM (无 CAP_SETGID 且 gid 不
匹配 real GID 或 saved set-group-ID)。

## 发现的 bug (本测试过程中暴露的 starry vs Linux 差异)

本 test PR 在 starry kernel 上跑时, 暴露以下 starry-vs-Linux 行为差异,
已各自登记为上游 issue, 单文件 C 复现程序在 fork repo 的 `bug-starry-*`
分支内单独维护 (路径 `test-suit/.../bugfix/bug-starry-<name>/c/src/main.c`):

- rcore-os#713 — `setuid((uid_t)-1)` 在 starry 上被错误接受
  (Linux 应返 EINVAL, 由 uid_valid 检查阻断)
- rcore-os#714 — `/proc/self/status` 在 starry loongarch64 上
  vm_write EFAULT (Uid:/Gid:/Groups: 三行均受影响)
- rcore-os#715 — `setgroups(N, list)` 之后 `/proc/self/status`
  的 Groups: 行不立即同步 (starry race)
- rcore-os#716 — apk + curl 组合在 starry loongarch64 上
  600s 超时 ~90% flake (与本测试矩阵 stress 路径相关)

上述 bug 由 rcore-os#717 (fix-uid-gid-bugs, local 修) 与
rcore-os#718 (fix-uid-gid-deep, cross-subsystem) 这两个 fix PR
分别托管 — fix PR 同步把 bug-starry-* 注册进 4 arch bugfix toml 矩阵,
CI 由 FAIL → PASS 转化作为修复验收。

## CI / 验证

rcore-os#726 当前 bucket: `pass=18 fail=0 skipping=30`。

## Matrix case 数学

### `matrix.c` — 448 base case (×3-5 assertion ≈ 1500-2000 PASS)

公式: `2 syscall × 7 state × 16 input × 2 mode = 448`

- syscall 2: `setuid` / `setgid`
- state 7: `root_unmod` / `after_setresuid(0,1000,2000)` /
  `after_setresuid(0,0,1000)` / `unpriv(1000)` /
  `unpriv_3id(1000,2000,3000)` / `after_setgid(1000)` / `mixed_uid_gid`
- input 16: 0 / 1 / 100 / 999 / 1000 / 2000 / 3000 / 65535 / 65536 /
  100001 / 0x7FFFFFFE / NOCHG((uid_t)-1) / current_uid / current_euid /
  current_suid / boundary
- mode 2: libc / raw

为什么这么多维度:
- input 16 值: man §ERRORS EINVAL 触发只在 `(uid_t)-1`; EPERM 触发依赖
  input 是否在 {uid, euid, suid}; root 接受任意值 — 16 值覆盖 small /
  large / boundary (16-bit / 32-bit) / sentinel / self-set / cross-set
- state 7: 同一 `setuid(input)` 在不同 cred 起点上行为不同 — 覆盖 man
  §EPERM 决策树各分支
- mode 2: libc/raw 分离测验 ABI 一致性

每 case 3-5 assertion: rc / errno / 后续 getresuid r/e/s 三槽各验 /
fsuid procfs 检查 → 单 case 平均 4 assert → 总 ~1800 PASS。

## 复现命令

切换分支:
```bash
cd <repo-root>
git checkout test-uid-gid-direct-setters
```

本地 Linux (host, WSL2):
```bash
cd <repo-root>/test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-direct-setters/c
rm -rf build && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug && make
sudo ./test-uid-gid-direct-setters
```

starry qemu (4 arch):
```bash
source .starry-env.sh && cargo starry test qemu --arch x86_64      -c syscall
source .starry-env.sh && cargo starry test qemu --arch aarch64     -c syscall
source .starry-env.sh && cargo starry test qemu --arch riscv64     -c syscall
source .starry-env.sh && cargo starry test qemu --arch loongarch64 -c syscall
```

## 引用

- 相关 PR: stacked 前 Group A (rcore-os#725); stacked 后 Group C/D/E
  (rcore-os#727..rcore-os#729); 衍生 bug-* rcore-os#713 (setuid/setgid no uid_valid),
  fix rcore-os#717 (fix-uid-gid-bugs) 修复该 bug

Signed-off-by: Leo Cheng <chengkelfanke@gmail.com>
Lfan-ke added a commit to Lfan-ke/tgoskits that referenced this pull request May 18, 2026
## 分支用途

Group C: `setreuid` / `setregid` 两参 (real + effective) setter syscall
的 man-first 地毯式测试, 本地 1562 PASS。对应 rcore-os#727。

## 测试覆盖 (每文件每函数)

测试位置: `test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-re-setters/c/src/`

模块总览 (9 模块 / 38 个 static void case 函数 / main.c 串联):
`setreuid` / `setregid` / `saved_id_semantics` / `boundary` / `matrix` /
`procfs_visibility` / `nptl_sync` / `core_dump_inhibit` /
`errno_preservation`。

### 3.1 c/src/setreuid.c (6 case + 1 helper)

- `waitpid_safely()` helper
- `setreuid_both_nochg_idempotent()` — setreuid((uid_t)-1,(uid_t)-1) 不改
  r/e/s 三字段 (man §DESCRIPTION sentinel -1)
- `setreuid_ruid_self()` — setreuid(self,-1) 后 real == 原 real
- `setreuid_euid_self()` — setreuid(-1,self) 后 effective == 原 effective
- `setreuid_root_arbitrary()` — root setreuid(r,e) 任意值成功
- `setreuid_unpriv_eperm()` — 非特权越权 (非 {ruid,euid,suid}) → EPERM
  (man §ERRORS EPERM)
- `setreuid_raw_matches_libc()` — 裸 syscall 与 libc 一致 (rc + 三字段)

### 3.2 c/src/setregid.c (6 case + 1 helper)

- `waitpid_safely()` helper
- `setregid_both_nochg()` / `setregid_rgid_self()` / `setregid_egid_self()`
  / `setregid_root_arbitrary()` / `setregid_unpriv_eperm()` /
  `setregid_raw_matches_libc()` — 镜像 setreuid 测试

### 3.3 c/src/saved_id_semantics.c (6 case + 1 helper)

- `waitpid_safely()` helper
- `saved_id_ruid_set_updates_suid()` — ruid != -1 → suid := new euid
  (D5 规则触发) (man §DESCRIPTION)
- `saved_id_euid_set_to_nonruid_updates_suid()` — euid 改成 ≠ 旧 ruid →
  suid 更新
- `saved_id_both_set_root()` — root 同时改 r 和 e, suid 更新到新 euid
- `saved_id_both_nochg_no_change()` — (-1,-1) → r/e/s 全无变化
- `saved_id_d5_reverse_corner()` — D5 反向 corner: euid := 旧 ruid 时
  saved 不应被更新
- `saved_id_d5_reverse_corner_gid()` — D5 corner GID 侧

### 3.4 c/src/boundary.c (3 case + 1 helper)

- `waitpid_safely()` helper
- `boundary_raw_u32max_nochg()` — 裸 syscall 传 (uid_t)-1 sentinel 走
  nochg 路径
- `boundary_root_extreme_values()` — root 极值 0/1000/65535/65536/
  (u32max-1) (man §HISTORY 16→32-bit)
- `boundary_unpriv_extreme_eperm()` — 非特权传极值 → EPERM

### 3.5 c/src/matrix.c (1 case + 4 helper)

- `setup_state()` / `read_uid_cred()` / `read_gid_cred()` /
  `waitpid_safely()` helper
- `matrix_one()` — (syscall × state × ruid × euid × mode) 笛卡尔单格 —
  验 rc/errno + 后续 r/e/s 三槽 + D5 saved 规则

### 3.6 c/src/procfs_visibility.c (5 case + 2 helper)

- `waitpid_safely_pv()` / `parse_proc_id_line()` helper
- `proc_uid_after_setreuid_full()` — setreuid(r,e) 全改 → /proc Uid:
  三字段一致 (proc(5) §Uid + man §DESCRIPTION)
- `proc_uid_after_setreuid_nochg_r()` — setreuid(-1,e) → procfs real
  不变、e 同步、s 按 D5
- `proc_uid_after_setreuid_nochg_same()` — setreuid(r,r) same-value →
  procfs 三字段一致
- `proc_gid_after_setregid_full()` — setregid 全改 → /proc Gid: 行同步
- `proc_compound_setre()` — 组合调用 (setreuid 再 setregid) 后 procfs
  三字段全一致

### 3.7 c/src/nptl_sync.c (3 case)

- `nptl_setreuid_main_to_pthread()` — 主线程 setreuid → 子 pthread
  getresuid 看到相同 cred (man §VERSIONS C library/kernel differences)
- `nptl_setregid_pthread_to_main()`
- `nptl_concurrent_setreuid()`

### 3.8 c/src/core_dump_inhibit.c (3 case)

- `cd_baseline()` — 未改动时 PR_GET_DUMPABLE == 1 (man §NOTES)
- `cd_setreuid_no_euid_change()` — euid 未变 → dumpable 不变
- `cd_setreuid_changes_euid()` — euid 变化 → PR_GET_DUMPABLE → 0

### 3.9 c/src/errno_preservation.c (5 case)

- `setreuid_nochg_errno_preserved()` / `setregid_nochg_errno_preserved()`
  / `setreuid_self_errno_preserved()` /
  `setregid_self_errno_preserved()` /
  `raw_setreuid_success_errno_preserved()`

### 3.10 c/src/main.c (聚合 entry point, 非测点)

按顺序 COLLECT 9 个模块, 每模块 `*_run()` 返 `__fail`, main 汇总
TOTAL: N fail。

## 相关 syscall man 摘录 (核心段)

`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."
- "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." (D5 规则)

`setreuid` §ERRORS:
- EINVAL — One or more of the target user or group IDs is not valid in
  this user namespace.
- EPERM — The calling process is not privileged and a change other than
  (i) swapping effective with real, (ii) setting one to the value of the
  other, (iii) setting effective to saved set-user-ID was specified.

`setreuid` §HISTORY: glibc setreuid/setregid wrapper transparently deals
with 16-bit/32-bit variations across kernel versions。

## 发现的 bug (本测试过程中暴露的 starry vs Linux 差异)

本 test PR 在 starry kernel 上跑时, 暴露以下 starry-vs-Linux 行为差异,
已各自登记为上游 issue, 单文件 C 复现程序在 fork repo 的 `bug-starry-*`
分支内单独维护 (路径 `test-suit/.../bugfix/bug-starry-<name>/c/src/main.c`):

- rcore-os#713 — `setuid((uid_t)-1)` 在 starry 上被错误接受
  (Linux 应返 EINVAL, 由 uid_valid 检查阻断)
- rcore-os#714 — `/proc/self/status` 在 starry loongarch64 上
  vm_write EFAULT (Uid:/Gid:/Groups: 三行均受影响)
- rcore-os#715 — `setgroups(N, list)` 之后 `/proc/self/status`
  的 Groups: 行不立即同步 (starry race)
- rcore-os#716 — apk + curl 组合在 starry loongarch64 上
  600s 超时 ~90% flake (与本测试矩阵 stress 路径相关)

上述 bug 由 rcore-os#717 (fix-uid-gid-bugs, local 修) 与
rcore-os#718 (fix-uid-gid-deep, cross-subsystem) 这两个 fix PR
分别托管 — fix PR 同步把 bug-starry-* 注册进 4 arch bugfix toml 矩阵,
CI 由 FAIL → PASS 转化作为修复验收。

## CI / 验证

rcore-os#727 当前 bucket: `pass=18 fail=0 skipping=30`。

## Matrix case 数学

### `matrix.c` — 1536 case → 缩减到 864 (aarch64 timeout)

原公式: `8 ruid × 8 euid × 6 state × 2 mode × 2 syscall = 1536`
缩减后: `6 ruid × 6 euid × 6 state × 2 mode × 2 syscall = 864`
(commit `5248f83fe`)

- ruid 6: `0 / 1000 / 2000 / 65535 / NOCHG / boundary`
- euid 6: 同 ruid (枚举全笛卡尔积)
- state 6: `root_unmod / 3id_distinct / unpriv / mid_priv /
  after_setuid / NOCHG_baseline`
- mode 2: libc / raw
- syscall 2: setreuid / setregid

为什么 (原) 这么多: man D5 saved 规则的真假组合依赖 (ruid, euid, old.ruid)
三元组关系 → 必须全笛卡尔积才能覆盖; ruid × euid 笛卡尔积是验 D5 规则的
最小覆盖单元; × 6 state: EPERM 决策依赖 old cred 起点。

为什么缩减: emulated aarch64 上 fork-heavy 矩阵 1536 case 接近 5 min,
大概率 CI 超时 (timeout=600s)。缩减 8→6 保留关键 boundary
(0/NOCHG/16-bit max), 减少笛卡尔积规模到 ~56%。边界覆盖仍完整。

每 case 4-5 assertion ≈ 3500-4500 PASS (864 base)。

## 复现命令

切换分支:
```bash
cd <repo-root>
git checkout test-uid-gid-re-setters
```

本地 Linux (host, WSL2):
```bash
cd <repo-root>/test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-re-setters/c
rm -rf build && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug && make
sudo ./test-uid-gid-re-setters
```

starry qemu (4 arch):
```bash
source .starry-env.sh && cargo starry test qemu --arch x86_64      -c syscall
source .starry-env.sh && cargo starry test qemu --arch aarch64     -c syscall
source .starry-env.sh && cargo starry test qemu --arch riscv64     -c syscall
source .starry-env.sh && cargo starry test qemu --arch loongarch64 -c syscall
```

## 引用

- 相关 PR: stacked 前 Group A/B (rcore-os#725..rcore-os#726); stacked 后 Group D/E
  (rcore-os#728..rcore-os#729); 衍生 fix rcore-os#718 (fix-uid-gid-deep) PR_SET/GET_DUMPABLE
  支撑 core_dump_inhibit 模块

Signed-off-by: Leo Cheng <chengkelfanke@gmail.com>
@Lfan-ke
Lfan-ke force-pushed the test-uid-gid-re-setters branch from 29c20ad to ff1eb04 Compare May 18, 2026 00:17
Lfan-ke added a commit to Lfan-ke/tgoskits that referenced this pull request May 18, 2026
## 分支用途

Group D: `setresuid` / `setresgid` 三参 (real + effective + saved)
setter syscall 的 man-first 地毯式测试, 本地 3022 PASS。对应 rcore-os#728。

## 测试覆盖 (每文件每函数)

测试位置: `test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-res-setters/c/src/`

模块总览 (10 模块 / 43 个 static void case 函数 / main.c 串联):
`setresuid` / `setresgid` / `three_arg_independence` / `boundary` /
`fsuid_follow` / `procfs_visibility` / `nptl_sync` / `core_dump_inhibit` /
`matrix` / `errno_preservation`。

### 3.1 c/src/setresuid.c (6 case + 1 helper)

- `waitpid_safely()` helper
- `setresuid_all_nochg()` — setresuid(-1,-1,-1) 三字段不变
  (man §DESCRIPTION sentinel -1)
- `setresuid_self_self_self()` — setresuid(self,self,self) 三字段不变
- `setresuid_root_arbitrary_three_values()` — root setresuid(r,e,s) 三个
  任意不同值都成功 (man §DESCRIPTION root)
- `setresuid_unpriv_within_set_ok()` — 非特权改到当前 {r,e,s} 集合内 → OK
- `setresuid_unpriv_outside_set_eperm()` — 非特权超出 {r,e,s} 集合 → EPERM
  (man §ERRORS EPERM)
- `setresuid_raw_matches_libc()` — 裸 syscall 与 libc 一致

### 3.2 c/src/setresgid.c (6 case + 1 helper)

- `waitpid_safely()` helper
- `setresgid_all_nochg()` / `setresgid_self_self_self()` /
  `setresgid_root_arbitrary_three_values()` /
  `setresgid_unpriv_within_set_ok()` /
  `setresgid_unpriv_outside_set_eperm()` /
  `setresgid_raw_matches_libc()`

### 3.3 c/src/three_arg_independence.c (7 case + 2 helper)

- `waitpid_safely()` helper / `test_combo()` helper
- `independence_root_only_r()` — root setresuid(NEW,-1,-1) 只改 r, e/s
  保持 (man §DESCRIPTION 三参独立)
- `independence_root_only_s()` — root setresuid(-1,-1,NEW) 只改 s
- `independence_root_only_e()` — root setresuid(-1,NEW,-1) 只改 e
- `independence_all_three_distinct()` — r/e/s 三参传不同值 → 三槽各自
  独立赋值
- `independence_all_nochg_no_op()` — root (-1,-1,-1) 无操作不变
- `independence_all_nochg_no_op_gid()` — GID 侧 (-1,-1,-1) 无操作
- `independence_all_nochg_unpriv_ok()` — 非特权 (-1,-1,-1) 也 OK
  (不触发 EPERM)

### 3.4 c/src/boundary.c (3 case + 1 helper)

- `waitpid_safely()` helper
- `boundary_raw_all_u32max()` — 裸 syscall 三参全 (uid_t)-1 sentinel →
  nochg 路径
- `boundary_root_extreme_uids()` — root 极值 0/65535/65536/(u32max-1)
  (man §HISTORY 16→32-bit)
- `boundary_unpriv_one_outside_eperm()` — 非特权一个字段出集合 → EPERM

### 3.5 c/src/fsuid_follow.c (3 case + 1 helper)

- `waitpid_safely2()` helper
- `fsuid_follows_euid_after_setresuid()` — setresuid 改 euid → fsuid 自动
  跟随 (verify via setfsuid(-1) 读回) (man §DESCRIPTION fsuid follow euid)
- `fsgid_follows_egid_after_setresgid()` — setresgid 改 egid → fsgid 自动
  跟随
- `fsuid_unchanged_when_euid_unchanged()` — euid 未变 → fsuid 也不变

### 3.6 c/src/matrix.c (1 case + 4 helper)

- `setup_state()` / `read_uid()` / `read_gid()` / `waitpid_safely()`
  helper
- `matrix_one()` — (syscall × state × ruid × euid × suid × mode) 笛卡尔
  单格 — 验 rc/errno + 三槽 + fsuid 跟随

### 3.7 c/src/procfs_visibility.c (6 case + 2 helper)

- `waitpid_safely_pv()` / `parse_proc_id_line()` helper
- `proc_uid_baseline()` — 未改动时 /proc Uid: == (uid,euid,suid)
- `proc_uid_after_setresuid()` — setresuid 后 /proc Uid: 三字段精确同步
- `proc_uid_after_setresuid_nochg()` — setresuid(-1,-1,-1) 后 procfs 不变
- `proc_gid_after_setresgid()` — setresgid 后 /proc Gid: 同步
- `proc_compound_setres()` — 复合调用后 procfs 全一致
- `proc_gid_baseline()` — /proc Gid: 基线一致

### 3.8 c/src/nptl_sync.c (3 case)

- `nptl_setresuid_main_to_pthread()` — 主线程 setresuid → 子 pthread
  getresuid 看到相同 cred (man §VERSIONS C library/kernel differences)
- `nptl_setresgid_pthread_to_main()`
- `nptl_concurrent_setresuid()` — 并发 setresuid 最终收敛

### 3.9 c/src/core_dump_inhibit.c (3 case)

- `cd_baseline()` — PR_GET_DUMPABLE == 1 (man 2 prctl §PR_GET_DUMPABLE)
- `cd_setresuid_nochg()` — setresuid 三参 nochg → dumpable 不变
- `cd_setresuid_changes()` — setresuid 改 euid → PR_GET_DUMPABLE → 0

### 3.10 c/src/errno_preservation.c (5 case)

- `setresuid_nochg_errno_preserved()` /
  `setresgid_nochg_errno_preserved()` /
  `setresuid_self_errno_preserved()` /
  `setresgid_self_errno_preserved()` /
  `raw_setresuid_success_errno_preserved()`

### 3.11 c/src/main.c (聚合 entry point, 非测点)

按顺序 COLLECT 10 个模块, 每模块 `*_run()` 返 `__fail`, main 汇总
TOTAL: N fail。

## 相关 syscall man 摘录 (核心段)

`setresuid` §DESCRIPTION:
- "setresuid() sets the real user ID, the effective user ID, and the
  saved set-user-ID of the calling process."
- "An unprivileged process may change its real UID, effective UID, and
  saved set-user-ID, each to one of: the current real UID, the current
  effective UID, or the current saved set-user-ID."
- "A privileged process (on Linux, one having the CAP_SETUID capability)
  may set its real UID, effective UID, and saved set-user-ID to
  arbitrary values."
- "If one of the arguments equals -1, the corresponding value is not
  changed."
- "Regardless of what changes are made to the real UID, effective UID,
  and saved set-user-ID, the filesystem UID is always set to the same
  value as the (possibly new) effective UID."

`setresuid` §ERRORS:
- EINVAL — One or more of the target user or group IDs is not valid in
  this user namespace.
- EPERM — The calling process is not privileged and tried to change the
  IDs to values that are not permitted.

`setresuid` §HISTORY: Linux 2.1.44, glibc 2.3.2. HP-UX, FreeBSD. glibc
wrapper transparently deals with 16-bit/32-bit variations。

## 发现的 bug (本测试过程中暴露的 starry vs Linux 差异)

本 test PR 在 starry kernel 上跑时, 暴露以下 starry-vs-Linux 行为差异,
已各自登记为上游 issue, 单文件 C 复现程序在 fork repo 的 `bug-starry-*`
分支内单独维护 (路径 `test-suit/.../bugfix/bug-starry-<name>/c/src/main.c`):

- rcore-os#713 — `setuid((uid_t)-1)` 在 starry 上被错误接受
  (Linux 应返 EINVAL, 由 uid_valid 检查阻断)
- rcore-os#714 — `/proc/self/status` 在 starry loongarch64 上
  vm_write EFAULT (Uid:/Gid:/Groups: 三行均受影响)
- rcore-os#715 — `setgroups(N, list)` 之后 `/proc/self/status`
  的 Groups: 行不立即同步 (starry race)
- rcore-os#716 — apk + curl 组合在 starry loongarch64 上
  600s 超时 ~90% flake (与本测试矩阵 stress 路径相关)

上述 bug 由 rcore-os#717 (fix-uid-gid-bugs, local 修) 与
rcore-os#718 (fix-uid-gid-deep, cross-subsystem) 这两个 fix PR
分别托管 — fix PR 同步把 bug-starry-* 注册进 4 arch bugfix toml 矩阵,
CI 由 FAIL → PASS 转化作为修复验收。

## CI / 验证

rcore-os#728 当前 bucket: `pass=18 fail=0 skipping=30`。

## Matrix case 数学

### `matrix.c` — 3000 case (Group D 最大规模)

公式: `5 ruid × 5 euid × 5 suid × 6 state × 2 mode × 2 syscall = 3000`

- ruid 5: `0 / 1000 / 2000 / NOCHG / boundary`
- euid 5: 同 ruid
- suid 5: 同 ruid
- state 6: `root_unmod / 3id_distinct / unpriv / mid_priv /
  after_setresuid / NOCHG_baseline`
- mode 2: libc / raw
- syscall 2: setresuid / setresgid

为什么这么多维度:
- `setresuid(ruid, euid, suid)` 是三参数独立接口 — 与 setreuid (2 参 +
  隐式 saved 更新) 不同, setres* 每参各自独立, 必须 5^3=125 个三元组
  覆盖全部组合空间
- unpriv 每槽位 in_set 检查独立, 需 5^3 组合验
- fsuid 必须验每个 (ruid, euid, suid) 组合下都跟新 euid

5^3 不能再缩: 缩到 4^3=64 会漏掉 NOCHG/boundary corner; 5^3=125 是
最小覆盖。

每 case 4-5 assertion ≈ 12000-15000 PASS (含 fsuid procfs 验证)。

## 复现命令

切换分支:
```bash
cd <repo-root>
git checkout test-uid-gid-res-setters
```

本地 Linux (host, WSL2):
```bash
cd <repo-root>/test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-res-setters/c
rm -rf build && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug && make
sudo ./test-uid-gid-res-setters
```

starry qemu (4 arch):
```bash
source .starry-env.sh && cargo starry test qemu --arch x86_64      -c syscall
source .starry-env.sh && cargo starry test qemu --arch aarch64     -c syscall
source .starry-env.sh && cargo starry test qemu --arch riscv64     -c syscall
source .starry-env.sh && cargo starry test qemu --arch loongarch64 -c syscall
```

## 引用

- 相关 PR: stacked 前 Group A/B/C (rcore-os#725..rcore-os#727); stacked 后 Group E
  (rcore-os#729); fsuid_follow 模块依赖 fix rcore-os#717 (fix-uid-gid-bugs) 新增
  setfsuid/setfsgid 实现

Signed-off-by: Leo Cheng <chengkelfanke@gmail.com>
@Lfan-ke Lfan-ke changed the title test(syscall): test-uid-gid-re-setters 1562 PASS (PR Lfan-ke/tgoskits#6) test(syscall): test-uid-gid-re-setters 1562 PASS May 18, 2026
Lfan-ke added a commit to Lfan-ke/tgoskits that referenced this pull request May 18, 2026
## 分支用途

Group B: `setuid` / `setgid` 两个直接 setter syscall 的 man-first 地毯式
测试, 本地 477 PASS。对应 rcore-os#726。

## 测试覆盖 (每文件每函数)

测试位置: `test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-direct-setters/c/src/`

模块总览 (10 模块 / 46 个 static void case 函数 / main.c 串联):
`setuid` / `setgid` / `cross_root_unprivileged` / `boundary` / `matrix` /
`procfs_visibility` / `nptl_sync` / `core_dump_inhibit` / `uid32_no_trunc` /
`errno_preservation`。

### 3.1 c/src/setuid.c (10 case + 1 helper)

- `waitpid_safely()` helper — 循环 waitpid 处理 EINTR
- `setuid_idempotent_to_self()` — setuid(geteuid()) 后三字段 (r,e,s) 不变
  (man §DESCRIPTION POSIX_SAVED_IDS)
- `setuid_root_sets_all_three()` — root setuid(target) → r/e/s 全被设为
  target (man §DESCRIPTION root path)
- `setuid_returns_zero_on_success()` — 成功 rc == 0 (不只是 ≥0)
- `setuid_raw_syscall_matches_libc()` — 裸 syscall 与 libc 行为一致
- `setuid_unprivileged_eperm_in_child()` — 非特权 setuid 在 fork 子 →
  EPERM (man §ERRORS EPERM)
- `setuid_raw_vs_libc_failed_path()` — EPERM 失败路径下 raw rc/errno 与
  libc 严格相同
- `setuid_irreversibility()` — root setuid(non-zero) drop 后无法
  setuid(0) 回去 (man §DESCRIPTION 不可逆)
- `setuid_fork_child_independent()` — fork 子 setuid 改动不污染父 cred
- `setuid_self_euid_no_flush()` — 非特权 setuid(euid) 不清除 saved-set-uid
- `setuid_einval_uid_minus_one()` — uid=(uid_t)-1 在非特权下 → EPERM/
  EINVAL (软容忍 starry bug B)

### 3.2 c/src/setgid.c (10 case + 1 helper)

- `waitpid_safely()` helper
- `setgid_idempotent_to_self()` — setgid(getegid()) 三字段不变
- `setgid_root_sets_all_three()` — root setgid(target) → r/e/s GID 全置
- `setgid_returns_zero_on_success()`
- `setgid_raw_syscall_matches_libc()`
- `setgid_unprivileged_eperm_in_child()` — 非特权 setgid(任意) → EPERM
- `setgid_raw_vs_libc_failed_path()`
- `setgid_alone_not_irreversible_caps_intact()` — 仅 setgid drop 不丢
  CAP_SETUID, 仍可 setuid
- `setgid_irreversible_after_setuid_drop()` — setuid drop 后 setgid 不可
  逆 (CAP_SETGID 丢)
- `setgid_fork_child_independent()`
- `setgid_self_egid_no_change()` — setgid(egid) 不动 saved-set-gid

### 3.3 c/src/cross_root_unprivileged.c (3 case + 1 helper)

- `waitpid_safely()` helper
- `root_setuid_arbitrary_updates_all()` — root setuid(任意值) → r/e/s
  全跟
- `unprivileged_setuid_self_succeeds()` — 非特权 setuid(self) 成功且字段
  不变
- `unprivileged_setuid_arbitrary_eperm()` — 非特权 setuid(非 {r,e,s}) →
  EPERM

### 3.4 c/src/boundary.c (3 case)

- `setuid_boundary_values_root()` — root 路径下边界 uid:
  0/1/65535/65536/0x7FFFFFFE 全 OK
- `setuid_u32max_sentinel_unprivileged()` — 非特权 setuid((uid_t)-1)
  sentinel → EPERM/EINVAL (man §ERRORS EINVAL)
- `setuid_raw_syscall_returns_negative_errno()` — 裸 syscall 失败时 rc
  为 `-EPERM` (不是 -1+errno)

### 3.5 c/src/matrix.c (1 case + 4 helper)

- `read_uid_cred()` / `read_gid_cred()` / `setup_state()` /
  `waitpid_safely()` helper
- `matrix_one()` — (syscall × state × input × mode) 笛卡尔单格 — 验
  rc/errno + getresuid 后续三槽

### 3.6 c/src/procfs_visibility.c (5 case + 2 helper)

- `waitpid_safely_pv()` / `parse_proc_id_line()` helper
- `procfs_uid_baseline_root()` — 未改动时 /proc Uid: == (uid,euid,suid)
  一致 (proc(5) §Uid)
- `procfs_uid_after_setuid_root()` — root setuid 后 procfs 三字段同步
- `procfs_uid_after_setuid_drop()` — drop 后 procfs Uid: 三字段一致更新
- `procfs_gid_after_setgid()` — setgid 后 procfs Gid: 行同步
- `procfs_uid_after_setresuid()` — setresuid 后 procfs 三字段精确同步

### 3.7 c/src/nptl_sync.c (3 case)

- `nptl_setuid_main_visible_to_pthread()` — 主线程 setuid → 子 pthread
  getresuid 看到相同 cred (man §VERSIONS C library/kernel differences)
- `nptl_setgid_pthread_visible_to_main()`
- `nptl_concurrent_setuid_converges()`

### 3.8 c/src/core_dump_inhibit.c (3 case + 1 helper)

- `waitpid_safely_cd()` helper
- `core_dump_baseline()` — 未改动时 PR_GET_DUMPABLE == 1 (man §NOTES)
- `core_dump_setuid_no_euid_change()` — setuid(euid) euid 未变 →
  dumpable 不变
- `core_dump_setuid_changes_euid()` — setuid 改变 euid → PR_GET_DUMPABLE
  → 0 (禁 core)

### 3.9 c/src/uid32_no_trunc.c (3 case + 1 helper)

- `waitpid_safely()` helper
- `setuid_32bit_no_truncate()` — child setuid 大 UID (>16-bit) 后
  getresuid 不被截 (man §HISTORY 16→32-bit)
- `setgid_32bit_no_truncate()`
- `setuid_32bit_via_raw_syscall()`

### 3.10 c/src/errno_preservation.c (5 case)

- `setuid_self_errno_preserved()` / `setgid_self_errno_preserved()` /
  `setuid_root_errno_preserved()` / `setgid_root_errno_preserved()` /
  `raw_setuid_success_errno_preserved()` — 成功路径不动预置 errno

### 3.11 c/src/main.c (聚合 entry point, 非测点)

按顺序 COLLECT 10 个模块, 每模块 `*_run()` 返 `__fail`, main 汇总
TOTAL: N fail 收尾。

## 相关 syscall man 摘录 (核心段)

`setuid` §DESCRIPTION: "setuid() sets the effective user ID of the
calling process. If the calling process is privileged (more precisely:
if the process has the CAP_SETUID capability in its user namespace),
the real UID and saved set-user-ID are also set."

`setuid` §ERRORS:
- EAGAIN — temporary failure allocating kernel data structures
- EINVAL — The user ID specified in uid is not valid in this user
  namespace
- EPERM — The user is not privileged and uid does not match the real
  UID or saved set-user-ID of the calling process

`setuid` §NOTES: "If uid is different from the old effective UID, the
process will be forbidden from leaving core dumps."

`setuid` §VERSIONS (C library/kernel differences): NPTL wrapper 使用
signal-based 技术保证 process-wide thread cred 同步。

`setgid` §ERRORS: EINVAL (gid 不合法) / EPERM (无 CAP_SETGID 且 gid 不
匹配 real GID 或 saved set-group-ID)。

## 发现的 bug (本测试过程中暴露的 starry vs Linux 差异)

本 test PR 在 starry kernel 上跑时, 暴露以下 starry-vs-Linux 行为差异,
已各自登记为上游 issue, 单文件 C 复现程序在 fork repo 的 `bug-starry-*`
分支内单独维护 (路径 `test-suit/.../bugfix/bug-starry-<name>/c/src/main.c`):

- rcore-os#713 — `setuid((uid_t)-1)` 在 starry 上被错误接受
  (Linux 应返 EINVAL, 由 uid_valid 检查阻断)
- rcore-os#714 — `/proc/self/status` 在 starry loongarch64 上
  vm_write EFAULT (Uid:/Gid:/Groups: 三行均受影响)
- rcore-os#715 — `setgroups(N, list)` 之后 `/proc/self/status`
  的 Groups: 行不立即同步 (starry race)
- rcore-os#716 — apk + curl 组合在 starry loongarch64 上
  600s 超时 ~90% flake (与本测试矩阵 stress 路径相关)

上述 bug 由 rcore-os#717 (fix-uid-gid-bugs, local 修) 与
rcore-os#718 (fix-uid-gid-deep, cross-subsystem) 这两个 fix PR
分别托管 — fix PR 同步把 bug-starry-* 注册进 4 arch bugfix toml 矩阵,
CI 由 FAIL → PASS 转化作为修复验收。

## CI / 验证

rcore-os#726 当前 bucket: `pass=18 fail=0 skipping=30`。

## Matrix case 数学

### `matrix.c` — 448 base case (×3-5 assertion ≈ 1500-2000 PASS)

公式: `2 syscall × 7 state × 16 input × 2 mode = 448`

- syscall 2: `setuid` / `setgid`
- state 7: `root_unmod` / `after_setresuid(0,1000,2000)` /
  `after_setresuid(0,0,1000)` / `unpriv(1000)` /
  `unpriv_3id(1000,2000,3000)` / `after_setgid(1000)` / `mixed_uid_gid`
- input 16: 0 / 1 / 100 / 999 / 1000 / 2000 / 3000 / 65535 / 65536 /
  100001 / 0x7FFFFFFE / NOCHG((uid_t)-1) / current_uid / current_euid /
  current_suid / boundary
- mode 2: libc / raw

为什么这么多维度:
- input 16 值: man §ERRORS EINVAL 触发只在 `(uid_t)-1`; EPERM 触发依赖
  input 是否在 {uid, euid, suid}; root 接受任意值 — 16 值覆盖 small /
  large / boundary (16-bit / 32-bit) / sentinel / self-set / cross-set
- state 7: 同一 `setuid(input)` 在不同 cred 起点上行为不同 — 覆盖 man
  §EPERM 决策树各分支
- mode 2: libc/raw 分离测验 ABI 一致性

每 case 3-5 assertion: rc / errno / 后续 getresuid r/e/s 三槽各验 /
fsuid procfs 检查 → 单 case 平均 4 assert → 总 ~1800 PASS。

## 复现命令

切换分支:
```bash
cd <repo-root>
git checkout test-uid-gid-direct-setters
```

本地 Linux (host, WSL2):
```bash
cd <repo-root>/test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-direct-setters/c
rm -rf build && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug && make
sudo ./test-uid-gid-direct-setters
```

starry qemu (4 arch):
```bash
source .starry-env.sh && cargo starry test qemu --arch x86_64      -c syscall
source .starry-env.sh && cargo starry test qemu --arch aarch64     -c syscall
source .starry-env.sh && cargo starry test qemu --arch riscv64     -c syscall
source .starry-env.sh && cargo starry test qemu --arch loongarch64 -c syscall
```

## 引用

- 相关 PR: stacked 前 Group A (rcore-os#725); stacked 后 Group C/D/E
  (rcore-os#727..rcore-os#729); 衍生 bug-* rcore-os#713 (setuid/setgid no uid_valid),
  fix rcore-os#717 (fix-uid-gid-bugs) 修复该 bug

Signed-off-by: Leo Cheng <chengkelfanke@gmail.com>
Lfan-ke added a commit to Lfan-ke/tgoskits that referenced this pull request May 18, 2026
## 分支用途

Group C: `setreuid` / `setregid` 两参 (real + effective) setter syscall
的 man-first 地毯式测试, 本地 913 PASS。对应 rcore-os#727。

## 测试覆盖 (每文件每函数)

测试位置: `test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-re-setters/c/src/`

模块总览 (9 模块 / 38 个 static void case 函数 / main.c 串联):
`setreuid` / `setregid` / `saved_id_semantics` / `boundary` / `matrix` /
`procfs_visibility` / `nptl_sync` / `core_dump_inhibit` /
`errno_preservation`。

### 3.1 c/src/setreuid.c (6 case + 1 helper)

- `waitpid_safely()` helper
- `setreuid_both_nochg_idempotent()` — setreuid((uid_t)-1,(uid_t)-1) 不改
  r/e/s 三字段 (man §DESCRIPTION sentinel -1)
- `setreuid_ruid_self()` — setreuid(self,-1) 后 real == 原 real
- `setreuid_euid_self()` — setreuid(-1,self) 后 effective == 原 effective
- `setreuid_root_arbitrary()` — root setreuid(r,e) 任意值成功
- `setreuid_unpriv_eperm()` — 非特权越权 (非 {ruid,euid,suid}) → EPERM
  (man §ERRORS EPERM)
- `setreuid_raw_matches_libc()` — 裸 syscall 与 libc 一致 (rc + 三字段)

### 3.2 c/src/setregid.c (6 case + 1 helper)

- `waitpid_safely()` helper
- `setregid_both_nochg()` / `setregid_rgid_self()` / `setregid_egid_self()`
  / `setregid_root_arbitrary()` / `setregid_unpriv_eperm()` /
  `setregid_raw_matches_libc()` — 镜像 setreuid 测试

### 3.3 c/src/saved_id_semantics.c (6 case + 1 helper)

- `waitpid_safely()` helper
- `saved_id_ruid_set_updates_suid()` — ruid != -1 → suid := new euid
  (D5 规则触发) (man §DESCRIPTION)
- `saved_id_euid_set_to_nonruid_updates_suid()` — euid 改成 ≠ 旧 ruid →
  suid 更新
- `saved_id_both_set_root()` — root 同时改 r 和 e, suid 更新到新 euid
- `saved_id_both_nochg_no_change()` — (-1,-1) → r/e/s 全无变化
- `saved_id_d5_reverse_corner()` — D5 反向 corner: euid := 旧 ruid 时
  saved 不应被更新
- `saved_id_d5_reverse_corner_gid()` — D5 corner GID 侧

### 3.4 c/src/boundary.c (3 case + 1 helper)

- `waitpid_safely()` helper
- `boundary_raw_u32max_nochg()` — 裸 syscall 传 (uid_t)-1 sentinel 走
  nochg 路径
- `boundary_root_extreme_values()` — root 极值 0/1000/65535/65536/
  (u32max-1) (man §HISTORY 16→32-bit)
- `boundary_unpriv_extreme_eperm()` — 非特权传极值 → EPERM

### 3.5 c/src/matrix.c (1 case + 4 helper)

- `setup_state()` / `read_uid_cred()` / `read_gid_cred()` /
  `waitpid_safely()` helper
- `matrix_one()` — (syscall × state × ruid × euid × mode) 笛卡尔单格 —
  验 rc/errno + 后续 r/e/s 三槽 + D5 saved 规则

### 3.6 c/src/procfs_visibility.c (5 case + 2 helper)

- `waitpid_safely_pv()` / `parse_proc_id_line()` helper
- `proc_uid_after_setreuid_full()` — setreuid(r,e) 全改 → /proc Uid:
  三字段一致 (proc(5) §Uid + man §DESCRIPTION)
- `proc_uid_after_setreuid_nochg_r()` — setreuid(-1,e) → procfs real
  不变、e 同步、s 按 D5
- `proc_uid_after_setreuid_nochg_same()` — setreuid(r,r) same-value →
  procfs 三字段一致
- `proc_gid_after_setregid_full()` — setregid 全改 → /proc Gid: 行同步
- `proc_compound_setre()` — 组合调用 (setreuid 再 setregid) 后 procfs
  三字段全一致

### 3.7 c/src/nptl_sync.c (3 case)

- `nptl_setreuid_main_to_pthread()` — 主线程 setreuid → 子 pthread
  getresuid 看到相同 cred (man §VERSIONS C library/kernel differences)
- `nptl_setregid_pthread_to_main()`
- `nptl_concurrent_setreuid()`

### 3.8 c/src/core_dump_inhibit.c (3 case)

- `cd_baseline()` — 未改动时 PR_GET_DUMPABLE == 1 (man §NOTES)
- `cd_setreuid_no_euid_change()` — euid 未变 → dumpable 不变
- `cd_setreuid_changes_euid()` — euid 变化 → PR_GET_DUMPABLE → 0

### 3.9 c/src/errno_preservation.c (5 case)

- `setreuid_nochg_errno_preserved()` / `setregid_nochg_errno_preserved()`
  / `setreuid_self_errno_preserved()` /
  `setregid_self_errno_preserved()` /
  `raw_setreuid_success_errno_preserved()`

### 3.10 c/src/main.c (聚合 entry point, 非测点)

按顺序 COLLECT 9 个模块, 每模块 `*_run()` 返 `__fail`, main 汇总
TOTAL: N fail。

## 相关 syscall man 摘录 (核心段)

`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."
- "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." (D5 规则)

`setreuid` §ERRORS:
- EINVAL — One or more of the target user or group IDs is not valid in
  this user namespace.
- EPERM — The calling process is not privileged and a change other than
  (i) swapping effective with real, (ii) setting one to the value of the
  other, (iii) setting effective to saved set-user-ID was specified.

`setreuid` §HISTORY: glibc setreuid/setregid wrapper transparently deals
with 16-bit/32-bit variations across kernel versions。

## 发现的 bug (本测试过程中暴露的 starry vs Linux 差异)

本 test PR 在 starry kernel 上跑时, 暴露以下 starry-vs-Linux 行为差异,
已各自登记为上游 issue, 单文件 C 复现程序在 fork repo 的 `bug-starry-*`
分支内单独维护 (路径 `test-suit/.../bugfix/bug-starry-<name>/c/src/main.c`):

- rcore-os#713 — `setuid((uid_t)-1)` 在 starry 上被错误接受
  (Linux 应返 EINVAL, 由 uid_valid 检查阻断)
- rcore-os#714 — `/proc/self/status` 在 starry loongarch64 上
  vm_write EFAULT (Uid:/Gid:/Groups: 三行均受影响)
- rcore-os#715 — `setgroups(N, list)` 之后 `/proc/self/status`
  的 Groups: 行不立即同步 (starry race)
- rcore-os#716 — apk + curl 组合在 starry loongarch64 上
  600s 超时 ~90% flake (与本测试矩阵 stress 路径相关)

上述 bug 由 rcore-os#717 (fix-uid-gid-bugs, local 修) 与
rcore-os#718 (fix-uid-gid-deep, cross-subsystem) 这两个 fix PR
分别托管 — fix PR 同步把 bug-starry-* 注册进 4 arch bugfix toml 矩阵,
CI 由 FAIL → PASS 转化作为修复验收。

## CI / 验证

rcore-os#727 当前 bucket: `pass=18 fail=0 skipping=30`。

## Matrix case 数学

### `matrix.c` — 1536 case → 缩减到 864 (aarch64 timeout)

原公式: `8 ruid × 8 euid × 6 state × 2 mode × 2 syscall = 1536`
缩减后: `6 ruid × 6 euid × 6 state × 2 mode × 2 syscall = 864`
(commit `5248f83fe`)

- ruid 6: `0 / 1000 / 2000 / 65535 / NOCHG / boundary`
- euid 6: 同 ruid (枚举全笛卡尔积)
- state 6: `root_unmod / 3id_distinct / unpriv / mid_priv /
  after_setuid / NOCHG_baseline`
- mode 2: libc / raw
- syscall 2: setreuid / setregid

为什么 (原) 这么多: man D5 saved 规则的真假组合依赖 (ruid, euid, old.ruid)
三元组关系 → 必须全笛卡尔积才能覆盖; ruid × euid 笛卡尔积是验 D5 规则的
最小覆盖单元; × 6 state: EPERM 决策依赖 old cred 起点。

为什么缩减: emulated aarch64 上 fork-heavy 矩阵 1536 case 接近 5 min,
大概率 CI 超时 (timeout=600s)。缩减 8→6 保留关键 boundary
(0/NOCHG/16-bit max), 减少笛卡尔积规模到 ~56%。边界覆盖仍完整。

每 case 4-5 assertion ≈ 3500-4500 PASS (864 base)。

## 复现命令

切换分支:
```bash
cd <repo-root>
git checkout test-uid-gid-re-setters
```

本地 Linux (host, WSL2):
```bash
cd <repo-root>/test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-re-setters/c
rm -rf build && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug && make
sudo ./test-uid-gid-re-setters
```

starry qemu (4 arch):
```bash
source .starry-env.sh && cargo starry test qemu --arch x86_64      -c syscall
source .starry-env.sh && cargo starry test qemu --arch aarch64     -c syscall
source .starry-env.sh && cargo starry test qemu --arch riscv64     -c syscall
source .starry-env.sh && cargo starry test qemu --arch loongarch64 -c syscall
```

## 引用

- 相关 PR: stacked 前 Group A/B (rcore-os#725..rcore-os#726); stacked 后 Group D/E
  (rcore-os#728..rcore-os#729); 衍生 fix rcore-os#718 (fix-uid-gid-deep) PR_SET/GET_DUMPABLE
  支撑 core_dump_inhibit 模块

Signed-off-by: Leo Cheng <chengkelfanke@gmail.com>
@Lfan-ke
Lfan-ke force-pushed the test-uid-gid-re-setters branch from ff1eb04 to 8d262ca Compare May 18, 2026 01:10
@Lfan-ke Lfan-ke changed the title test(syscall): test-uid-gid-re-setters 1562 PASS test(syscall): test-uid-gid-re-setters 913 PASS May 18, 2026
Lfan-ke added a commit to Lfan-ke/tgoskits that referenced this pull request May 18, 2026
## 分支用途

Group D: `setresuid` / `setresgid` 三参 (real + effective + saved)
setter syscall 的 man-first 地毯式测试, 本地 3052 PASS。对应 rcore-os#728。

## 测试覆盖 (每文件每函数)

测试位置: `test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-res-setters/c/src/`

模块总览 (10 模块 / 43 个 static void case 函数 / main.c 串联):
`setresuid` / `setresgid` / `three_arg_independence` / `boundary` /
`fsuid_follow` / `procfs_visibility` / `nptl_sync` / `core_dump_inhibit` /
`matrix` / `errno_preservation`。

### 3.1 c/src/setresuid.c (6 case + 1 helper)

- `waitpid_safely()` helper
- `setresuid_all_nochg()` — setresuid(-1,-1,-1) 三字段不变
  (man §DESCRIPTION sentinel -1)
- `setresuid_self_self_self()` — setresuid(self,self,self) 三字段不变
- `setresuid_root_arbitrary_three_values()` — root setresuid(r,e,s) 三个
  任意不同值都成功 (man §DESCRIPTION root)
- `setresuid_unpriv_within_set_ok()` — 非特权改到当前 {r,e,s} 集合内 → OK
- `setresuid_unpriv_outside_set_eperm()` — 非特权超出 {r,e,s} 集合 → EPERM
  (man §ERRORS EPERM)
- `setresuid_raw_matches_libc()` — 裸 syscall 与 libc 一致

### 3.2 c/src/setresgid.c (6 case + 1 helper)

- `waitpid_safely()` helper
- `setresgid_all_nochg()` / `setresgid_self_self_self()` /
  `setresgid_root_arbitrary_three_values()` /
  `setresgid_unpriv_within_set_ok()` /
  `setresgid_unpriv_outside_set_eperm()` /
  `setresgid_raw_matches_libc()`

### 3.3 c/src/three_arg_independence.c (7 case + 2 helper)

- `waitpid_safely()` helper / `test_combo()` helper
- `independence_root_only_r()` — root setresuid(NEW,-1,-1) 只改 r, e/s
  保持 (man §DESCRIPTION 三参独立)
- `independence_root_only_s()` — root setresuid(-1,-1,NEW) 只改 s
- `independence_root_only_e()` — root setresuid(-1,NEW,-1) 只改 e
- `independence_all_three_distinct()` — r/e/s 三参传不同值 → 三槽各自
  独立赋值
- `independence_all_nochg_no_op()` — root (-1,-1,-1) 无操作不变
- `independence_all_nochg_no_op_gid()` — GID 侧 (-1,-1,-1) 无操作
- `independence_all_nochg_unpriv_ok()` — 非特权 (-1,-1,-1) 也 OK
  (不触发 EPERM)

### 3.4 c/src/boundary.c (3 case + 1 helper)

- `waitpid_safely()` helper
- `boundary_raw_all_u32max()` — 裸 syscall 三参全 (uid_t)-1 sentinel →
  nochg 路径
- `boundary_root_extreme_uids()` — root 极值 0/65535/65536/(u32max-1)
  (man §HISTORY 16→32-bit)
- `boundary_unpriv_one_outside_eperm()` — 非特权一个字段出集合 → EPERM

### 3.5 c/src/fsuid_follow.c (3 case + 1 helper)

- `waitpid_safely2()` helper
- `fsuid_follows_euid_after_setresuid()` — setresuid 改 euid → fsuid 自动
  跟随 (verify via setfsuid(-1) 读回) (man §DESCRIPTION fsuid follow euid)
- `fsgid_follows_egid_after_setresgid()` — setresgid 改 egid → fsgid 自动
  跟随
- `fsuid_unchanged_when_euid_unchanged()` — euid 未变 → fsuid 也不变

### 3.6 c/src/matrix.c (1 case + 4 helper)

- `setup_state()` / `read_uid()` / `read_gid()` / `waitpid_safely()`
  helper
- `matrix_one()` — (syscall × state × ruid × euid × suid × mode) 笛卡尔
  单格 — 验 rc/errno + 三槽 + fsuid 跟随

### 3.7 c/src/procfs_visibility.c (6 case + 2 helper)

- `waitpid_safely_pv()` / `parse_proc_id_line()` helper
- `proc_uid_baseline()` — 未改动时 /proc Uid: == (uid,euid,suid)
- `proc_uid_after_setresuid()` — setresuid 后 /proc Uid: 三字段精确同步
- `proc_uid_after_setresuid_nochg()` — setresuid(-1,-1,-1) 后 procfs 不变
- `proc_gid_after_setresgid()` — setresgid 后 /proc Gid: 同步
- `proc_compound_setres()` — 复合调用后 procfs 全一致
- `proc_gid_baseline()` — /proc Gid: 基线一致

### 3.8 c/src/nptl_sync.c (3 case)

- `nptl_setresuid_main_to_pthread()` — 主线程 setresuid → 子 pthread
  getresuid 看到相同 cred (man §VERSIONS C library/kernel differences)
- `nptl_setresgid_pthread_to_main()`
- `nptl_concurrent_setresuid()` — 并发 setresuid 最终收敛

### 3.9 c/src/core_dump_inhibit.c (3 case)

- `cd_baseline()` — PR_GET_DUMPABLE == 1 (man 2 prctl §PR_GET_DUMPABLE)
- `cd_setresuid_nochg()` — setresuid 三参 nochg → dumpable 不变
- `cd_setresuid_changes()` — setresuid 改 euid → PR_GET_DUMPABLE → 0

### 3.10 c/src/errno_preservation.c (5 case)

- `setresuid_nochg_errno_preserved()` /
  `setresgid_nochg_errno_preserved()` /
  `setresuid_self_errno_preserved()` /
  `setresgid_self_errno_preserved()` /
  `raw_setresuid_success_errno_preserved()`

### 3.11 c/src/main.c (聚合 entry point, 非测点)

按顺序 COLLECT 10 个模块, 每模块 `*_run()` 返 `__fail`, main 汇总
TOTAL: N fail。

## 相关 syscall man 摘录 (核心段)

`setresuid` §DESCRIPTION:
- "setresuid() sets the real user ID, the effective user ID, and the
  saved set-user-ID of the calling process."
- "An unprivileged process may change its real UID, effective UID, and
  saved set-user-ID, each to one of: the current real UID, the current
  effective UID, or the current saved set-user-ID."
- "A privileged process (on Linux, one having the CAP_SETUID capability)
  may set its real UID, effective UID, and saved set-user-ID to
  arbitrary values."
- "If one of the arguments equals -1, the corresponding value is not
  changed."
- "Regardless of what changes are made to the real UID, effective UID,
  and saved set-user-ID, the filesystem UID is always set to the same
  value as the (possibly new) effective UID."

`setresuid` §ERRORS:
- EINVAL — One or more of the target user or group IDs is not valid in
  this user namespace.
- EPERM — The calling process is not privileged and tried to change the
  IDs to values that are not permitted.

`setresuid` §HISTORY: Linux 2.1.44, glibc 2.3.2. HP-UX, FreeBSD. glibc
wrapper transparently deals with 16-bit/32-bit variations。

## 发现的 bug (本测试过程中暴露的 starry vs Linux 差异)

本 test PR 在 starry kernel 上跑时, 暴露以下 starry-vs-Linux 行为差异,
已各自登记为上游 issue, 单文件 C 复现程序在 fork repo 的 `bug-starry-*`
分支内单独维护 (路径 `test-suit/.../bugfix/bug-starry-<name>/c/src/main.c`):

- rcore-os#713 — `setuid((uid_t)-1)` 在 starry 上被错误接受
  (Linux 应返 EINVAL, 由 uid_valid 检查阻断)
- rcore-os#714 — `/proc/self/status` 在 starry loongarch64 上
  vm_write EFAULT (Uid:/Gid:/Groups: 三行均受影响)
- rcore-os#715 — `setgroups(N, list)` 之后 `/proc/self/status`
  的 Groups: 行不立即同步 (starry race)
- rcore-os#716 — apk + curl 组合在 starry loongarch64 上
  600s 超时 ~90% flake (与本测试矩阵 stress 路径相关)

上述 bug 由 rcore-os#717 (fix-uid-gid-bugs, local 修) 与
rcore-os#718 (fix-uid-gid-deep, cross-subsystem) 这两个 fix PR
分别托管 — fix PR 同步把 bug-starry-* 注册进 4 arch bugfix toml 矩阵,
CI 由 FAIL → PASS 转化作为修复验收。

## CI / 验证

rcore-os#728 当前 bucket: `pass=18 fail=0 skipping=30`。

## Matrix case 数学

### `matrix.c` — 3000 case (Group D 最大规模)

公式: `5 ruid × 5 euid × 5 suid × 6 state × 2 mode × 2 syscall = 3000`

- ruid 5: `0 / 1000 / 2000 / NOCHG / boundary`
- euid 5: 同 ruid
- suid 5: 同 ruid
- state 6: `root_unmod / 3id_distinct / unpriv / mid_priv /
  after_setresuid / NOCHG_baseline`
- mode 2: libc / raw
- syscall 2: setresuid / setresgid

为什么这么多维度:
- `setresuid(ruid, euid, suid)` 是三参数独立接口 — 与 setreuid (2 参 +
  隐式 saved 更新) 不同, setres* 每参各自独立, 必须 5^3=125 个三元组
  覆盖全部组合空间
- unpriv 每槽位 in_set 检查独立, 需 5^3 组合验
- fsuid 必须验每个 (ruid, euid, suid) 组合下都跟新 euid

5^3 不能再缩: 缩到 4^3=64 会漏掉 NOCHG/boundary corner; 5^3=125 是
最小覆盖。

每 case 4-5 assertion ≈ 12000-15000 PASS (含 fsuid procfs 验证)。

## 复现命令

切换分支:
```bash
cd <repo-root>
git checkout test-uid-gid-res-setters
```

本地 Linux (host, WSL2):
```bash
cd <repo-root>/test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-res-setters/c
rm -rf build && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug && make
sudo ./test-uid-gid-res-setters
```

starry qemu (4 arch):
```bash
source .starry-env.sh && cargo starry test qemu --arch x86_64      -c syscall
source .starry-env.sh && cargo starry test qemu --arch aarch64     -c syscall
source .starry-env.sh && cargo starry test qemu --arch riscv64     -c syscall
source .starry-env.sh && cargo starry test qemu --arch loongarch64 -c syscall
```

## 引用

- 相关 PR: stacked 前 Group A/B/C (rcore-os#725..rcore-os#727); stacked 后 Group E
  (rcore-os#729); fsuid_follow 模块依赖 fix rcore-os#717 (fix-uid-gid-bugs) 新增
  setfsuid/setfsgid 实现

Signed-off-by: Leo Cheng <chengkelfanke@gmail.com>
Lfan-ke added a commit to Lfan-ke/tgoskits that referenced this pull request May 18, 2026
## 分支用途

Group C: `setreuid` / `setregid` 两参 (real + effective) setter syscall 的 man-first 地毯式测试, 本地 913 PASS。对应 rcore-os#727。

## 测试覆盖 (每文件每函数)

测试位置: `test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-re-setters/c/src/`

模块总览 (9 模块 / 38 个 static void case 函数 / main.c 串联): `setreuid` / `setregid` / `saved_id_semantics` / `boundary` / `matrix` / `procfs_visibility` / `nptl_sync` / `core_dump_inhibit` / `errno_preservation`。

### 3.1 c/src/setreuid.c (6 case + 1 helper)

- `waitpid_safely()` helper
- `setreuid_both_nochg_idempotent()` — setreuid((uid_t)-1,(uid_t)-1) 不改 r/e/s 三字段 (man §DESCRIPTION sentinel -1)
- `setreuid_ruid_self()` — setreuid(self,-1) 后 real == 原 real
- `setreuid_euid_self()` — setreuid(-1,self) 后 effective == 原 effective
- `setreuid_root_arbitrary()` — root setreuid(r,e) 任意值成功
- `setreuid_unpriv_eperm()` — 非特权越权 (非 {ruid,euid,suid}) → EPERM (man §ERRORS EPERM)
- `setreuid_raw_matches_libc()` — 裸 syscall 与 libc 一致 (rc + 三字段)

### 3.2 c/src/setregid.c (6 case + 1 helper)

- `waitpid_safely()` helper
- `setregid_both_nochg()` / `setregid_rgid_self()` / `setregid_egid_self()` / `setregid_root_arbitrary()` / `setregid_unpriv_eperm()` / `setregid_raw_matches_libc()` — 镜像 setreuid 测试

### 3.3 c/src/saved_id_semantics.c (6 case + 1 helper)

- `waitpid_safely()` helper
- `saved_id_ruid_set_updates_suid()` — ruid != -1 → suid := new euid (D5 规则触发) (man §DESCRIPTION)
- `saved_id_euid_set_to_nonruid_updates_suid()` — euid 改成 ≠ 旧 ruid → suid 更新
- `saved_id_both_set_root()` — root 同时改 r 和 e, suid 更新到新 euid
- `saved_id_both_nochg_no_change()` — (-1,-1) → r/e/s 全无变化
- `saved_id_d5_reverse_corner()` — D5 反向 corner: euid := 旧 ruid 时 saved 不应被更新
- `saved_id_d5_reverse_corner_gid()` — D5 corner GID 侧

### 3.4 c/src/boundary.c (3 case + 1 helper)

- `waitpid_safely()` helper
- `boundary_raw_u32max_nochg()` — 裸 syscall 传 (uid_t)-1 sentinel 走 nochg 路径
- `boundary_root_extreme_values()` — root 极值 0/1000/65535/65536/ (u32max-1) (man §HISTORY 16→32-bit)
- `boundary_unpriv_extreme_eperm()` — 非特权传极值 → EPERM

### 3.5 c/src/matrix.c (1 case + 4 helper)

- `setup_state()` / `read_uid_cred()` / `read_gid_cred()` / `waitpid_safely()` helper
- `matrix_one()` — (syscall × state × ruid × euid × mode) 笛卡尔单格 — 验 rc/errno + 后续 r/e/s 三槽 + D5 saved 规则

### 3.6 c/src/procfs_visibility.c (5 case + 2 helper)

- `waitpid_safely_pv()` / `parse_proc_id_line()` helper
- `proc_uid_after_setreuid_full()` — setreuid(r,e) 全改 → /proc Uid: 三字段一致 (proc(5) §Uid + man §DESCRIPTION)
- `proc_uid_after_setreuid_nochg_r()` — setreuid(-1,e) → procfs real 不变、e 同步、s 按 D5
- `proc_uid_after_setreuid_nochg_same()` — setreuid(r,r) same-value → procfs 三字段一致
- `proc_gid_after_setregid_full()` — setregid 全改 → /proc Gid: 行同步
- `proc_compound_setre()` — 组合调用 (setreuid 再 setregid) 后 procfs 三字段全一致

### 3.7 c/src/nptl_sync.c (3 case)

- `nptl_setreuid_main_to_pthread()` — 主线程 setreuid → 子 pthread getresuid 看到相同 cred (man §VERSIONS C library/kernel differences)
- `nptl_setregid_pthread_to_main()`
- `nptl_concurrent_setreuid()`

### 3.8 c/src/core_dump_inhibit.c (3 case)

- `cd_baseline()` — 未改动时 PR_GET_DUMPABLE == 1 (man §NOTES)
- `cd_setreuid_no_euid_change()` — euid 未变 → dumpable 不变
- `cd_setreuid_changes_euid()` — euid 变化 → PR_GET_DUMPABLE → 0

### 3.9 c/src/errno_preservation.c (5 case)

- `setreuid_nochg_errno_preserved()` / `setregid_nochg_errno_preserved()` / `setreuid_self_errno_preserved()` / `setregid_self_errno_preserved()` / `raw_setreuid_success_errno_preserved()`

### 3.10 c/src/main.c (聚合 entry point, 非测点)

按顺序 COLLECT 9 个模块, 每模块 `*_run()` 返 `__fail`, main 汇总 TOTAL: N fail。

## 相关 syscall man 摘录 (核心段)

`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."
- "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." (D5 规则)

`setreuid` §ERRORS:
- EINVAL — One or more of the target user or group IDs is not valid in this user namespace.
- EPERM — The calling process is not privileged and a change other than (i) swapping effective with real, (ii) setting one to the value of the other, (iii) setting effective to saved set-user-ID was specified.

`setreuid` §HISTORY: glibc setreuid/setregid wrapper transparently deals with 16-bit/32-bit variations across kernel versions。

## 发现的 bug (本测试过程中暴露的 starry vs Linux 差异)

本 test PR 在 starry kernel 上跑时, 暴露以下 starry-vs-Linux 行为差异, 已各自登记为上游 issue, 单文件 C 复现程序在 fork repo 的 `bug-starry-*` 分支内单独维护 (路径 `test-suit/.../bugfix/bug-starry-<name>/c/src/main.c`):

- rcore-os#713 — `setuid((uid_t)-1)` 在 starry 上被错误接受 (Linux 应返 EINVAL, 由 uid_valid 检查阻断)
- rcore-os#714 — `/proc/self/status` 在 starry loongarch64 上 vm_write EFAULT (Uid:/Gid:/Groups: 三行均受影响)
- rcore-os#715 — `setgroups(N, list)` 之后 `/proc/self/status` 的 Groups: 行不立即同步 (starry race)
- rcore-os#716 — apk + curl 组合在 starry loongarch64 上 600s 超时 ~90% flake (与本测试矩阵 stress 路径相关)

上述 bug 由 rcore-os#717 (fix-uid-gid-bugs, local 修) 与 rcore-os#718 (fix-uid-gid-deep, cross-subsystem) 这两个 fix PR 分别托管 — fix PR 同步把 bug-starry-* 注册进 4 arch bugfix toml 矩阵, CI 由 FAIL → PASS 转化作为修复验收。

## CI / 验证

rcore-os#727 当前 bucket: `pass=18 fail=0 skipping=30`。

## Matrix case 数学

### `matrix.c` — 1536 case → 缩减到 864 (aarch64 timeout)

原公式: `8 ruid × 8 euid × 6 state × 2 mode × 2 syscall = 1536` 缩减后: `6 ruid × 6 euid × 6 state × 2 mode × 2 syscall = 864` (commit `5248f83fe`)

- ruid 6: `0 / 1000 / 2000 / 65535 / NOCHG / boundary`
- euid 6: 同 ruid (枚举全笛卡尔积)
- state 6: `root_unmod / 3id_distinct / unpriv / mid_priv / after_setuid / NOCHG_baseline`
- mode 2: libc / raw
- syscall 2: setreuid / setregid

为什么 (原) 这么多: man D5 saved 规则的真假组合依赖 (ruid, euid, old.ruid) 三元组关系 → 必须全笛卡尔积才能覆盖; ruid × euid 笛卡尔积是验 D5 规则的最小覆盖单元; × 6 state: EPERM 决策依赖 old cred 起点。

为什么缩减: emulated aarch64 上 fork-heavy 矩阵 1536 case 接近 5 min, 大概率 CI 超时 (timeout=600s)。缩减 8→6 保留关键 boundary (0/NOCHG/16-bit max), 减少笛卡尔积规模到 ~56%。边界覆盖仍完整。

每 case 4-5 assertion ≈ 3500-4500 PASS (864 base)。

## 复现命令

切换分支:
```bash
cd <repo-root>
git checkout test-uid-gid-re-setters
```

本地 Linux (host, WSL2):
```bash
cd <repo-root>/test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-re-setters/c
rm -rf build && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug && make
sudo ./test-uid-gid-re-setters
```

starry qemu (4 arch):
```bash
source .starry-env.sh && cargo starry test qemu --arch x86_64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch aarch64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch riscv64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch loongarch64 -c syscall
```

## 引用

- 相关 PR: stacked 前 Group A/B (rcore-os#725..rcore-os#726); stacked 后 Group D/E (rcore-os#728..rcore-os#729); 衍生 fix rcore-os#718 (fix-uid-gid-deep) PR_SET/GET_DUMPABLE 支撑 core_dump_inhibit 模块

Signed-off-by: Leo Cheng <chengkelfan@qq.com>
@Lfan-ke
Lfan-ke force-pushed the test-uid-gid-re-setters branch from 8d262ca to f6aa9ee Compare May 18, 2026 02:40
Lfan-ke added a commit to Lfan-ke/tgoskits that referenced this pull request May 18, 2026
## 分支用途

Group C: `setreuid` / `setregid` 两参 (real + effective) setter syscall 的 man-first 地毯式测试, 本地 913 PASS。对应 rcore-os#727。

## 测试覆盖 (每文件每函数)

测试位置: `test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-re-setters/c/src/`

模块总览 (9 模块 / 38 个 static void case 函数 / main.c 串联): `setreuid` / `setregid` / `saved_id_semantics` / `boundary` / `matrix` / `procfs_visibility` / `nptl_sync` / `core_dump_inhibit` / `errno_preservation`。

### 3.1 c/src/setreuid.c (6 case + 1 helper)

- `waitpid_safely()` helper
- `setreuid_both_nochg_idempotent()` — setreuid((uid_t)-1,(uid_t)-1) 不改 r/e/s 三字段 (man §DESCRIPTION sentinel -1)
- `setreuid_ruid_self()` — setreuid(self,-1) 后 real == 原 real
- `setreuid_euid_self()` — setreuid(-1,self) 后 effective == 原 effective
- `setreuid_root_arbitrary()` — root setreuid(r,e) 任意值成功
- `setreuid_unpriv_eperm()` — 非特权越权 (非 {ruid,euid,suid}) → EPERM (man §ERRORS EPERM)
- `setreuid_raw_matches_libc()` — 裸 syscall 与 libc 一致 (rc + 三字段)

### 3.2 c/src/setregid.c (6 case + 1 helper)

- `waitpid_safely()` helper
- `setregid_both_nochg()` / `setregid_rgid_self()` / `setregid_egid_self()` / `setregid_root_arbitrary()` / `setregid_unpriv_eperm()` / `setregid_raw_matches_libc()` — 镜像 setreuid 测试

### 3.3 c/src/saved_id_semantics.c (6 case + 1 helper)

- `waitpid_safely()` helper
- `saved_id_ruid_set_updates_suid()` — ruid != -1 → suid := new euid (D5 规则触发) (man §DESCRIPTION)
- `saved_id_euid_set_to_nonruid_updates_suid()` — euid 改成 ≠ 旧 ruid → suid 更新
- `saved_id_both_set_root()` — root 同时改 r 和 e, suid 更新到新 euid
- `saved_id_both_nochg_no_change()` — (-1,-1) → r/e/s 全无变化
- `saved_id_d5_reverse_corner()` — D5 反向 corner: euid := 旧 ruid 时 saved 不应被更新
- `saved_id_d5_reverse_corner_gid()` — D5 corner GID 侧

### 3.4 c/src/boundary.c (3 case + 1 helper)

- `waitpid_safely()` helper
- `boundary_raw_u32max_nochg()` — 裸 syscall 传 (uid_t)-1 sentinel 走 nochg 路径
- `boundary_root_extreme_values()` — root 极值 0/1000/65535/65536/ (u32max-1) (man §HISTORY 16→32-bit)
- `boundary_unpriv_extreme_eperm()` — 非特权传极值 → EPERM

### 3.5 c/src/matrix.c (1 case + 4 helper)

- `setup_state()` / `read_uid_cred()` / `read_gid_cred()` / `waitpid_safely()` helper
- `matrix_one()` — (syscall × state × ruid × euid × mode) 笛卡尔单格 — 验 rc/errno + 后续 r/e/s 三槽 + D5 saved 规则

### 3.6 c/src/procfs_visibility.c (5 case + 2 helper)

- `waitpid_safely_pv()` / `parse_proc_id_line()` helper
- `proc_uid_after_setreuid_full()` — setreuid(r,e) 全改 → /proc Uid: 三字段一致 (proc(5) §Uid + man §DESCRIPTION)
- `proc_uid_after_setreuid_nochg_r()` — setreuid(-1,e) → procfs real 不变、e 同步、s 按 D5
- `proc_uid_after_setreuid_nochg_same()` — setreuid(r,r) same-value → procfs 三字段一致
- `proc_gid_after_setregid_full()` — setregid 全改 → /proc Gid: 行同步
- `proc_compound_setre()` — 组合调用 (setreuid 再 setregid) 后 procfs 三字段全一致

### 3.7 c/src/nptl_sync.c (3 case)

- `nptl_setreuid_main_to_pthread()` — 主线程 setreuid → 子 pthread getresuid 看到相同 cred (man §VERSIONS C library/kernel differences)
- `nptl_setregid_pthread_to_main()`
- `nptl_concurrent_setreuid()`

### 3.8 c/src/core_dump_inhibit.c (3 case)

- `cd_baseline()` — 未改动时 PR_GET_DUMPABLE == 1 (man §NOTES)
- `cd_setreuid_no_euid_change()` — euid 未变 → dumpable 不变
- `cd_setreuid_changes_euid()` — euid 变化 → PR_GET_DUMPABLE → 0

### 3.9 c/src/errno_preservation.c (5 case)

- `setreuid_nochg_errno_preserved()` / `setregid_nochg_errno_preserved()` / `setreuid_self_errno_preserved()` / `setregid_self_errno_preserved()` / `raw_setreuid_success_errno_preserved()`

### 3.10 c/src/main.c (聚合 entry point, 非测点)

按顺序 COLLECT 9 个模块, 每模块 `*_run()` 返 `__fail`, main 汇总 TOTAL: N fail。

## 相关 syscall man 摘录 (核心段)

`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."
- "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." (D5 规则)

`setreuid` §ERRORS:
- EINVAL — One or more of the target user or group IDs is not valid in this user namespace.
- EPERM — The calling process is not privileged and a change other than (i) swapping effective with real, (ii) setting one to the value of the other, (iii) setting effective to saved set-user-ID was specified.

`setreuid` §HISTORY: glibc setreuid/setregid wrapper transparently deals with 16-bit/32-bit variations across kernel versions。

## 发现的 bug (本测试过程中暴露的 starry vs Linux 差异)

本 test PR 在 starry kernel 上跑时, 暴露以下 starry-vs-Linux 行为差异, 已各自登记为上游 issue, 单文件 C 复现程序在 fork repo 的 `bug-starry-*` 分支内单独维护 (路径 `test-suit/.../bugfix/bug-starry-<name>/c/src/main.c`):

- rcore-os#713 — `setuid((uid_t)-1)` 在 starry 上被错误接受 (Linux 应返 EINVAL, 由 uid_valid 检查阻断)
- rcore-os#714 — `/proc/self/status` 在 starry loongarch64 上 vm_write EFAULT (Uid:/Gid:/Groups: 三行均受影响)
- rcore-os#715 — `setgroups(N, list)` 之后 `/proc/self/status` 的 Groups: 行不立即同步 (starry race)
- rcore-os#716 — apk + curl 组合在 starry loongarch64 上 600s 超时 ~90% flake (与本测试矩阵 stress 路径相关)

上述 bug 由 rcore-os#717 (fix-uid-gid-bugs, local 修) 与 rcore-os#718 (fix-uid-gid-deep, cross-subsystem) 这两个 fix PR 分别托管 — fix PR 同步把 bug-starry-* 注册进 4 arch bugfix toml 矩阵, CI 由 FAIL → PASS 转化作为修复验收。

## CI / 验证

rcore-os#727 当前 bucket: `pass=18 fail=0 skipping=30`。

## Matrix case 数学

### `matrix.c` — 1536 case → 缩减到 864 (aarch64 timeout)

原公式: `8 ruid × 8 euid × 6 state × 2 mode × 2 syscall = 1536` 缩减后: `6 ruid × 6 euid × 6 state × 2 mode × 2 syscall = 864` (commit `5248f83fe`)

- ruid 6: `0 / 1000 / 2000 / 65535 / NOCHG / boundary`
- euid 6: 同 ruid (枚举全笛卡尔积)
- state 6: `root_unmod / 3id_distinct / unpriv / mid_priv / after_setuid / NOCHG_baseline`
- mode 2: libc / raw
- syscall 2: setreuid / setregid

为什么 (原) 这么多: man D5 saved 规则的真假组合依赖 (ruid, euid, old.ruid) 三元组关系 → 必须全笛卡尔积才能覆盖; ruid × euid 笛卡尔积是验 D5 规则的最小覆盖单元; × 6 state: EPERM 决策依赖 old cred 起点。

为什么缩减: emulated aarch64 上 fork-heavy 矩阵 1536 case 接近 5 min, 大概率 CI 超时 (timeout=600s)。缩减 8→6 保留关键 boundary (0/NOCHG/16-bit max), 减少笛卡尔积规模到 ~56%。边界覆盖仍完整。

每 case 4-5 assertion ≈ 3500-4500 PASS (864 base)。

## 复现命令

切换分支:
```bash
cd <repo-root>
git checkout test-uid-gid-re-setters
```

本地 Linux (host, WSL2):
```bash
cd <repo-root>/test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-re-setters/c
rm -rf build && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug && make
sudo ./test-uid-gid-re-setters
```

starry qemu (4 arch):
```bash
source .starry-env.sh && cargo starry test qemu --arch x86_64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch aarch64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch riscv64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch loongarch64 -c syscall
```

## 引用

- 相关 PR: stacked 前 Group A/B (rcore-os#725..rcore-os#726); stacked 后 Group D/E (rcore-os#728..rcore-os#729); 衍生 fix rcore-os#718 (fix-uid-gid-deep) PR_SET/GET_DUMPABLE 支撑 core_dump_inhibit 模块

Signed-off-by: Leo Cheng <chengkelfan@qq.com>
@Lfan-ke
Lfan-ke force-pushed the test-uid-gid-re-setters branch from f6aa9ee to 56c931f Compare May 18, 2026 02:41
Lfan-ke added a commit to Lfan-ke/tgoskits that referenced this pull request May 18, 2026
## 分支用途

Group D: `setresuid` / `setresgid` 三参 (real + effective + saved) setter syscall 的 man-first 地毯式测试, 本地 3052 PASS。对应 rcore-os#728。

## 测试覆盖 (每文件每函数)

测试位置: `test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-res-setters/c/src/`

模块总览 (10 模块 / 43 个 static void case 函数 / main.c 串联): `setresuid` / `setresgid` / `three_arg_independence` / `boundary` / `fsuid_follow` / `procfs_visibility` / `nptl_sync` / `core_dump_inhibit` / `matrix` / `errno_preservation`。

### 3.1 c/src/setresuid.c (6 case + 1 helper)

- `waitpid_safely()` helper
- `setresuid_all_nochg()` — setresuid(-1,-1,-1) 三字段不变 (man §DESCRIPTION sentinel -1)
- `setresuid_self_self_self()` — setresuid(self,self,self) 三字段不变
- `setresuid_root_arbitrary_three_values()` — root setresuid(r,e,s) 三个任意不同值都成功 (man §DESCRIPTION root)
- `setresuid_unpriv_within_set_ok()` — 非特权改到当前 {r,e,s} 集合内 → OK
- `setresuid_unpriv_outside_set_eperm()` — 非特权超出 {r,e,s} 集合 → EPERM (man §ERRORS EPERM)
- `setresuid_raw_matches_libc()` — 裸 syscall 与 libc 一致

### 3.2 c/src/setresgid.c (6 case + 1 helper)

- `waitpid_safely()` helper
- `setresgid_all_nochg()` / `setresgid_self_self_self()` / `setresgid_root_arbitrary_three_values()` / `setresgid_unpriv_within_set_ok()` / `setresgid_unpriv_outside_set_eperm()` / `setresgid_raw_matches_libc()`

### 3.3 c/src/three_arg_independence.c (7 case + 2 helper)

- `waitpid_safely()` helper / `test_combo()` helper
- `independence_root_only_r()` — root setresuid(NEW,-1,-1) 只改 r, e/s 保持 (man §DESCRIPTION 三参独立)
- `independence_root_only_s()` — root setresuid(-1,-1,NEW) 只改 s
- `independence_root_only_e()` — root setresuid(-1,NEW,-1) 只改 e
- `independence_all_three_distinct()` — r/e/s 三参传不同值 → 三槽各自独立赋值
- `independence_all_nochg_no_op()` — root (-1,-1,-1) 无操作不变
- `independence_all_nochg_no_op_gid()` — GID 侧 (-1,-1,-1) 无操作
- `independence_all_nochg_unpriv_ok()` — 非特权 (-1,-1,-1) 也 OK (不触发 EPERM)

### 3.4 c/src/boundary.c (3 case + 1 helper)

- `waitpid_safely()` helper
- `boundary_raw_all_u32max()` — 裸 syscall 三参全 (uid_t)-1 sentinel → nochg 路径
- `boundary_root_extreme_uids()` — root 极值 0/65535/65536/(u32max-1) (man §HISTORY 16→32-bit)
- `boundary_unpriv_one_outside_eperm()` — 非特权一个字段出集合 → EPERM

### 3.5 c/src/fsuid_follow.c (3 case + 1 helper)

- `waitpid_safely2()` helper
- `fsuid_follows_euid_after_setresuid()` — setresuid 改 euid → fsuid 自动跟随 (verify via setfsuid(-1) 读回) (man §DESCRIPTION fsuid follow euid)
- `fsgid_follows_egid_after_setresgid()` — setresgid 改 egid → fsgid 自动跟随
- `fsuid_unchanged_when_euid_unchanged()` — euid 未变 → fsuid 也不变

### 3.6 c/src/matrix.c (1 case + 4 helper)

- `setup_state()` / `read_uid()` / `read_gid()` / `waitpid_safely()` helper
- `matrix_one()` — (syscall × state × ruid × euid × suid × mode) 笛卡尔单格 — 验 rc/errno + 三槽 + fsuid 跟随

### 3.7 c/src/procfs_visibility.c (6 case + 2 helper)

- `waitpid_safely_pv()` / `parse_proc_id_line()` helper
- `proc_uid_baseline()` — 未改动时 /proc Uid: == (uid,euid,suid)
- `proc_uid_after_setresuid()` — setresuid 后 /proc Uid: 三字段精确同步
- `proc_uid_after_setresuid_nochg()` — setresuid(-1,-1,-1) 后 procfs 不变
- `proc_gid_after_setresgid()` — setresgid 后 /proc Gid: 同步
- `proc_compound_setres()` — 复合调用后 procfs 全一致
- `proc_gid_baseline()` — /proc Gid: 基线一致

### 3.8 c/src/nptl_sync.c (3 case)

- `nptl_setresuid_main_to_pthread()` — 主线程 setresuid → 子 pthread getresuid 看到相同 cred (man §VERSIONS C library/kernel differences)
- `nptl_setresgid_pthread_to_main()`
- `nptl_concurrent_setresuid()` — 并发 setresuid 最终收敛

### 3.9 c/src/core_dump_inhibit.c (3 case)

- `cd_baseline()` — PR_GET_DUMPABLE == 1 (man 2 prctl §PR_GET_DUMPABLE)
- `cd_setresuid_nochg()` — setresuid 三参 nochg → dumpable 不变
- `cd_setresuid_changes()` — setresuid 改 euid → PR_GET_DUMPABLE → 0

### 3.10 c/src/errno_preservation.c (5 case)

- `setresuid_nochg_errno_preserved()` / `setresgid_nochg_errno_preserved()` / `setresuid_self_errno_preserved()` / `setresgid_self_errno_preserved()` / `raw_setresuid_success_errno_preserved()`

### 3.11 c/src/main.c (聚合 entry point, 非测点)

按顺序 COLLECT 10 个模块, 每模块 `*_run()` 返 `__fail`, main 汇总 TOTAL: N fail。

## 相关 syscall man 摘录 (核心段)

`setresuid` §DESCRIPTION:
- "setresuid() sets the real user ID, the effective user ID, and the saved set-user-ID of the calling process."
- "An unprivileged process may change its real UID, effective UID, and saved set-user-ID, each to one of: the current real UID, the current effective UID, or the current saved set-user-ID."
- "A privileged process (on Linux, one having the CAP_SETUID capability) may set its real UID, effective UID, and saved set-user-ID to arbitrary values."
- "If one of the arguments equals -1, the corresponding value is not changed."
- "Regardless of what changes are made to the real UID, effective UID, and saved set-user-ID, the filesystem UID is always set to the same value as the (possibly new) effective UID."

`setresuid` §ERRORS:
- EINVAL — One or more of the target user or group IDs is not valid in this user namespace.
- EPERM — The calling process is not privileged and tried to change the IDs to values that are not permitted.

`setresuid` §HISTORY: Linux 2.1.44, glibc 2.3.2. HP-UX, FreeBSD. glibc wrapper transparently deals with 16-bit/32-bit variations。

## 发现的 bug (本测试过程中暴露的 starry vs Linux 差异)

本 test PR 在 starry kernel 上跑时, 暴露以下 starry-vs-Linux 行为差异, 已各自登记为上游 issue, 单文件 C 复现程序在 fork repo 的 `bug-starry-*` 分支内单独维护 (路径 `test-suit/.../bugfix/bug-starry-<name>/c/src/main.c`):

- rcore-os#713 — `setuid((uid_t)-1)` 在 starry 上被错误接受 (Linux 应返 EINVAL, 由 uid_valid 检查阻断)
- rcore-os#714 — `/proc/self/status` 在 starry loongarch64 上 vm_write EFAULT (Uid:/Gid:/Groups: 三行均受影响)
- rcore-os#715 — `setgroups(N, list)` 之后 `/proc/self/status` 的 Groups: 行不立即同步 (starry race)
- rcore-os#716 — apk + curl 组合在 starry loongarch64 上 600s 超时 ~90% flake (与本测试矩阵 stress 路径相关)

上述 bug 由 rcore-os#717 (fix-uid-gid-bugs, local 修) 与 rcore-os#718 (fix-uid-gid-deep, cross-subsystem) 这两个 fix PR 分别托管 — fix PR 同步把 bug-starry-* 注册进 4 arch bugfix toml 矩阵, CI 由 FAIL → PASS 转化作为修复验收。

## CI / 验证

rcore-os#728 当前 bucket: `pass=18 fail=0 skipping=30`。

## Matrix case 数学

### `matrix.c` — 3000 case (Group D 最大规模)

公式: `5 ruid × 5 euid × 5 suid × 6 state × 2 mode × 2 syscall = 3000`

- ruid 5: `0 / 1000 / 2000 / NOCHG / boundary`
- euid 5: 同 ruid
- suid 5: 同 ruid
- state 6: `root_unmod / 3id_distinct / unpriv / mid_priv / after_setresuid / NOCHG_baseline`
- mode 2: libc / raw
- syscall 2: setresuid / setresgid

为什么这么多维度:
- `setresuid(ruid, euid, suid)` 是三参数独立接口 — 与 setreuid (2 参 + 隐式 saved 更新) 不同, setres* 每参各自独立, 必须 5^3=125 个三元组覆盖全部组合空间
- unpriv 每槽位 in_set 检查独立, 需 5^3 组合验
- fsuid 必须验每个 (ruid, euid, suid) 组合下都跟新 euid

5^3 不能再缩: 缩到 4^3=64 会漏掉 NOCHG/boundary corner; 5^3=125 是最小覆盖。

每 case 4-5 assertion ≈ 12000-15000 PASS (含 fsuid procfs 验证)。

## 复现命令

切换分支:
```bash
cd <repo-root>
git checkout test-uid-gid-res-setters
```

本地 Linux (host, WSL2):
```bash
cd <repo-root>/test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-res-setters/c
rm -rf build && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug && make
sudo ./test-uid-gid-res-setters
```

starry qemu (4 arch):
```bash
source .starry-env.sh && cargo starry test qemu --arch x86_64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch aarch64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch riscv64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch loongarch64 -c syscall
```

## 引用

- 相关 PR: stacked 前 Group A/B/C (rcore-os#725..rcore-os#727); stacked 后 Group E (rcore-os#729); fsuid_follow 模块依赖 fix rcore-os#717 (fix-uid-gid-bugs) 新增 setfsuid/setfsgid 实现

Signed-off-by: Leo Cheng <chengkelfan@qq.com>
Lfan-ke added a commit to Lfan-ke/tgoskits that referenced this pull request May 18, 2026
## 分支用途

Group D: `setresuid` / `setresgid` 三参 (real + effective + saved) setter syscall 的 man-first 地毯式测试, 本地 3052 PASS。对应 rcore-os#728。

## 测试覆盖 (每文件每函数)

测试位置: `test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-res-setters/c/src/`

模块总览 (10 模块 / 43 个 static void case 函数 / main.c 串联): `setresuid` / `setresgid` / `three_arg_independence` / `boundary` / `fsuid_follow` / `procfs_visibility` / `nptl_sync` / `core_dump_inhibit` / `matrix` / `errno_preservation`。

### 3.1 c/src/setresuid.c (6 case + 1 helper)

- `waitpid_safely()` helper
- `setresuid_all_nochg()` — setresuid(-1,-1,-1) 三字段不变 (man §DESCRIPTION sentinel -1)
- `setresuid_self_self_self()` — setresuid(self,self,self) 三字段不变
- `setresuid_root_arbitrary_three_values()` — root setresuid(r,e,s) 三个任意不同值都成功 (man §DESCRIPTION root)
- `setresuid_unpriv_within_set_ok()` — 非特权改到当前 {r,e,s} 集合内 → OK
- `setresuid_unpriv_outside_set_eperm()` — 非特权超出 {r,e,s} 集合 → EPERM (man §ERRORS EPERM)
- `setresuid_raw_matches_libc()` — 裸 syscall 与 libc 一致

### 3.2 c/src/setresgid.c (6 case + 1 helper)

- `waitpid_safely()` helper
- `setresgid_all_nochg()` / `setresgid_self_self_self()` / `setresgid_root_arbitrary_three_values()` / `setresgid_unpriv_within_set_ok()` / `setresgid_unpriv_outside_set_eperm()` / `setresgid_raw_matches_libc()`

### 3.3 c/src/three_arg_independence.c (7 case + 2 helper)

- `waitpid_safely()` helper / `test_combo()` helper
- `independence_root_only_r()` — root setresuid(NEW,-1,-1) 只改 r, e/s 保持 (man §DESCRIPTION 三参独立)
- `independence_root_only_s()` — root setresuid(-1,-1,NEW) 只改 s
- `independence_root_only_e()` — root setresuid(-1,NEW,-1) 只改 e
- `independence_all_three_distinct()` — r/e/s 三参传不同值 → 三槽各自独立赋值
- `independence_all_nochg_no_op()` — root (-1,-1,-1) 无操作不变
- `independence_all_nochg_no_op_gid()` — GID 侧 (-1,-1,-1) 无操作
- `independence_all_nochg_unpriv_ok()` — 非特权 (-1,-1,-1) 也 OK (不触发 EPERM)

### 3.4 c/src/boundary.c (3 case + 1 helper)

- `waitpid_safely()` helper
- `boundary_raw_all_u32max()` — 裸 syscall 三参全 (uid_t)-1 sentinel → nochg 路径
- `boundary_root_extreme_uids()` — root 极值 0/65535/65536/(u32max-1) (man §HISTORY 16→32-bit)
- `boundary_unpriv_one_outside_eperm()` — 非特权一个字段出集合 → EPERM

### 3.5 c/src/fsuid_follow.c (3 case + 1 helper)

- `waitpid_safely2()` helper
- `fsuid_follows_euid_after_setresuid()` — setresuid 改 euid → fsuid 自动跟随 (verify via setfsuid(-1) 读回) (man §DESCRIPTION fsuid follow euid)
- `fsgid_follows_egid_after_setresgid()` — setresgid 改 egid → fsgid 自动跟随
- `fsuid_unchanged_when_euid_unchanged()` — euid 未变 → fsuid 也不变

### 3.6 c/src/matrix.c (1 case + 4 helper)

- `setup_state()` / `read_uid()` / `read_gid()` / `waitpid_safely()` helper
- `matrix_one()` — (syscall × state × ruid × euid × suid × mode) 笛卡尔单格 — 验 rc/errno + 三槽 + fsuid 跟随

### 3.7 c/src/procfs_visibility.c (6 case + 2 helper)

- `waitpid_safely_pv()` / `parse_proc_id_line()` helper
- `proc_uid_baseline()` — 未改动时 /proc Uid: == (uid,euid,suid)
- `proc_uid_after_setresuid()` — setresuid 后 /proc Uid: 三字段精确同步
- `proc_uid_after_setresuid_nochg()` — setresuid(-1,-1,-1) 后 procfs 不变
- `proc_gid_after_setresgid()` — setresgid 后 /proc Gid: 同步
- `proc_compound_setres()` — 复合调用后 procfs 全一致
- `proc_gid_baseline()` — /proc Gid: 基线一致

### 3.8 c/src/nptl_sync.c (3 case)

- `nptl_setresuid_main_to_pthread()` — 主线程 setresuid → 子 pthread getresuid 看到相同 cred (man §VERSIONS C library/kernel differences)
- `nptl_setresgid_pthread_to_main()`
- `nptl_concurrent_setresuid()` — 并发 setresuid 最终收敛

### 3.9 c/src/core_dump_inhibit.c (3 case)

- `cd_baseline()` — PR_GET_DUMPABLE == 1 (man 2 prctl §PR_GET_DUMPABLE)
- `cd_setresuid_nochg()` — setresuid 三参 nochg → dumpable 不变
- `cd_setresuid_changes()` — setresuid 改 euid → PR_GET_DUMPABLE → 0

### 3.10 c/src/errno_preservation.c (5 case)

- `setresuid_nochg_errno_preserved()` / `setresgid_nochg_errno_preserved()` / `setresuid_self_errno_preserved()` / `setresgid_self_errno_preserved()` / `raw_setresuid_success_errno_preserved()`

### 3.11 c/src/main.c (聚合 entry point, 非测点)

按顺序 COLLECT 10 个模块, 每模块 `*_run()` 返 `__fail`, main 汇总 TOTAL: N fail。

## 相关 syscall man 摘录 (核心段)

`setresuid` §DESCRIPTION:
- "setresuid() sets the real user ID, the effective user ID, and the saved set-user-ID of the calling process."
- "An unprivileged process may change its real UID, effective UID, and saved set-user-ID, each to one of: the current real UID, the current effective UID, or the current saved set-user-ID."
- "A privileged process (on Linux, one having the CAP_SETUID capability) may set its real UID, effective UID, and saved set-user-ID to arbitrary values."
- "If one of the arguments equals -1, the corresponding value is not changed."
- "Regardless of what changes are made to the real UID, effective UID, and saved set-user-ID, the filesystem UID is always set to the same value as the (possibly new) effective UID."

`setresuid` §ERRORS:
- EINVAL — One or more of the target user or group IDs is not valid in this user namespace.
- EPERM — The calling process is not privileged and tried to change the IDs to values that are not permitted.

`setresuid` §HISTORY: Linux 2.1.44, glibc 2.3.2. HP-UX, FreeBSD. glibc wrapper transparently deals with 16-bit/32-bit variations。

## 发现的 bug (本测试过程中暴露的 starry vs Linux 差异)

本 test PR 在 starry kernel 上跑时, 暴露以下 starry-vs-Linux 行为差异, 已各自登记为上游 issue, 单文件 C 复现程序在 fork repo 的 `bug-starry-*` 分支内单独维护 (路径 `test-suit/.../bugfix/bug-starry-<name>/c/src/main.c`):

- rcore-os#713 — `setuid((uid_t)-1)` 在 starry 上被错误接受 (Linux 应返 EINVAL, 由 uid_valid 检查阻断)
- rcore-os#714 — `/proc/self/status` 在 starry loongarch64 上 vm_write EFAULT (Uid:/Gid:/Groups: 三行均受影响)
- rcore-os#715 — `setgroups(N, list)` 之后 `/proc/self/status` 的 Groups: 行不立即同步 (starry race)
- rcore-os#716 — apk + curl 组合在 starry loongarch64 上 600s 超时 ~90% flake (与本测试矩阵 stress 路径相关)

上述 bug 由 rcore-os#717 (fix-uid-gid-bugs, local 修) 与 rcore-os#718 (fix-uid-gid-deep, cross-subsystem) 这两个 fix PR 分别托管 — fix PR 同步把 bug-starry-* 注册进 4 arch bugfix toml 矩阵, CI 由 FAIL → PASS 转化作为修复验收。

## CI / 验证

rcore-os#728 当前 bucket: `pass=18 fail=0 skipping=30`。

## Matrix case 数学

### `matrix.c` — 3000 case (Group D 最大规模)

公式: `5 ruid × 5 euid × 5 suid × 6 state × 2 mode × 2 syscall = 3000`

- ruid 5: `0 / 1000 / 2000 / NOCHG / boundary`
- euid 5: 同 ruid
- suid 5: 同 ruid
- state 6: `root_unmod / 3id_distinct / unpriv / mid_priv / after_setresuid / NOCHG_baseline`
- mode 2: libc / raw
- syscall 2: setresuid / setresgid

为什么这么多维度:
- `setresuid(ruid, euid, suid)` 是三参数独立接口 — 与 setreuid (2 参 + 隐式 saved 更新) 不同, setres* 每参各自独立, 必须 5^3=125 个三元组覆盖全部组合空间
- unpriv 每槽位 in_set 检查独立, 需 5^3 组合验
- fsuid 必须验每个 (ruid, euid, suid) 组合下都跟新 euid

5^3 不能再缩: 缩到 4^3=64 会漏掉 NOCHG/boundary corner; 5^3=125 是最小覆盖。

每 case 4-5 assertion ≈ 12000-15000 PASS (含 fsuid procfs 验证)。

## 复现命令

切换分支:
```bash
cd <repo-root>
git checkout test-uid-gid-res-setters
```

本地 Linux (host, WSL2):
```bash
cd <repo-root>/test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-res-setters/c
rm -rf build && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug && make
sudo ./test-uid-gid-res-setters
```

starry qemu (4 arch):
```bash
source .starry-env.sh && cargo starry test qemu --arch x86_64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch aarch64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch riscv64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch loongarch64 -c syscall
```

## 引用

- 相关 PR: stacked 前 Group A/B/C (rcore-os#725..rcore-os#727); stacked 后 Group E (rcore-os#729); fsuid_follow 模块依赖 fix rcore-os#717 (fix-uid-gid-bugs) 新增 setfsuid/setfsgid 实现

Signed-off-by: Leo Cheng <chengkelfan@qq.com>
Lfan-ke added a commit to Lfan-ke/tgoskits that referenced this pull request May 18, 2026
## 分支用途

Group B: `setuid` / `setgid` 两个直接 setter syscall 的 man-first 地毯式测试, 本地 477 PASS。对应 rcore-os#726。

## 测试覆盖 (每文件每函数)

测试位置: `test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-direct-setters/c/src/`

模块总览 (10 模块 / 46 个 static void case 函数 / main.c 串联): `setuid` / `setgid` / `cross_root_unprivileged` / `boundary` / `matrix` / `procfs_visibility` / `nptl_sync` / `core_dump_inhibit` / `uid32_no_trunc` / `errno_preservation`。

### 3.1 c/src/setuid.c (10 case + 1 helper)

- `waitpid_safely()` helper — 循环 waitpid 处理 EINTR
- `setuid_idempotent_to_self()` — setuid(geteuid()) 后三字段 (r,e,s) 不变 (man §DESCRIPTION POSIX_SAVED_IDS)
- `setuid_root_sets_all_three()` — root setuid(target) → r/e/s 全被设为 target (man §DESCRIPTION root path)
- `setuid_returns_zero_on_success()` — 成功 rc == 0 (不只是 ≥0)
- `setuid_raw_syscall_matches_libc()` — 裸 syscall 与 libc 行为一致
- `setuid_unprivileged_eperm_in_child()` — 非特权 setuid 在 fork 子 → EPERM (man §ERRORS EPERM)
- `setuid_raw_vs_libc_failed_path()` — EPERM 失败路径下 raw rc/errno 与 libc 严格相同
- `setuid_irreversibility()` — root setuid(non-zero) drop 后无法 setuid(0) 回去 (man §DESCRIPTION 不可逆)
- `setuid_fork_child_independent()` — fork 子 setuid 改动不污染父 cred
- `setuid_self_euid_no_flush()` — 非特权 setuid(euid) 不清除 saved-set-uid
- `setuid_einval_uid_minus_one()` — uid=(uid_t)-1 在非特权下 → EPERM/ EINVAL (软容忍 starry bug B)

### 3.2 c/src/setgid.c (10 case + 1 helper)

- `waitpid_safely()` helper
- `setgid_idempotent_to_self()` — setgid(getegid()) 三字段不变
- `setgid_root_sets_all_three()` — root setgid(target) → r/e/s GID 全置
- `setgid_returns_zero_on_success()`
- `setgid_raw_syscall_matches_libc()`
- `setgid_unprivileged_eperm_in_child()` — 非特权 setgid(任意) → EPERM
- `setgid_raw_vs_libc_failed_path()`
- `setgid_alone_not_irreversible_caps_intact()` — 仅 setgid drop 不丢 CAP_SETUID, 仍可 setuid
- `setgid_irreversible_after_setuid_drop()` — setuid drop 后 setgid 不可逆 (CAP_SETGID 丢)
- `setgid_fork_child_independent()`
- `setgid_self_egid_no_change()` — setgid(egid) 不动 saved-set-gid

### 3.3 c/src/cross_root_unprivileged.c (3 case + 1 helper)

- `waitpid_safely()` helper
- `root_setuid_arbitrary_updates_all()` — root setuid(任意值) → r/e/s 全跟
- `unprivileged_setuid_self_succeeds()` — 非特权 setuid(self) 成功且字段不变
- `unprivileged_setuid_arbitrary_eperm()` — 非特权 setuid(非 {r,e,s}) → EPERM

### 3.4 c/src/boundary.c (3 case)

- `setuid_boundary_values_root()` — root 路径下边界 uid: 0/1/65535/65536/0x7FFFFFFE 全 OK
- `setuid_u32max_sentinel_unprivileged()` — 非特权 setuid((uid_t)-1) sentinel → EPERM/EINVAL (man §ERRORS EINVAL)
- `setuid_raw_syscall_returns_negative_errno()` — 裸 syscall 失败时 rc 为 `-EPERM` (不是 -1+errno)

### 3.5 c/src/matrix.c (1 case + 4 helper)

- `read_uid_cred()` / `read_gid_cred()` / `setup_state()` / `waitpid_safely()` helper
- `matrix_one()` — (syscall × state × input × mode) 笛卡尔单格 — 验 rc/errno + getresuid 后续三槽

### 3.6 c/src/procfs_visibility.c (5 case + 2 helper)

- `waitpid_safely_pv()` / `parse_proc_id_line()` helper
- `procfs_uid_baseline_root()` — 未改动时 /proc Uid: == (uid,euid,suid) 一致 (proc(5) §Uid)
- `procfs_uid_after_setuid_root()` — root setuid 后 procfs 三字段同步
- `procfs_uid_after_setuid_drop()` — drop 后 procfs Uid: 三字段一致更新
- `procfs_gid_after_setgid()` — setgid 后 procfs Gid: 行同步
- `procfs_uid_after_setresuid()` — setresuid 后 procfs 三字段精确同步

### 3.7 c/src/nptl_sync.c (3 case)

- `nptl_setuid_main_visible_to_pthread()` — 主线程 setuid → 子 pthread getresuid 看到相同 cred (man §VERSIONS C library/kernel differences)
- `nptl_setgid_pthread_visible_to_main()`
- `nptl_concurrent_setuid_converges()`

### 3.8 c/src/core_dump_inhibit.c (3 case + 1 helper)

- `waitpid_safely_cd()` helper
- `core_dump_baseline()` — 未改动时 PR_GET_DUMPABLE == 1 (man §NOTES)
- `core_dump_setuid_no_euid_change()` — setuid(euid) euid 未变 → dumpable 不变
- `core_dump_setuid_changes_euid()` — setuid 改变 euid → PR_GET_DUMPABLE → 0 (禁 core)

### 3.9 c/src/uid32_no_trunc.c (3 case + 1 helper)

- `waitpid_safely()` helper
- `setuid_32bit_no_truncate()` — child setuid 大 UID (>16-bit) 后 getresuid 不被截 (man §HISTORY 16→32-bit)
- `setgid_32bit_no_truncate()`
- `setuid_32bit_via_raw_syscall()`

### 3.10 c/src/errno_preservation.c (5 case)

- `setuid_self_errno_preserved()` / `setgid_self_errno_preserved()` / `setuid_root_errno_preserved()` / `setgid_root_errno_preserved()` / `raw_setuid_success_errno_preserved()` — 成功路径不动预置 errno

### 3.11 c/src/main.c (聚合 entry point, 非测点)

按顺序 COLLECT 10 个模块, 每模块 `*_run()` 返 `__fail`, main 汇总 TOTAL: N fail 收尾。

## 相关 syscall man 摘录 (核心段)

`setuid` §DESCRIPTION: "setuid() sets the effective user ID of the calling process. If the calling process is privileged (more precisely: if the process has the CAP_SETUID capability in its user namespace), the real UID and saved set-user-ID are also set."

`setuid` §ERRORS:
- EAGAIN — temporary failure allocating kernel data structures
- EINVAL — The user ID specified in uid is not valid in this user namespace
- EPERM — The user is not privileged and uid does not match the real UID or saved set-user-ID of the calling process

`setuid` §NOTES: "If uid is different from the old effective UID, the process will be forbidden from leaving core dumps."

`setuid` §VERSIONS (C library/kernel differences): NPTL wrapper 使用 signal-based 技术保证 process-wide thread cred 同步。

`setgid` §ERRORS: EINVAL (gid 不合法) / EPERM (无 CAP_SETGID 且 gid 不匹配 real GID 或 saved set-group-ID)。

## 发现的 bug (本测试过程中暴露的 starry vs Linux 差异)

本 test PR 在 starry kernel 上跑时, 暴露以下 starry-vs-Linux 行为差异, 已各自登记为上游 issue, 单文件 C 复现程序在 fork repo 的 `bug-starry-*` 分支内单独维护 (路径 `test-suit/.../bugfix/bug-starry-<name>/c/src/main.c`):

- rcore-os#713 — `setuid((uid_t)-1)` 在 starry 上被错误接受 (Linux 应返 EINVAL, 由 uid_valid 检查阻断)
- rcore-os#714 — `/proc/self/status` 在 starry loongarch64 上 vm_write EFAULT (Uid:/Gid:/Groups: 三行均受影响)
- rcore-os#715 — `setgroups(N, list)` 之后 `/proc/self/status` 的 Groups: 行不立即同步 (starry race)
- rcore-os#716 — apk + curl 组合在 starry loongarch64 上 600s 超时 ~90% flake (与本测试矩阵 stress 路径相关)

上述 bug 由 rcore-os#717 (fix-uid-gid-bugs, local 修) 与 rcore-os#718 (fix-uid-gid-deep, cross-subsystem) 这两个 fix PR 分别托管 — fix PR 同步把 bug-starry-* 注册进 4 arch bugfix toml 矩阵, CI 由 FAIL → PASS 转化作为修复验收。

## CI / 验证

rcore-os#726 当前 bucket: `pass=18 fail=0 skipping=30`。

## Matrix case 数学

### `matrix.c` — 448 base case (×3-5 assertion ≈ 1500-2000 PASS)

公式: `2 syscall × 7 state × 16 input × 2 mode = 448`

- syscall 2: `setuid` / `setgid`
- state 7: `root_unmod` / `after_setresuid(0,1000,2000)` / `after_setresuid(0,0,1000)` / `unpriv(1000)` / `unpriv_3id(1000,2000,3000)` / `after_setgid(1000)` / `mixed_uid_gid`
- input 16: 0 / 1 / 100 / 999 / 1000 / 2000 / 3000 / 65535 / 65536 / 100001 / 0x7FFFFFFE / NOCHG((uid_t)-1) / current_uid / current_euid / current_suid / boundary
- mode 2: libc / raw

为什么这么多维度:
- input 16 值: man §ERRORS EINVAL 触发只在 `(uid_t)-1`; EPERM 触发依赖 input 是否在 {uid, euid, suid}; root 接受任意值 — 16 值覆盖 small / large / boundary (16-bit / 32-bit) / sentinel / self-set / cross-set
- state 7: 同一 `setuid(input)` 在不同 cred 起点上行为不同 — 覆盖 man §EPERM 决策树各分支
- mode 2: libc/raw 分离测验 ABI 一致性

每 case 3-5 assertion: rc / errno / 后续 getresuid r/e/s 三槽各验 / fsuid procfs 检查 → 单 case 平均 4 assert → 总 ~1800 PASS。

## 复现命令

切换分支:
```bash
cd <repo-root>
git checkout test-uid-gid-direct-setters
```

本地 Linux (host, WSL2):
```bash
cd <repo-root>/test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-direct-setters/c
rm -rf build && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug && make
sudo ./test-uid-gid-direct-setters
```

starry qemu (4 arch):
```bash
source .starry-env.sh && cargo starry test qemu --arch x86_64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch aarch64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch riscv64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch loongarch64 -c syscall
```

## 引用

- 相关 PR: stacked 前 Group A (rcore-os#725); stacked 后 Group C/D/E (rcore-os#727..rcore-os#729); 衍生 bug-* rcore-os#713 (setuid/setgid no uid_valid), fix rcore-os#717 (fix-uid-gid-bugs) 修复该 bug

Signed-off-by: Leo Cheng <chengkelfan@qq.com>

@mai-team-app mai-team-app Bot left a comment

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.

🔍 PR #727 Review — test(syscall): test-uid-gid-re-setters 913 PASS

📋 总览

本 PR 纯新增测试代码(16 个文件,+1612 行,0 删除),为 setreuid / setregid 两参 syscall 提供 man-first 地毯式测试,9 模块 / 38+ 测试函数 / 913+ 断言。CI 在 4 架构(x86_64、aarch64、riscv64、loongarch64)toml 中注册新测试命令。

✅ 代码质量与正确性

  1. 测试框架(test_framework.h):简洁的宏驱动框架,CHECK/CHECK_ERR/CHECK_OR_BUG 覆盖硬断言、errno 检查和已知 starry bug 软断言。设计合理。
  2. main.c COLLECT 宏:防御性处理可疑返回值(rc < 0 || rc > 1000000),9 模块串行收集,fail 累加。逻辑正确。
  3. setreuid.c / setregid.c:6 个 case 各自覆盖 NOCHG sentinel、self-set、root 任意值、非特权 EPERM、raw vs libc 一致性。fork-waitpid 隔离安全。
  4. saved_id_semantics.c:D5 saved-set-uid 自动更新规则测试覆盖完整,特别是 D5 反向 corner casesetreuid(-1, old_ruid) 时 saved 不应更新),这是最容易被实现遗漏的语义。
  5. matrix.c:864 case 笛卡尔积(6×6×6×2×2),derive() 函数直接从 man page 规则推导期望值。从 1536 缩减到 864 保留了关键边界覆盖。设计正确。
  6. procfs_visibility.c:验证 /proc/self/status 的 Uid:/Gid: 行同步反映 setreuid/setregid 后的状态。parse_proc_id_line() 使用 sscanf 解析安全。
  7. nptl_sync.c:验证 pthread 间 credential 同步。使用 _Atomic 变量 + usleep() 轮询,对 starry 非 NPTL 实现优雅降级(KNOWN-STARRY-LIMITATION)。
  8. core_dump_inhibit.c:验证 euid 变化后 PR_GET_DUMPABLE 降为 0。对 prctl 不支持的情况有 fallback。
  9. errno_preservation.c:验证成功路径不误改 errno,这是 starry 常见的 vm_write/cred update 残值 bug 类型。

🔒 死锁分析

无死锁风险。逐模块分析:

  • 所有模块:使用 fork() + waitpid() 模式,父子进程间无 IPC、无共享锁、无信号量依赖。
  • nptl_sync.cfork() → 子进程内 pthread_create() + atomic 变量 + usleep() 轮询 + pthread_join()无 mutex/condvar 依赖链,不存在 lock ordering 问题。observer 线程通过 atomic_load(&main_done) 轮询退出,主线程在 setreuid() 完成后才设 flag,再 pthread_join() 等待。顺序明确,不会自死锁。
  • matrix.c:864 个独立 fork-waitpid 子进程,每个 case 完全隔离,无跨进程通信。
  • procfs_visibility.c:每个子进程独立读 /proc/self/status,无并发写竞争。

📊 对既有功能的影响

零影响。本 PR 是纯新增:

  • 不修改任何现有源代码、构建配置或 Cargo.toml
  • 4 个 qemu-*.toml 仅在 test_commands 数组中追加一行新命令,不改变现有测试的顺序或配置
  • CMakeLists.txt 是独立的,不影响其他测试目标的构建
  • cargo fmt --check 通过(本 PR 无 Rust 代码变更)

🔍 CI 集成

  • 4 架构 toml 一致性验证通过:x86_64、aarch64、riscv64、loongarch64 均注册了 /usr/bin/test-uid-gid-re-setters
  • toml 中 success_regex / fail_regex 在 suite 级别定义,测试二进制通过 exit code (0=pass, 1=fail) 报告结果,与框架兼容
  • timeout=300s,matrix 864 case 在 aarch64 上约 3 分钟(已从 1536 优化),不超时

📝 小建议(不阻塞)

  1. waitpid_safely() 在 8 个文件中重复定义(均为 static)。当前自包含设计可接受,但如果后续模块继续增长,可考虑提取到 test_framework.h 中。
  2. Commit 信息详尽且 GPG 签名验证通过,Signed-off-by 规范。

结论

APPROVE — 高质量的 man-first 测试套件,覆盖面广(9 模块 913 断言),代码结构清晰,注释充分,无死锁风险,零影响现有功能。D5 saved-set-uid 反向 corner case 和 matrix 笛卡尔积测试设计尤为出色。

Powered by mimo-v2.5-pro

Lfan-ke added a commit to Lfan-ke/tgoskits that referenced this pull request May 18, 2026
## 分支用途

Group D: `setresuid` / `setresgid` 三参 (real + effective + saved) setter syscall 的 man-first 地毯式测试, 本地 3052 PASS。对应 rcore-os#728。

## 测试覆盖 (每文件每函数)

测试位置: `test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-res-setters/c/src/`

模块总览 (10 模块 / 43 个 static void case 函数 / main.c 串联): `setresuid` / `setresgid` / `three_arg_independence` / `boundary` / `fsuid_follow` / `procfs_visibility` / `nptl_sync` / `core_dump_inhibit` / `matrix` / `errno_preservation`。

### 3.1 c/src/setresuid.c (6 case + 1 helper)

- `waitpid_safely()` helper
- `setresuid_all_nochg()` — setresuid(-1,-1,-1) 三字段不变 (man §DESCRIPTION sentinel -1)
- `setresuid_self_self_self()` — setresuid(self,self,self) 三字段不变
- `setresuid_root_arbitrary_three_values()` — root setresuid(r,e,s) 三个任意不同值都成功 (man §DESCRIPTION root)
- `setresuid_unpriv_within_set_ok()` — 非特权改到当前 {r,e,s} 集合内 → OK
- `setresuid_unpriv_outside_set_eperm()` — 非特权超出 {r,e,s} 集合 → EPERM (man §ERRORS EPERM)
- `setresuid_raw_matches_libc()` — 裸 syscall 与 libc 一致

### 3.2 c/src/setresgid.c (6 case + 1 helper)

- `waitpid_safely()` helper
- `setresgid_all_nochg()` / `setresgid_self_self_self()` / `setresgid_root_arbitrary_three_values()` / `setresgid_unpriv_within_set_ok()` / `setresgid_unpriv_outside_set_eperm()` / `setresgid_raw_matches_libc()`

### 3.3 c/src/three_arg_independence.c (7 case + 2 helper)

- `waitpid_safely()` helper / `test_combo()` helper
- `independence_root_only_r()` — root setresuid(NEW,-1,-1) 只改 r, e/s 保持 (man §DESCRIPTION 三参独立)
- `independence_root_only_s()` — root setresuid(-1,-1,NEW) 只改 s
- `independence_root_only_e()` — root setresuid(-1,NEW,-1) 只改 e
- `independence_all_three_distinct()` — r/e/s 三参传不同值 → 三槽各自独立赋值
- `independence_all_nochg_no_op()` — root (-1,-1,-1) 无操作不变
- `independence_all_nochg_no_op_gid()` — GID 侧 (-1,-1,-1) 无操作
- `independence_all_nochg_unpriv_ok()` — 非特权 (-1,-1,-1) 也 OK (不触发 EPERM)

### 3.4 c/src/boundary.c (3 case + 1 helper)

- `waitpid_safely()` helper
- `boundary_raw_all_u32max()` — 裸 syscall 三参全 (uid_t)-1 sentinel → nochg 路径
- `boundary_root_extreme_uids()` — root 极值 0/65535/65536/(u32max-1) (man §HISTORY 16→32-bit)
- `boundary_unpriv_one_outside_eperm()` — 非特权一个字段出集合 → EPERM

### 3.5 c/src/fsuid_follow.c (3 case + 1 helper)

- `waitpid_safely2()` helper
- `fsuid_follows_euid_after_setresuid()` — setresuid 改 euid → fsuid 自动跟随 (verify via setfsuid(-1) 读回) (man §DESCRIPTION fsuid follow euid)
- `fsgid_follows_egid_after_setresgid()` — setresgid 改 egid → fsgid 自动跟随
- `fsuid_unchanged_when_euid_unchanged()` — euid 未变 → fsuid 也不变

### 3.6 c/src/matrix.c (1 case + 4 helper)

- `setup_state()` / `read_uid()` / `read_gid()` / `waitpid_safely()` helper
- `matrix_one()` — (syscall × state × ruid × euid × suid × mode) 笛卡尔单格 — 验 rc/errno + 三槽 + fsuid 跟随

### 3.7 c/src/procfs_visibility.c (6 case + 2 helper)

- `waitpid_safely_pv()` / `parse_proc_id_line()` helper
- `proc_uid_baseline()` — 未改动时 /proc Uid: == (uid,euid,suid)
- `proc_uid_after_setresuid()` — setresuid 后 /proc Uid: 三字段精确同步
- `proc_uid_after_setresuid_nochg()` — setresuid(-1,-1,-1) 后 procfs 不变
- `proc_gid_after_setresgid()` — setresgid 后 /proc Gid: 同步
- `proc_compound_setres()` — 复合调用后 procfs 全一致
- `proc_gid_baseline()` — /proc Gid: 基线一致

### 3.8 c/src/nptl_sync.c (3 case)

- `nptl_setresuid_main_to_pthread()` — 主线程 setresuid → 子 pthread getresuid 看到相同 cred (man §VERSIONS C library/kernel differences)
- `nptl_setresgid_pthread_to_main()`
- `nptl_concurrent_setresuid()` — 并发 setresuid 最终收敛

### 3.9 c/src/core_dump_inhibit.c (3 case)

- `cd_baseline()` — PR_GET_DUMPABLE == 1 (man 2 prctl §PR_GET_DUMPABLE)
- `cd_setresuid_nochg()` — setresuid 三参 nochg → dumpable 不变
- `cd_setresuid_changes()` — setresuid 改 euid → PR_GET_DUMPABLE → 0

### 3.10 c/src/errno_preservation.c (5 case)

- `setresuid_nochg_errno_preserved()` / `setresgid_nochg_errno_preserved()` / `setresuid_self_errno_preserved()` / `setresgid_self_errno_preserved()` / `raw_setresuid_success_errno_preserved()`

### 3.11 c/src/main.c (聚合 entry point, 非测点)

按顺序 COLLECT 10 个模块, 每模块 `*_run()` 返 `__fail`, main 汇总 TOTAL: N fail。

## 相关 syscall man 摘录 (核心段)

`setresuid` §DESCRIPTION:
- "setresuid() sets the real user ID, the effective user ID, and the saved set-user-ID of the calling process."
- "An unprivileged process may change its real UID, effective UID, and saved set-user-ID, each to one of: the current real UID, the current effective UID, or the current saved set-user-ID."
- "A privileged process (on Linux, one having the CAP_SETUID capability) may set its real UID, effective UID, and saved set-user-ID to arbitrary values."
- "If one of the arguments equals -1, the corresponding value is not changed."
- "Regardless of what changes are made to the real UID, effective UID, and saved set-user-ID, the filesystem UID is always set to the same value as the (possibly new) effective UID."

`setresuid` §ERRORS:
- EINVAL — One or more of the target user or group IDs is not valid in this user namespace.
- EPERM — The calling process is not privileged and tried to change the IDs to values that are not permitted.

`setresuid` §HISTORY: Linux 2.1.44, glibc 2.3.2. HP-UX, FreeBSD. glibc wrapper transparently deals with 16-bit/32-bit variations。

## 发现的 bug (本测试过程中暴露的 starry vs Linux 差异)

本 test PR 在 starry kernel 上跑时, 暴露以下 starry-vs-Linux 行为差异, 已各自登记为上游 issue, 单文件 C 复现程序在 fork repo 的 `bug-starry-*` 分支内单独维护 (路径 `test-suit/.../bugfix/bug-starry-<name>/c/src/main.c`):

- rcore-os#713 — `setuid((uid_t)-1)` 在 starry 上被错误接受 (Linux 应返 EINVAL, 由 uid_valid 检查阻断)
- rcore-os#714 — `/proc/self/status` 在 starry loongarch64 上 vm_write EFAULT (Uid:/Gid:/Groups: 三行均受影响)
- rcore-os#715 — `setgroups(N, list)` 之后 `/proc/self/status` 的 Groups: 行不立即同步 (starry race)
- rcore-os#716 — apk + curl 组合在 starry loongarch64 上 600s 超时 ~90% flake (与本测试矩阵 stress 路径相关)

上述 bug 由 rcore-os#717 (fix-uid-gid-bugs, local 修) 与 rcore-os#718 (fix-uid-gid-deep, cross-subsystem) 这两个 fix PR 分别托管 — fix PR 同步把 bug-starry-* 注册进 4 arch bugfix toml 矩阵, CI 由 FAIL → PASS 转化作为修复验收。

## CI / 验证

rcore-os#728 当前 bucket: `pass=18 fail=0 skipping=30`。

## Matrix case 数学

### `matrix.c` — 3000 case (Group D 最大规模)

公式: `5 ruid × 5 euid × 5 suid × 6 state × 2 mode × 2 syscall = 3000`

- ruid 5: `0 / 1000 / 2000 / NOCHG / boundary`
- euid 5: 同 ruid
- suid 5: 同 ruid
- state 6: `root_unmod / 3id_distinct / unpriv / mid_priv / after_setresuid / NOCHG_baseline`
- mode 2: libc / raw
- syscall 2: setresuid / setresgid

为什么这么多维度:
- `setresuid(ruid, euid, suid)` 是三参数独立接口 — 与 setreuid (2 参 + 隐式 saved 更新) 不同, setres* 每参各自独立, 必须 5^3=125 个三元组覆盖全部组合空间
- unpriv 每槽位 in_set 检查独立, 需 5^3 组合验
- fsuid 必须验每个 (ruid, euid, suid) 组合下都跟新 euid

5^3 不能再缩: 缩到 4^3=64 会漏掉 NOCHG/boundary corner; 5^3=125 是最小覆盖。

每 case 4-5 assertion ≈ 12000-15000 PASS (含 fsuid procfs 验证)。

## 复现命令

切换分支:
```bash
cd <repo-root>
git checkout test-uid-gid-res-setters
```

本地 Linux (host, WSL2):
```bash
cd <repo-root>/test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-res-setters/c
rm -rf build && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug && make
sudo ./test-uid-gid-res-setters
```

starry qemu (4 arch):
```bash
source .starry-env.sh && cargo starry test qemu --arch x86_64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch aarch64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch riscv64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch loongarch64 -c syscall
```

## 引用

- 相关 PR: stacked 前 Group A/B/C (rcore-os#725..rcore-os#727); stacked 后 Group E (rcore-os#729); fsuid_follow 模块依赖 fix rcore-os#717 (fix-uid-gid-bugs) 新增 setfsuid/setfsgid 实现

Signed-off-by: Leo Cheng <chengkelfan@qq.com>
ZR233 pushed a commit that referenced this pull request May 18, 2026
## 分支用途

Group D: `setresuid` / `setresgid` 三参 (real + effective + saved) setter syscall 的 man-first 地毯式测试, 本地 3052 PASS。对应 #728。

## 测试覆盖 (每文件每函数)

测试位置: `test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-res-setters/c/src/`

模块总览 (10 模块 / 43 个 static void case 函数 / main.c 串联): `setresuid` / `setresgid` / `three_arg_independence` / `boundary` / `fsuid_follow` / `procfs_visibility` / `nptl_sync` / `core_dump_inhibit` / `matrix` / `errno_preservation`。

### 3.1 c/src/setresuid.c (6 case + 1 helper)

- `waitpid_safely()` helper
- `setresuid_all_nochg()` — setresuid(-1,-1,-1) 三字段不变 (man §DESCRIPTION sentinel -1)
- `setresuid_self_self_self()` — setresuid(self,self,self) 三字段不变
- `setresuid_root_arbitrary_three_values()` — root setresuid(r,e,s) 三个任意不同值都成功 (man §DESCRIPTION root)
- `setresuid_unpriv_within_set_ok()` — 非特权改到当前 {r,e,s} 集合内 → OK
- `setresuid_unpriv_outside_set_eperm()` — 非特权超出 {r,e,s} 集合 → EPERM (man §ERRORS EPERM)
- `setresuid_raw_matches_libc()` — 裸 syscall 与 libc 一致

### 3.2 c/src/setresgid.c (6 case + 1 helper)

- `waitpid_safely()` helper
- `setresgid_all_nochg()` / `setresgid_self_self_self()` / `setresgid_root_arbitrary_three_values()` / `setresgid_unpriv_within_set_ok()` / `setresgid_unpriv_outside_set_eperm()` / `setresgid_raw_matches_libc()`

### 3.3 c/src/three_arg_independence.c (7 case + 2 helper)

- `waitpid_safely()` helper / `test_combo()` helper
- `independence_root_only_r()` — root setresuid(NEW,-1,-1) 只改 r, e/s 保持 (man §DESCRIPTION 三参独立)
- `independence_root_only_s()` — root setresuid(-1,-1,NEW) 只改 s
- `independence_root_only_e()` — root setresuid(-1,NEW,-1) 只改 e
- `independence_all_three_distinct()` — r/e/s 三参传不同值 → 三槽各自独立赋值
- `independence_all_nochg_no_op()` — root (-1,-1,-1) 无操作不变
- `independence_all_nochg_no_op_gid()` — GID 侧 (-1,-1,-1) 无操作
- `independence_all_nochg_unpriv_ok()` — 非特权 (-1,-1,-1) 也 OK (不触发 EPERM)

### 3.4 c/src/boundary.c (3 case + 1 helper)

- `waitpid_safely()` helper
- `boundary_raw_all_u32max()` — 裸 syscall 三参全 (uid_t)-1 sentinel → nochg 路径
- `boundary_root_extreme_uids()` — root 极值 0/65535/65536/(u32max-1) (man §HISTORY 16→32-bit)
- `boundary_unpriv_one_outside_eperm()` — 非特权一个字段出集合 → EPERM

### 3.5 c/src/fsuid_follow.c (3 case + 1 helper)

- `waitpid_safely2()` helper
- `fsuid_follows_euid_after_setresuid()` — setresuid 改 euid → fsuid 自动跟随 (verify via setfsuid(-1) 读回) (man §DESCRIPTION fsuid follow euid)
- `fsgid_follows_egid_after_setresgid()` — setresgid 改 egid → fsgid 自动跟随
- `fsuid_unchanged_when_euid_unchanged()` — euid 未变 → fsuid 也不变

### 3.6 c/src/matrix.c (1 case + 4 helper)

- `setup_state()` / `read_uid()` / `read_gid()` / `waitpid_safely()` helper
- `matrix_one()` — (syscall × state × ruid × euid × suid × mode) 笛卡尔单格 — 验 rc/errno + 三槽 + fsuid 跟随

### 3.7 c/src/procfs_visibility.c (6 case + 2 helper)

- `waitpid_safely_pv()` / `parse_proc_id_line()` helper
- `proc_uid_baseline()` — 未改动时 /proc Uid: == (uid,euid,suid)
- `proc_uid_after_setresuid()` — setresuid 后 /proc Uid: 三字段精确同步
- `proc_uid_after_setresuid_nochg()` — setresuid(-1,-1,-1) 后 procfs 不变
- `proc_gid_after_setresgid()` — setresgid 后 /proc Gid: 同步
- `proc_compound_setres()` — 复合调用后 procfs 全一致
- `proc_gid_baseline()` — /proc Gid: 基线一致

### 3.8 c/src/nptl_sync.c (3 case)

- `nptl_setresuid_main_to_pthread()` — 主线程 setresuid → 子 pthread getresuid 看到相同 cred (man §VERSIONS C library/kernel differences)
- `nptl_setresgid_pthread_to_main()`
- `nptl_concurrent_setresuid()` — 并发 setresuid 最终收敛

### 3.9 c/src/core_dump_inhibit.c (3 case)

- `cd_baseline()` — PR_GET_DUMPABLE == 1 (man 2 prctl §PR_GET_DUMPABLE)
- `cd_setresuid_nochg()` — setresuid 三参 nochg → dumpable 不变
- `cd_setresuid_changes()` — setresuid 改 euid → PR_GET_DUMPABLE → 0

### 3.10 c/src/errno_preservation.c (5 case)

- `setresuid_nochg_errno_preserved()` / `setresgid_nochg_errno_preserved()` / `setresuid_self_errno_preserved()` / `setresgid_self_errno_preserved()` / `raw_setresuid_success_errno_preserved()`

### 3.11 c/src/main.c (聚合 entry point, 非测点)

按顺序 COLLECT 10 个模块, 每模块 `*_run()` 返 `__fail`, main 汇总 TOTAL: N fail。

## 相关 syscall man 摘录 (核心段)

`setresuid` §DESCRIPTION:
- "setresuid() sets the real user ID, the effective user ID, and the saved set-user-ID of the calling process."
- "An unprivileged process may change its real UID, effective UID, and saved set-user-ID, each to one of: the current real UID, the current effective UID, or the current saved set-user-ID."
- "A privileged process (on Linux, one having the CAP_SETUID capability) may set its real UID, effective UID, and saved set-user-ID to arbitrary values."
- "If one of the arguments equals -1, the corresponding value is not changed."
- "Regardless of what changes are made to the real UID, effective UID, and saved set-user-ID, the filesystem UID is always set to the same value as the (possibly new) effective UID."

`setresuid` §ERRORS:
- EINVAL — One or more of the target user or group IDs is not valid in this user namespace.
- EPERM — The calling process is not privileged and tried to change the IDs to values that are not permitted.

`setresuid` §HISTORY: Linux 2.1.44, glibc 2.3.2. HP-UX, FreeBSD. glibc wrapper transparently deals with 16-bit/32-bit variations。

## 发现的 bug (本测试过程中暴露的 starry vs Linux 差异)

本 test PR 在 starry kernel 上跑时, 暴露以下 starry-vs-Linux 行为差异, 已各自登记为上游 issue, 单文件 C 复现程序在 fork repo 的 `bug-starry-*` 分支内单独维护 (路径 `test-suit/.../bugfix/bug-starry-<name>/c/src/main.c`):

- #713 — `setuid((uid_t)-1)` 在 starry 上被错误接受 (Linux 应返 EINVAL, 由 uid_valid 检查阻断)
- #714 — `/proc/self/status` 在 starry loongarch64 上 vm_write EFAULT (Uid:/Gid:/Groups: 三行均受影响)
- #715 — `setgroups(N, list)` 之后 `/proc/self/status` 的 Groups: 行不立即同步 (starry race)
- #716 — apk + curl 组合在 starry loongarch64 上 600s 超时 ~90% flake (与本测试矩阵 stress 路径相关)

上述 bug 由 #717 (fix-uid-gid-bugs, local 修) 与 #718 (fix-uid-gid-deep, cross-subsystem) 这两个 fix PR 分别托管 — fix PR 同步把 bug-starry-* 注册进 4 arch bugfix toml 矩阵, CI 由 FAIL → PASS 转化作为修复验收。

## CI / 验证

#728 当前 bucket: `pass=18 fail=0 skipping=30`。

## Matrix case 数学

### `matrix.c` — 3000 case (Group D 最大规模)

公式: `5 ruid × 5 euid × 5 suid × 6 state × 2 mode × 2 syscall = 3000`

- ruid 5: `0 / 1000 / 2000 / NOCHG / boundary`
- euid 5: 同 ruid
- suid 5: 同 ruid
- state 6: `root_unmod / 3id_distinct / unpriv / mid_priv / after_setresuid / NOCHG_baseline`
- mode 2: libc / raw
- syscall 2: setresuid / setresgid

为什么这么多维度:
- `setresuid(ruid, euid, suid)` 是三参数独立接口 — 与 setreuid (2 参 + 隐式 saved 更新) 不同, setres* 每参各自独立, 必须 5^3=125 个三元组覆盖全部组合空间
- unpriv 每槽位 in_set 检查独立, 需 5^3 组合验
- fsuid 必须验每个 (ruid, euid, suid) 组合下都跟新 euid

5^3 不能再缩: 缩到 4^3=64 会漏掉 NOCHG/boundary corner; 5^3=125 是最小覆盖。

每 case 4-5 assertion ≈ 12000-15000 PASS (含 fsuid procfs 验证)。

## 复现命令

切换分支:
```bash
cd <repo-root>
git checkout test-uid-gid-res-setters
```

本地 Linux (host, WSL2):
```bash
cd <repo-root>/test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-res-setters/c
rm -rf build && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug && make
sudo ./test-uid-gid-res-setters
```

starry qemu (4 arch):
```bash
source .starry-env.sh && cargo starry test qemu --arch x86_64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch aarch64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch riscv64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch loongarch64 -c syscall
```

## 引用

- 相关 PR: stacked 前 Group A/B/C (#725..#727); stacked 后 Group E (#729); fsuid_follow 模块依赖 fix #717 (fix-uid-gid-bugs) 新增 setfsuid/setfsgid 实现

Signed-off-by: Leo Cheng <chengkelfan@qq.com>
ZCShou pushed a commit that referenced this pull request May 19, 2026
## 分支用途

Group D: `setresuid` / `setresgid` 三参 (real + effective + saved) setter syscall 的 man-first 地毯式测试, 本地 3052 PASS。对应 #728。

## 测试覆盖 (每文件每函数)

测试位置: `test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-res-setters/c/src/`

模块总览 (10 模块 / 43 个 static void case 函数 / main.c 串联): `setresuid` / `setresgid` / `three_arg_independence` / `boundary` / `fsuid_follow` / `procfs_visibility` / `nptl_sync` / `core_dump_inhibit` / `matrix` / `errno_preservation`。

### 3.1 c/src/setresuid.c (6 case + 1 helper)

- `waitpid_safely()` helper
- `setresuid_all_nochg()` — setresuid(-1,-1,-1) 三字段不变 (man §DESCRIPTION sentinel -1)
- `setresuid_self_self_self()` — setresuid(self,self,self) 三字段不变
- `setresuid_root_arbitrary_three_values()` — root setresuid(r,e,s) 三个任意不同值都成功 (man §DESCRIPTION root)
- `setresuid_unpriv_within_set_ok()` — 非特权改到当前 {r,e,s} 集合内 → OK
- `setresuid_unpriv_outside_set_eperm()` — 非特权超出 {r,e,s} 集合 → EPERM (man §ERRORS EPERM)
- `setresuid_raw_matches_libc()` — 裸 syscall 与 libc 一致

### 3.2 c/src/setresgid.c (6 case + 1 helper)

- `waitpid_safely()` helper
- `setresgid_all_nochg()` / `setresgid_self_self_self()` / `setresgid_root_arbitrary_three_values()` / `setresgid_unpriv_within_set_ok()` / `setresgid_unpriv_outside_set_eperm()` / `setresgid_raw_matches_libc()`

### 3.3 c/src/three_arg_independence.c (7 case + 2 helper)

- `waitpid_safely()` helper / `test_combo()` helper
- `independence_root_only_r()` — root setresuid(NEW,-1,-1) 只改 r, e/s 保持 (man §DESCRIPTION 三参独立)
- `independence_root_only_s()` — root setresuid(-1,-1,NEW) 只改 s
- `independence_root_only_e()` — root setresuid(-1,NEW,-1) 只改 e
- `independence_all_three_distinct()` — r/e/s 三参传不同值 → 三槽各自独立赋值
- `independence_all_nochg_no_op()` — root (-1,-1,-1) 无操作不变
- `independence_all_nochg_no_op_gid()` — GID 侧 (-1,-1,-1) 无操作
- `independence_all_nochg_unpriv_ok()` — 非特权 (-1,-1,-1) 也 OK (不触发 EPERM)

### 3.4 c/src/boundary.c (3 case + 1 helper)

- `waitpid_safely()` helper
- `boundary_raw_all_u32max()` — 裸 syscall 三参全 (uid_t)-1 sentinel → nochg 路径
- `boundary_root_extreme_uids()` — root 极值 0/65535/65536/(u32max-1) (man §HISTORY 16→32-bit)
- `boundary_unpriv_one_outside_eperm()` — 非特权一个字段出集合 → EPERM

### 3.5 c/src/fsuid_follow.c (3 case + 1 helper)

- `waitpid_safely2()` helper
- `fsuid_follows_euid_after_setresuid()` — setresuid 改 euid → fsuid 自动跟随 (verify via setfsuid(-1) 读回) (man §DESCRIPTION fsuid follow euid)
- `fsgid_follows_egid_after_setresgid()` — setresgid 改 egid → fsgid 自动跟随
- `fsuid_unchanged_when_euid_unchanged()` — euid 未变 → fsuid 也不变

### 3.6 c/src/matrix.c (1 case + 4 helper)

- `setup_state()` / `read_uid()` / `read_gid()` / `waitpid_safely()` helper
- `matrix_one()` — (syscall × state × ruid × euid × suid × mode) 笛卡尔单格 — 验 rc/errno + 三槽 + fsuid 跟随

### 3.7 c/src/procfs_visibility.c (6 case + 2 helper)

- `waitpid_safely_pv()` / `parse_proc_id_line()` helper
- `proc_uid_baseline()` — 未改动时 /proc Uid: == (uid,euid,suid)
- `proc_uid_after_setresuid()` — setresuid 后 /proc Uid: 三字段精确同步
- `proc_uid_after_setresuid_nochg()` — setresuid(-1,-1,-1) 后 procfs 不变
- `proc_gid_after_setresgid()` — setresgid 后 /proc Gid: 同步
- `proc_compound_setres()` — 复合调用后 procfs 全一致
- `proc_gid_baseline()` — /proc Gid: 基线一致

### 3.8 c/src/nptl_sync.c (3 case)

- `nptl_setresuid_main_to_pthread()` — 主线程 setresuid → 子 pthread getresuid 看到相同 cred (man §VERSIONS C library/kernel differences)
- `nptl_setresgid_pthread_to_main()`
- `nptl_concurrent_setresuid()` — 并发 setresuid 最终收敛

### 3.9 c/src/core_dump_inhibit.c (3 case)

- `cd_baseline()` — PR_GET_DUMPABLE == 1 (man 2 prctl §PR_GET_DUMPABLE)
- `cd_setresuid_nochg()` — setresuid 三参 nochg → dumpable 不变
- `cd_setresuid_changes()` — setresuid 改 euid → PR_GET_DUMPABLE → 0

### 3.10 c/src/errno_preservation.c (5 case)

- `setresuid_nochg_errno_preserved()` / `setresgid_nochg_errno_preserved()` / `setresuid_self_errno_preserved()` / `setresgid_self_errno_preserved()` / `raw_setresuid_success_errno_preserved()`

### 3.11 c/src/main.c (聚合 entry point, 非测点)

按顺序 COLLECT 10 个模块, 每模块 `*_run()` 返 `__fail`, main 汇总 TOTAL: N fail。

## 相关 syscall man 摘录 (核心段)

`setresuid` §DESCRIPTION:
- "setresuid() sets the real user ID, the effective user ID, and the saved set-user-ID of the calling process."
- "An unprivileged process may change its real UID, effective UID, and saved set-user-ID, each to one of: the current real UID, the current effective UID, or the current saved set-user-ID."
- "A privileged process (on Linux, one having the CAP_SETUID capability) may set its real UID, effective UID, and saved set-user-ID to arbitrary values."
- "If one of the arguments equals -1, the corresponding value is not changed."
- "Regardless of what changes are made to the real UID, effective UID, and saved set-user-ID, the filesystem UID is always set to the same value as the (possibly new) effective UID."

`setresuid` §ERRORS:
- EINVAL — One or more of the target user or group IDs is not valid in this user namespace.
- EPERM — The calling process is not privileged and tried to change the IDs to values that are not permitted.

`setresuid` §HISTORY: Linux 2.1.44, glibc 2.3.2. HP-UX, FreeBSD. glibc wrapper transparently deals with 16-bit/32-bit variations。

## 发现的 bug (本测试过程中暴露的 starry vs Linux 差异)

本 test PR 在 starry kernel 上跑时, 暴露以下 starry-vs-Linux 行为差异, 已各自登记为上游 issue, 单文件 C 复现程序在 fork repo 的 `bug-starry-*` 分支内单独维护 (路径 `test-suit/.../bugfix/bug-starry-<name>/c/src/main.c`):

- #713 — `setuid((uid_t)-1)` 在 starry 上被错误接受 (Linux 应返 EINVAL, 由 uid_valid 检查阻断)
- #714 — `/proc/self/status` 在 starry loongarch64 上 vm_write EFAULT (Uid:/Gid:/Groups: 三行均受影响)
- #715 — `setgroups(N, list)` 之后 `/proc/self/status` 的 Groups: 行不立即同步 (starry race)
- #716 — apk + curl 组合在 starry loongarch64 上 600s 超时 ~90% flake (与本测试矩阵 stress 路径相关)

上述 bug 由 #717 (fix-uid-gid-bugs, local 修) 与 #718 (fix-uid-gid-deep, cross-subsystem) 这两个 fix PR 分别托管 — fix PR 同步把 bug-starry-* 注册进 4 arch bugfix toml 矩阵, CI 由 FAIL → PASS 转化作为修复验收。

## CI / 验证

#728 当前 bucket: `pass=18 fail=0 skipping=30`。

## Matrix case 数学

### `matrix.c` — 3000 case (Group D 最大规模)

公式: `5 ruid × 5 euid × 5 suid × 6 state × 2 mode × 2 syscall = 3000`

- ruid 5: `0 / 1000 / 2000 / NOCHG / boundary`
- euid 5: 同 ruid
- suid 5: 同 ruid
- state 6: `root_unmod / 3id_distinct / unpriv / mid_priv / after_setresuid / NOCHG_baseline`
- mode 2: libc / raw
- syscall 2: setresuid / setresgid

为什么这么多维度:
- `setresuid(ruid, euid, suid)` 是三参数独立接口 — 与 setreuid (2 参 + 隐式 saved 更新) 不同, setres* 每参各自独立, 必须 5^3=125 个三元组覆盖全部组合空间
- unpriv 每槽位 in_set 检查独立, 需 5^3 组合验
- fsuid 必须验每个 (ruid, euid, suid) 组合下都跟新 euid

5^3 不能再缩: 缩到 4^3=64 会漏掉 NOCHG/boundary corner; 5^3=125 是最小覆盖。

每 case 4-5 assertion ≈ 12000-15000 PASS (含 fsuid procfs 验证)。

## 复现命令

切换分支:
```bash
cd <repo-root>
git checkout test-uid-gid-res-setters
```

本地 Linux (host, WSL2):
```bash
cd <repo-root>/test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-res-setters/c
rm -rf build && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug && make
sudo ./test-uid-gid-res-setters
```

starry qemu (4 arch):
```bash
source .starry-env.sh && cargo starry test qemu --arch x86_64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch aarch64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch riscv64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch loongarch64 -c syscall
```

## 引用

- 相关 PR: stacked 前 Group A/B/C (#725..#727); stacked 后 Group E (#729); fsuid_follow 模块依赖 fix #717 (fix-uid-gid-bugs) 新增 setfsuid/setfsgid 实现

Signed-off-by: Leo Cheng <chengkelfan@qq.com>
ZCShou added a commit that referenced this pull request May 19, 2026
#739)

* Update ARM PL031 RTC and PL011 UART drivers position with documentation

- Introduced `ax-arm-pl031` crate for ARM PL031 RTC with functionalities to get and set time, and manage interrupts.
- Added Chinese localization for the RTC README.
- Implemented core functionalities in `chrono.rs` and `lib.rs` for the RTC driver.
- Created CI workflow for `ax-arm-pl011` UART driver with testing and documentation generation.
- Added `ax-arm-pl011` crate for ARM PL011 UART with register definitions and basic operations.
- Included Chinese localization for the UART README.
- Established basic UART operations including initialization, character transmission, and interrupt handling.
- Added necessary license and configuration files for both drivers.

* Add SCMI Base and Clock Protocol Implementations

- Introduced the Base protocol client for SCMI, enabling vendor discovery and protocol listing.
- Implemented the Clock protocol client, providing functionalities for clock management including enabling/disabling clocks, querying attributes, and setting/getting clock rates.
- Created a transport layer for SCMI message exchange, supporting SMC (Secure Monitor Call) transport.
- Added shared memory management for SCMI communication.
- Developed tests to validate the functionality of the SCMI protocols and transport layer.

* feat(dw_apb_uart): migrate driver to new directory structure and add necessary files

* feat(dw_apb_uart): update dependency to use workspace and adjust features

* fix(dependencies): update path for arm-scmi-rs crate

* chore(starry): regroup normal qemu test cases (#744)

* chore(starry): regroup normal qemu test cases

* chore(starry): regroup normal qemu test cases

* chore(rockchip-npu): add repository.workspace to Cargo.toml (#753)

* chore(rockchip-npu): add repository.workspace to Cargo.toml

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs: add CLAUDE.md for repository guidance and build instructions

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>

* feat(starry): add PicoClaw phase 1 and 2 support (#689)

* chore: initialize picoclaw support branch

* feat(starry): add picoclaw phase 1 and 2 support

* ci: re-trigger

* fix(dw_apb_uart): update version to 0.1.2 in Cargo.toml and Cargo.lock (#754)

* chore: release (#755)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* test(starry): remove non-semantic BusyBox checks (#752)

* fix(unix-stream,poll_io): non-blocking accept, peer EOF, waker registration (#697)

Three related fixes for Unix socket and async I/O correctness:

1. fix(unix-stream): non-blocking accept via try_accept() + poll_io

   Tokio uses EPOLLET and drains accept() until EAGAIN after every EPOLLIN.
   The old code block_on(interruptible(transport.accept())) always blocked
   the kernel thread — even with O_NONBLOCK — because interruptible wraps
   an async_channel::recv that has no timeout.  After accepting TUI's one
   connection, Tokio called accept() again; the kernel thread blocked
   indefinitely, freezing the entire serve async runtime.  No messages from
   TUI were ever processed.

   Fix: add try_accept() on StreamTransport using async_channel::try_recv()
   (returns WouldBlock immediately when queue empty).  UnixSocket::accept()
   now reads the O_NONBLOCK flag and delegates to poll_io + try_accept(),
   matching POSIX accept(2) semantics.

2. fix(unix-stream): signal peer EOF before wake to prevent epoll hang

   When StreamTransport dropped, poll_update.wake() fired while HeapProd
   was still alive.  The peer's poll() ran in that window: write_is_held()
   returned true, rx was empty, so poll() reported no events and re-armed
   its waker — which never fired again, causing a ~50 s timeout wait.

   Fix: add cross-wired Arc<AtomicBool> close flags (my_tx_closed /
   peer_tx_closed).  Drop sets my_tx_closed=true BEFORE calling wake(), so
   poll() immediately reports IN+RDHUP.  recv() also treats peer_tx_closed
   as an EOF condition, avoiding any busy-wait in the race window.

3. fix(poll_io): register waker before returning WouldBlock

   For a non-blocking TCP connect, poll_io returned WouldBlock without
   calling pollable.register(cx, events).  If the TCP handshake completed
   before the subsequent epoll_ctl ADD call registered the InterestWaker,
   the IRQ wake hit an empty PollSet and the EPOLLOUT notification was
   silently lost.

   Fix: always register the waker first; then, for non-blocking callers,
   return WouldBlock immediately.  For blocking callers, retry once and
   suspend on a second WouldBlock.

Co-authored-by: StarryOS Fix <fix@starryos.dev>
Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* fix(starry): support v4-mapped IPv6 sockets (#694)

* fix(starry): support v4-mapped IPv6 sockets

* fix(starry): wrap accepted IPv6 peer addresses

---------

Co-authored-by: 周睿 <34859362+ZR233@users.noreply.github.com>
Co-authored-by: Tianxin Tech <txc@TianxindeMacBook-Air.local>

* fix(starry): reject invalid umount2 flags (#699)

* fix(starry): reject invalid umount2 flags

* fix sys_mount and umount2

---------

Co-authored-by: 54dK3n <ken@kendeAir.lan>

* test(starry): add uname/sysinfo coverage and minimal syslog syscall support (#705)

* Add standalone uname test case

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test(uname): align assertions with syscall semantics

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test(sysinfo): add semantic userspace syscall coverage

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test(syslog): add semantic userspace coverage and minimal kernel support

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: retrigger CI

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test(syslog): tighten console level and read semantics

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test(syslog): add non-root EPERM permission denial coverage

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test(starry): add x86_64/riscv64/loongarch64 QEMU configs for uname, sysinfo, syslog

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: retrigger CI (unrelated test-clone-files-race flake)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: retrigger CI (loongarch64/aarch64 starry jobs not reached)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* test(syscall): test-uid-gid-res-setters 3052 PASS (#728)

## 分支用途

Group D: `setresuid` / `setresgid` 三参 (real + effective + saved) setter syscall 的 man-first 地毯式测试, 本地 3052 PASS。对应 #728。

## 测试覆盖 (每文件每函数)

测试位置: `test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-res-setters/c/src/`

模块总览 (10 模块 / 43 个 static void case 函数 / main.c 串联): `setresuid` / `setresgid` / `three_arg_independence` / `boundary` / `fsuid_follow` / `procfs_visibility` / `nptl_sync` / `core_dump_inhibit` / `matrix` / `errno_preservation`。

### 3.1 c/src/setresuid.c (6 case + 1 helper)

- `waitpid_safely()` helper
- `setresuid_all_nochg()` — setresuid(-1,-1,-1) 三字段不变 (man §DESCRIPTION sentinel -1)
- `setresuid_self_self_self()` — setresuid(self,self,self) 三字段不变
- `setresuid_root_arbitrary_three_values()` — root setresuid(r,e,s) 三个任意不同值都成功 (man §DESCRIPTION root)
- `setresuid_unpriv_within_set_ok()` — 非特权改到当前 {r,e,s} 集合内 → OK
- `setresuid_unpriv_outside_set_eperm()` — 非特权超出 {r,e,s} 集合 → EPERM (man §ERRORS EPERM)
- `setresuid_raw_matches_libc()` — 裸 syscall 与 libc 一致

### 3.2 c/src/setresgid.c (6 case + 1 helper)

- `waitpid_safely()` helper
- `setresgid_all_nochg()` / `setresgid_self_self_self()` / `setresgid_root_arbitrary_three_values()` / `setresgid_unpriv_within_set_ok()` / `setresgid_unpriv_outside_set_eperm()` / `setresgid_raw_matches_libc()`

### 3.3 c/src/three_arg_independence.c (7 case + 2 helper)

- `waitpid_safely()` helper / `test_combo()` helper
- `independence_root_only_r()` — root setresuid(NEW,-1,-1) 只改 r, e/s 保持 (man §DESCRIPTION 三参独立)
- `independence_root_only_s()` — root setresuid(-1,-1,NEW) 只改 s
- `independence_root_only_e()` — root setresuid(-1,NEW,-1) 只改 e
- `independence_all_three_distinct()` — r/e/s 三参传不同值 → 三槽各自独立赋值
- `independence_all_nochg_no_op()` — root (-1,-1,-1) 无操作不变
- `independence_all_nochg_no_op_gid()` — GID 侧 (-1,-1,-1) 无操作
- `independence_all_nochg_unpriv_ok()` — 非特权 (-1,-1,-1) 也 OK (不触发 EPERM)

### 3.4 c/src/boundary.c (3 case + 1 helper)

- `waitpid_safely()` helper
- `boundary_raw_all_u32max()` — 裸 syscall 三参全 (uid_t)-1 sentinel → nochg 路径
- `boundary_root_extreme_uids()` — root 极值 0/65535/65536/(u32max-1) (man §HISTORY 16→32-bit)
- `boundary_unpriv_one_outside_eperm()` — 非特权一个字段出集合 → EPERM

### 3.5 c/src/fsuid_follow.c (3 case + 1 helper)

- `waitpid_safely2()` helper
- `fsuid_follows_euid_after_setresuid()` — setresuid 改 euid → fsuid 自动跟随 (verify via setfsuid(-1) 读回) (man §DESCRIPTION fsuid follow euid)
- `fsgid_follows_egid_after_setresgid()` — setresgid 改 egid → fsgid 自动跟随
- `fsuid_unchanged_when_euid_unchanged()` — euid 未变 → fsuid 也不变

### 3.6 c/src/matrix.c (1 case + 4 helper)

- `setup_state()` / `read_uid()` / `read_gid()` / `waitpid_safely()` helper
- `matrix_one()` — (syscall × state × ruid × euid × suid × mode) 笛卡尔单格 — 验 rc/errno + 三槽 + fsuid 跟随

### 3.7 c/src/procfs_visibility.c (6 case + 2 helper)

- `waitpid_safely_pv()` / `parse_proc_id_line()` helper
- `proc_uid_baseline()` — 未改动时 /proc Uid: == (uid,euid,suid)
- `proc_uid_after_setresuid()` — setresuid 后 /proc Uid: 三字段精确同步
- `proc_uid_after_setresuid_nochg()` — setresuid(-1,-1,-1) 后 procfs 不变
- `proc_gid_after_setresgid()` — setresgid 后 /proc Gid: 同步
- `proc_compound_setres()` — 复合调用后 procfs 全一致
- `proc_gid_baseline()` — /proc Gid: 基线一致

### 3.8 c/src/nptl_sync.c (3 case)

- `nptl_setresuid_main_to_pthread()` — 主线程 setresuid → 子 pthread getresuid 看到相同 cred (man §VERSIONS C library/kernel differences)
- `nptl_setresgid_pthread_to_main()`
- `nptl_concurrent_setresuid()` — 并发 setresuid 最终收敛

### 3.9 c/src/core_dump_inhibit.c (3 case)

- `cd_baseline()` — PR_GET_DUMPABLE == 1 (man 2 prctl §PR_GET_DUMPABLE)
- `cd_setresuid_nochg()` — setresuid 三参 nochg → dumpable 不变
- `cd_setresuid_changes()` — setresuid 改 euid → PR_GET_DUMPABLE → 0

### 3.10 c/src/errno_preservation.c (5 case)

- `setresuid_nochg_errno_preserved()` / `setresgid_nochg_errno_preserved()` / `setresuid_self_errno_preserved()` / `setresgid_self_errno_preserved()` / `raw_setresuid_success_errno_preserved()`

### 3.11 c/src/main.c (聚合 entry point, 非测点)

按顺序 COLLECT 10 个模块, 每模块 `*_run()` 返 `__fail`, main 汇总 TOTAL: N fail。

## 相关 syscall man 摘录 (核心段)

`setresuid` §DESCRIPTION:
- "setresuid() sets the real user ID, the effective user ID, and the saved set-user-ID of the calling process."
- "An unprivileged process may change its real UID, effective UID, and saved set-user-ID, each to one of: the current real UID, the current effective UID, or the current saved set-user-ID."
- "A privileged process (on Linux, one having the CAP_SETUID capability) may set its real UID, effective UID, and saved set-user-ID to arbitrary values."
- "If one of the arguments equals -1, the corresponding value is not changed."
- "Regardless of what changes are made to the real UID, effective UID, and saved set-user-ID, the filesystem UID is always set to the same value as the (possibly new) effective UID."

`setresuid` §ERRORS:
- EINVAL — One or more of the target user or group IDs is not valid in this user namespace.
- EPERM — The calling process is not privileged and tried to change the IDs to values that are not permitted.

`setresuid` §HISTORY: Linux 2.1.44, glibc 2.3.2. HP-UX, FreeBSD. glibc wrapper transparently deals with 16-bit/32-bit variations。

## 发现的 bug (本测试过程中暴露的 starry vs Linux 差异)

本 test PR 在 starry kernel 上跑时, 暴露以下 starry-vs-Linux 行为差异, 已各自登记为上游 issue, 单文件 C 复现程序在 fork repo 的 `bug-starry-*` 分支内单独维护 (路径 `test-suit/.../bugfix/bug-starry-<name>/c/src/main.c`):

- #713 — `setuid((uid_t)-1)` 在 starry 上被错误接受 (Linux 应返 EINVAL, 由 uid_valid 检查阻断)
- #714 — `/proc/self/status` 在 starry loongarch64 上 vm_write EFAULT (Uid:/Gid:/Groups: 三行均受影响)
- #715 — `setgroups(N, list)` 之后 `/proc/self/status` 的 Groups: 行不立即同步 (starry race)
- #716 — apk + curl 组合在 starry loongarch64 上 600s 超时 ~90% flake (与本测试矩阵 stress 路径相关)

上述 bug 由 #717 (fix-uid-gid-bugs, local 修) 与 #718 (fix-uid-gid-deep, cross-subsystem) 这两个 fix PR 分别托管 — fix PR 同步把 bug-starry-* 注册进 4 arch bugfix toml 矩阵, CI 由 FAIL → PASS 转化作为修复验收。

## CI / 验证

#728 当前 bucket: `pass=18 fail=0 skipping=30`。

## Matrix case 数学

### `matrix.c` — 3000 case (Group D 最大规模)

公式: `5 ruid × 5 euid × 5 suid × 6 state × 2 mode × 2 syscall = 3000`

- ruid 5: `0 / 1000 / 2000 / NOCHG / boundary`
- euid 5: 同 ruid
- suid 5: 同 ruid
- state 6: `root_unmod / 3id_distinct / unpriv / mid_priv / after_setresuid / NOCHG_baseline`
- mode 2: libc / raw
- syscall 2: setresuid / setresgid

为什么这么多维度:
- `setresuid(ruid, euid, suid)` 是三参数独立接口 — 与 setreuid (2 参 + 隐式 saved 更新) 不同, setres* 每参各自独立, 必须 5^3=125 个三元组覆盖全部组合空间
- unpriv 每槽位 in_set 检查独立, 需 5^3 组合验
- fsuid 必须验每个 (ruid, euid, suid) 组合下都跟新 euid

5^3 不能再缩: 缩到 4^3=64 会漏掉 NOCHG/boundary corner; 5^3=125 是最小覆盖。

每 case 4-5 assertion ≈ 12000-15000 PASS (含 fsuid procfs 验证)。

## 复现命令

切换分支:
```bash
cd <repo-root>
git checkout test-uid-gid-res-setters
```

本地 Linux (host, WSL2):
```bash
cd <repo-root>/test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-res-setters/c
rm -rf build && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug && make
sudo ./test-uid-gid-res-setters
```

starry qemu (4 arch):
```bash
source .starry-env.sh && cargo starry test qemu --arch x86_64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch aarch64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch riscv64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch loongarch64 -c syscall
```

## 引用

- 相关 PR: stacked 前 Group A/B/C (#725..#727); stacked 后 Group E (#729); fsuid_follow 模块依赖 fix #717 (fix-uid-gid-bugs) 新增 setfsuid/setfsgid 实现

Signed-off-by: Leo Cheng <chengkelfan@qq.com>

* fix(Cargo.toml): update arm-pl011 and arm-pl031 versions, add arm-scmi-rs driver

* fix(repo): resolve Cargo.toml merge conflict fallout

Agent-Logs-Url: https://github.com/rcore-os/tgoskits/sessions/f26a8ea9-07de-42b1-894a-9883908c763c

Co-authored-by: ZCShou <8591065+ZCShou@users.noreply.github.com>

---------

Signed-off-by: Leo Cheng <chengkelfan@qq.com>
Co-authored-by: YanLien <128586861+YanLien@users.noreply.github.com>
Co-authored-by: 周睿 <34859362+ZR233@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Shuo Zhang <zhangs24@mails.tsinghua.edu.cn>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Kevin Choo <3056063115@qq.com>
Co-authored-by: Feiran Qin <161046517+jakeuibn@users.noreply.github.com>
Co-authored-by: StarryOS Fix <fix@starryos.dev>
Co-authored-by: 杨凯森 <157187169+yks23@users.noreply.github.com>
Co-authored-by: Tianxin Tech <txc@TianxindeMacBook-Air.local>
Co-authored-by: 54dK3n <69028823+54dK3n@users.noreply.github.com>
Co-authored-by: 54dK3n <ken@kendeAir.lan>
Co-authored-by: 玲因 <2030746443@qq.com>
Co-authored-by: 禾可 <chengkelfan@qq.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: ZCShou <8591065+ZCShou@users.noreply.github.com>

@mai-team-app mai-team-app Bot left a comment

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.

🔍 PR #727 Review — test(syscall): test-uid-gid-re-setters 915 PASS

📋 总览

本 PR 纯新增 C 测试代码(16 文件,+1684 行,0 删除),为 setreuid / setregid 两参 syscall 提供 man-first 地毯式测试。9 模块 / 40 测试函数 / 915 断言,覆盖 NOCHG sentinel、self-set、root 任意值、非特权 EPERM、raw vs libc 一致性、D5 saved-set-uid 规则(含反向 corner)、864 case 笛卡尔积矩阵、procfs 可见性、pthread cred 同步、core dump 抑制、errno 保护、supplementary groups 不变量。

✅ 代码质量与正确性

逐模块审查(全部通过):

  1. test_framework.h:简洁宏驱动框架,CHECK/CHECK_ERR/CHECK_OR_BUG 支持硬断言、errno 检查和已知 starry bug 软断言,设计合理。
  2. main.cCOLLECT 宏防御性处理可疑返回值,9 模块串行收集,逻辑正确。
  3. setreuid.c (7 case):覆盖 NOCHG sentinel、self-set、root 任意值、非特权 EPERM、raw vs libc、supplementary groups 不变量。
  4. setregid.c (7 case):镜像 setreuid 测试,含 supplementary groups invariant。
  5. saved_id_semantics.c (6 case):D5 saved-set-uid 规则完整覆盖,特别是 D5 反向 corner case,UID 和 GID 双维度。
  6. matrix.c (864 case)derive() 从 man page 推导期望值,从 1536 缩减到 864 保留关键边界。
  7. procfs_visibility.c (5 case):验证 /proc/self/status Uid:/Gid: 行同步,含 D5 反向和 compound 验证。
  8. nptl_sync.c (3 case)_Atomic + 轮询,对非 NPTL 实现优雅降级。
  9. core_dump_inhibit.c (3 case):验证 euid 变化后 PR_GET_DUMPABLE 降为 0。
  10. errno_preservation.c (5 case):验证成功路径不误改 errno。

🔒 安全与死锁分析

无死锁风险、无资源泄漏。所有模块使用 fork() + waitpid() 隔离模式,无共享锁、无 IPC 依赖。

📊 对既有功能的影响

零影响。纯新增代码,4 个 qemu-*.toml 仅追加一行新测试命令。

🔧 本地验证

10 个 C 源文件全部编译通过(GCC 13.3.0,-Wall -Wextra -Werror),零警告零错误。

🔍 CI 状态

当前 head CI 失败全部是基础设施问题(SMP4 timeout flake #740、cargo registry 网络错误、self-hosted runner infra),与 PR 改动无关。

🔗 合并状态

  • 与 dev 分支合并无冲突(已验证 git merge --no-commit origin/dev 成功)
  • 已有 2 个 APPROVE review

结论

APPROVE — 高质量 man-first 测试套件,覆盖面广、代码结构清晰、无风险。建议合并。

Powered by mimo-v2.5-pro

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; }

Lfan-ke added a commit to Lfan-ke/tgoskits that referenced this pull request May 19, 2026
Group B: `setuid` / `setgid` 两个直接 setter syscall 的 man-first 地毯式
测试, 本地 477 PASS。对应 rcore-os#726。

测试位置: `test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-direct-setters/c/src/`

模块总览 (10 模块 / 46 个 static void case 函数 / main.c 串联):
`setuid` / `setgid` / `cross_root_unprivileged` / `boundary` / `matrix` /
`procfs_visibility` / `nptl_sync` / `core_dump_inhibit` / `uid32_no_trunc` /
`errno_preservation`。

- `waitpid_safely()` helper — 循环 waitpid 处理 EINTR
- `setuid_idempotent_to_self()` — setuid(geteuid()) 后三字段 (r,e,s) 不变
  (man §DESCRIPTION POSIX_SAVED_IDS)
- `setuid_root_sets_all_three()` — root setuid(target) → r/e/s 全被设为
  target (man §DESCRIPTION root path)
- `setuid_returns_zero_on_success()` — 成功 rc == 0 (不只是 ≥0)
- `setuid_raw_syscall_matches_libc()` — 裸 syscall 与 libc 行为一致
- `setuid_unprivileged_eperm_in_child()` — 非特权 setuid 在 fork 子 →
  EPERM (man §ERRORS EPERM)
- `setuid_raw_vs_libc_failed_path()` — EPERM 失败路径下 raw rc/errno 与
  libc 严格相同
- `setuid_irreversibility()` — root setuid(non-zero) drop 后无法
  setuid(0) 回去 (man §DESCRIPTION 不可逆)
- `setuid_fork_child_independent()` — fork 子 setuid 改动不污染父 cred
- `setuid_self_euid_no_flush()` — 非特权 setuid(euid) 不清除 saved-set-uid
- `setuid_einval_uid_minus_one()` — uid=(uid_t)-1 在非特权下 → EPERM/
  EINVAL (软容忍 starry bug B)

- `waitpid_safely()` helper
- `setgid_idempotent_to_self()` — setgid(getegid()) 三字段不变
- `setgid_root_sets_all_three()` — root setgid(target) → r/e/s GID 全置
- `setgid_returns_zero_on_success()`
- `setgid_raw_syscall_matches_libc()`
- `setgid_unprivileged_eperm_in_child()` — 非特权 setgid(任意) → EPERM
- `setgid_raw_vs_libc_failed_path()`
- `setgid_alone_not_irreversible_caps_intact()` — 仅 setgid drop 不丢
  CAP_SETUID, 仍可 setuid
- `setgid_irreversible_after_setuid_drop()` — setuid drop 后 setgid 不可
  逆 (CAP_SETGID 丢)
- `setgid_fork_child_independent()`
- `setgid_self_egid_no_change()` — setgid(egid) 不动 saved-set-gid

- `waitpid_safely()` helper
- `root_setuid_arbitrary_updates_all()` — root setuid(任意值) → r/e/s
  全跟
- `unprivileged_setuid_self_succeeds()` — 非特权 setuid(self) 成功且字段
  不变
- `unprivileged_setuid_arbitrary_eperm()` — 非特权 setuid(非 {r,e,s}) →
  EPERM

- `setuid_boundary_values_root()` — root 路径下边界 uid:
  0/1/65535/65536/0x7FFFFFFE 全 OK
- `setuid_u32max_sentinel_unprivileged()` — 非特权 setuid((uid_t)-1)
  sentinel → EPERM/EINVAL (man §ERRORS EINVAL)
- `setuid_raw_syscall_returns_negative_errno()` — 裸 syscall 失败时 rc
  为 `-EPERM` (不是 -1+errno)

- `read_uid_cred()` / `read_gid_cred()` / `setup_state()` /
  `waitpid_safely()` helper
- `matrix_one()` — (syscall × state × input × mode) 笛卡尔单格 — 验
  rc/errno + getresuid 后续三槽

- `waitpid_safely_pv()` / `parse_proc_id_line()` helper
- `procfs_uid_baseline_root()` — 未改动时 /proc Uid: == (uid,euid,suid)
  一致 (proc(5) §Uid)
- `procfs_uid_after_setuid_root()` — root setuid 后 procfs 三字段同步
- `procfs_uid_after_setuid_drop()` — drop 后 procfs Uid: 三字段一致更新
- `procfs_gid_after_setgid()` — setgid 后 procfs Gid: 行同步
- `procfs_uid_after_setresuid()` — setresuid 后 procfs 三字段精确同步

- `nptl_setuid_main_visible_to_pthread()` — 主线程 setuid → 子 pthread
  getresuid 看到相同 cred (man §VERSIONS C library/kernel differences)
- `nptl_setgid_pthread_visible_to_main()`
- `nptl_concurrent_setuid_converges()`

- `waitpid_safely_cd()` helper
- `core_dump_baseline()` — 未改动时 PR_GET_DUMPABLE == 1 (man §NOTES)
- `core_dump_setuid_no_euid_change()` — setuid(euid) euid 未变 →
  dumpable 不变
- `core_dump_setuid_changes_euid()` — setuid 改变 euid → PR_GET_DUMPABLE
  → 0 (禁 core)

- `waitpid_safely()` helper
- `setuid_32bit_no_truncate()` — child setuid 大 UID (>16-bit) 后
  getresuid 不被截 (man §HISTORY 16→32-bit)
- `setgid_32bit_no_truncate()`
- `setuid_32bit_via_raw_syscall()`

- `setuid_self_errno_preserved()` / `setgid_self_errno_preserved()` /
  `setuid_root_errno_preserved()` / `setgid_root_errno_preserved()` /
  `raw_setuid_success_errno_preserved()` — 成功路径不动预置 errno

按顺序 COLLECT 10 个模块, 每模块 `*_run()` 返 `__fail`, main 汇总
TOTAL: N fail 收尾。

`setuid` §DESCRIPTION: "setuid() sets the effective user ID of the
calling process. If the calling process is privileged (more precisely:
if the process has the CAP_SETUID capability in its user namespace),
the real UID and saved set-user-ID are also set."

`setuid` §ERRORS:
- EAGAIN — temporary failure allocating kernel data structures
- EINVAL — The user ID specified in uid is not valid in this user
  namespace
- EPERM — The user is not privileged and uid does not match the real
  UID or saved set-user-ID of the calling process

`setuid` §NOTES: "If uid is different from the old effective UID, the
process will be forbidden from leaving core dumps."

`setuid` §VERSIONS (C library/kernel differences): NPTL wrapper 使用
signal-based 技术保证 process-wide thread cred 同步。

`setgid` §ERRORS: EINVAL (gid 不合法) / EPERM (无 CAP_SETGID 且 gid 不
匹配 real GID 或 saved set-group-ID)。

本 test PR 在 starry kernel 上跑时, 暴露以下 starry-vs-Linux 行为差异,
已各自登记为上游 issue, 单文件 C 复现程序在 fork repo 的 `bug-starry-*`
分支内单独维护 (路径 `test-suit/.../bugfix/bug-starry-<name>/c/src/main.c`):

- rcore-os#713 — `setuid((uid_t)-1)` 在 starry 上被错误接受
  (Linux 应返 EINVAL, 由 uid_valid 检查阻断)
- rcore-os#714 — `/proc/self/status` 在 starry loongarch64 上
  vm_write EFAULT (Uid:/Gid:/Groups: 三行均受影响)
- rcore-os#715 — `setgroups(N, list)` 之后 `/proc/self/status`
  的 Groups: 行不立即同步 (starry race)
- rcore-os#716 — apk + curl 组合在 starry loongarch64 上
  600s 超时 ~90% flake (与本测试矩阵 stress 路径相关)

上述 bug 由 rcore-os#717 (fix-uid-gid-bugs, local 修) 与
rcore-os#718 (fix-uid-gid-deep, cross-subsystem) 这两个 fix PR
分别托管 — fix PR 同步把 bug-starry-* 注册进 4 arch bugfix toml 矩阵,
CI 由 FAIL → PASS 转化作为修复验收。

rcore-os#726 当前 bucket: `pass=18 fail=0 skipping=30`。

公式: `2 syscall × 7 state × 16 input × 2 mode = 448`

- syscall 2: `setuid` / `setgid`
- state 7: `root_unmod` / `after_setresuid(0,1000,2000)` /
  `after_setresuid(0,0,1000)` / `unpriv(1000)` /
  `unpriv_3id(1000,2000,3000)` / `after_setgid(1000)` / `mixed_uid_gid`
- input 16: 0 / 1 / 100 / 999 / 1000 / 2000 / 3000 / 65535 / 65536 /
  100001 / 0x7FFFFFFE / NOCHG((uid_t)-1) / current_uid / current_euid /
  current_suid / boundary
- mode 2: libc / raw

为什么这么多维度:
- input 16 值: man §ERRORS EINVAL 触发只在 `(uid_t)-1`; EPERM 触发依赖
  input 是否在 {uid, euid, suid}; root 接受任意值 — 16 值覆盖 small /
  large / boundary (16-bit / 32-bit) / sentinel / self-set / cross-set
- state 7: 同一 `setuid(input)` 在不同 cred 起点上行为不同 — 覆盖 man
  §EPERM 决策树各分支
- mode 2: libc/raw 分离测验 ABI 一致性

每 case 3-5 assertion: rc / errno / 后续 getresuid r/e/s 三槽各验 /
fsuid procfs 检查 → 单 case 平均 4 assert → 总 ~1800 PASS。

切换分支:
```bash
cd <repo-root>
git checkout test-uid-gid-direct-setters
```

本地 Linux (host, WSL2):
```bash
cd <repo-root>/test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-direct-setters/c
rm -rf build && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug && make
sudo ./test-uid-gid-direct-setters
```

starry qemu (4 arch):
```bash
source .starry-env.sh && cargo starry test qemu --arch x86_64      -c syscall
source .starry-env.sh && cargo starry test qemu --arch aarch64     -c syscall
source .starry-env.sh && cargo starry test qemu --arch riscv64     -c syscall
source .starry-env.sh && cargo starry test qemu --arch loongarch64 -c syscall
```

- 相关 PR: stacked 前 Group A (rcore-os#725); stacked 后 Group C/D/E
  (rcore-os#727..rcore-os#729); 衍生 bug-* rcore-os#713 (setuid/setgid no uid_valid),
  fix rcore-os#717 (fix-uid-gid-bugs) 修复该 bug

Signed-off-by: Leo Cheng <chengkelfanke@gmail.com>
Lfan-ke added a commit to Lfan-ke/tgoskits that referenced this pull request May 19, 2026
Group C: `setreuid` / `setregid` 两参 (real + effective) setter syscall 的 man-first 地毯式测试, 本地 915 PASS。对应 rcore-os#727。

测试位置: `test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-re-setters/c/src/`

模块总览 (9 模块 / 40 个 static void case 函数 / main.c 串联): `setreuid` / `setregid` / `saved_id_semantics` / `boundary` / `matrix` / `procfs_visibility` / `nptl_sync` / `core_dump_inhibit` / `errno_preservation`。

- `waitpid_safely()` helper
- `setreuid_both_nochg_idempotent()` — setreuid((uid_t)-1,(uid_t)-1) 不改 r/e/s 三字段 (man §DESCRIPTION sentinel -1)
- `setreuid_ruid_self()` — setreuid(self,-1) 后 real == 原 real
- `setreuid_euid_self()` — setreuid(-1,self) 后 effective == 原 effective
- `setreuid_root_arbitrary()` — root setreuid(r,e) 任意值成功
- `setreuid_unpriv_eperm()` — 非特权越权 (非 {ruid,euid,suid}) → EPERM (man §ERRORS EPERM)
- `setreuid_raw_matches_libc()` — 裸 syscall 与 libc 一致 (rc + 三字段)
- `setreuid_keeps_supp_groups()` — **Linux invariant** (man 未显式但 kernel 行为强制): root → setgroups([7,8,9]) → setreuid(1000,1000) drop → getgroups 应仍 [7,8,9]; host gcc 实测 PASS, 防 starry sys_setreuid 错误清 cred.group_info 回归

- `waitpid_safely()` helper
- `setregid_both_nochg()` / `setregid_rgid_self()` / `setregid_egid_self()` / `setregid_root_arbitrary()` / `setregid_unpriv_eperm()` / `setregid_raw_matches_libc()` / `setregid_keeps_supp_groups()` — 镜像 setreuid 测试 (含 (g) supp groups invariant)

- `waitpid_safely()` helper
- `saved_id_ruid_set_updates_suid()` — ruid != -1 → suid := new euid (D5 规则触发) (man §DESCRIPTION)
- `saved_id_euid_set_to_nonruid_updates_suid()` — euid 改成 ≠ 旧 ruid → suid 更新
- `saved_id_both_set_root()` — root 同时改 r 和 e, suid 更新到新 euid
- `saved_id_both_nochg_no_change()` — (-1,-1) → r/e/s 全无变化
- `saved_id_d5_reverse_corner()` — D5 反向 corner: euid := 旧 ruid 时 saved 不应被更新
- `saved_id_d5_reverse_corner_gid()` — D5 corner GID 侧

- `waitpid_safely()` helper
- `boundary_raw_u32max_nochg()` — 裸 syscall 传 (uid_t)-1 sentinel 走 nochg 路径
- `boundary_root_extreme_values()` — root 极值 0/1000/65535/65536/ (u32max-1) (man §HISTORY 16→32-bit)
- `boundary_unpriv_extreme_eperm()` — 非特权传极值 → EPERM

- `setup_state()` / `read_uid_cred()` / `read_gid_cred()` / `waitpid_safely()` helper
- `matrix_one()` — (syscall × state × ruid × euid × mode) 笛卡尔单格 — 验 rc/errno + 后续 r/e/s 三槽 + D5 saved 规则

- `waitpid_safely_pv()` / `parse_proc_id_line()` helper
- `proc_uid_after_setreuid_full()` — setreuid(r,e) 全改 → /proc Uid: 三字段一致 (proc(5) §Uid + man §DESCRIPTION)
- `proc_uid_after_setreuid_nochg_r()` — setreuid(-1,e) → procfs real 不变、e 同步、s 按 D5
- `proc_uid_after_setreuid_nochg_same()` — setreuid(r,r) same-value → procfs 三字段一致
- `proc_gid_after_setregid_full()` — setregid 全改 → /proc Gid: 行同步
- `proc_compound_setre()` — 组合调用 (setreuid 再 setregid) 后 procfs 三字段全一致

- `nptl_setreuid_main_to_pthread()` — 主线程 setreuid → 子 pthread getresuid 看到相同 cred (man §VERSIONS C library/kernel differences)
- `nptl_setregid_pthread_to_main()`
- `nptl_concurrent_setreuid()`

- `cd_baseline()` — 未改动时 PR_GET_DUMPABLE == 1 (man §NOTES)
- `cd_setreuid_no_euid_change()` — euid 未变 → dumpable 不变
- `cd_setreuid_changes_euid()` — euid 变化 → PR_GET_DUMPABLE → 0

- `setreuid_nochg_errno_preserved()` / `setregid_nochg_errno_preserved()` / `setreuid_self_errno_preserved()` / `setregid_self_errno_preserved()` / `raw_setreuid_success_errno_preserved()`

按顺序 COLLECT 9 个模块, 每模块 `*_run()` 返 `__fail`, main 汇总 TOTAL: N fail。

`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."
- "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." (D5 规则)

`setreuid` §ERRORS:
- EINVAL — One or more of the target user or group IDs is not valid in this user namespace.
- EPERM — The calling process is not privileged and a change other than (i) swapping effective with real, (ii) setting one to the value of the other, (iii) setting effective to saved set-user-ID was specified.

`setreuid` §HISTORY: glibc setreuid/setregid wrapper transparently deals with 16-bit/32-bit variations across kernel versions。

本 test PR 在 starry kernel 上跑时, 暴露以下 starry-vs-Linux 行为差异, 已各自登记为上游 issue, 单文件 C 复现程序在 fork repo 的 `bug-starry-*` 分支内单独维护 (路径 `test-suit/.../bugfix/bug-starry-<name>/c/src/main.c`):

- rcore-os#713 — `setuid((uid_t)-1)` 在 starry 上被错误接受 (Linux 应返 EINVAL, 由 uid_valid 检查阻断)
- rcore-os#714 — `/proc/self/status` 在 starry loongarch64 上 vm_write EFAULT (Uid:/Gid:/Groups: 三行均受影响)
- rcore-os#715 — `setgroups(N, list)` 之后 `/proc/self/status` 的 Groups: 行不立即同步 (starry race)
- rcore-os#716 — apk + curl 组合在 starry loongarch64 上 600s 超时 ~90% flake (与本测试矩阵 stress 路径相关)

上述 bug 由 rcore-os#717 (fix-uid-gid-bugs, local 修) 与 rcore-os#718 (fix-uid-gid-deep, cross-subsystem) 这两个 fix PR 分别托管 — fix PR 同步把 bug-starry-* 注册进 4 arch bugfix toml 矩阵, CI 由 FAIL → PASS 转化作为修复验收。

rcore-os#727 当前 bucket: `pass=18 fail=0 skipping=30`。

原公式: `8 ruid × 8 euid × 6 state × 2 mode × 2 syscall = 1536` 缩减后: `6 ruid × 6 euid × 6 state × 2 mode × 2 syscall = 864` (commit `5248f83fe`)

- ruid 6: `0 / 1000 / 2000 / 65535 / NOCHG / boundary`
- euid 6: 同 ruid (枚举全笛卡尔积)
- state 6: `root_unmod / 3id_distinct / unpriv / mid_priv / after_setuid / NOCHG_baseline`
- mode 2: libc / raw
- syscall 2: setreuid / setregid

为什么 (原) 这么多: man D5 saved 规则的真假组合依赖 (ruid, euid, old.ruid) 三元组关系 → 必须全笛卡尔积才能覆盖; ruid × euid 笛卡尔积是验 D5 规则的最小覆盖单元; × 6 state: EPERM 决策依赖 old cred 起点。

为什么缩减: emulated aarch64 上 fork-heavy 矩阵 1536 case 接近 5 min, 大概率 CI 超时 (timeout=600s)。缩减 8→6 保留关键 boundary (0/NOCHG/16-bit max), 减少笛卡尔积规模到 ~56%。边界覆盖仍完整。

每 case 4-5 assertion ≈ 3500-4500 PASS (864 base)。

切换分支:
```bash
cd <repo-root>
git checkout test-uid-gid-re-setters
```

本地 Linux (host, WSL2):
```bash
cd <repo-root>/test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-re-setters/c
rm -rf build && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug && make
sudo ./test-uid-gid-re-setters
```

starry qemu (4 arch):
```bash
source .starry-env.sh && cargo starry test qemu --arch x86_64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch aarch64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch riscv64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch loongarch64 -c syscall
```

- 相关 PR: stacked 前 Group A/B (rcore-os#725..rcore-os#726); stacked 后 Group D/E (rcore-os#728..rcore-os#729); 衍生 fix rcore-os#718 (fix-uid-gid-deep) PR_SET/GET_DUMPABLE 支撑 core_dump_inhibit 模块

Signed-off-by: Leo Cheng <chengkelfan@qq.com>
@Lfan-ke
Lfan-ke force-pushed the test-uid-gid-re-setters branch from 72e23d3 to 3db9c83 Compare May 19, 2026 11:33

@mai-team-app mai-team-app Bot left a comment

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.

🔍 PR #727 第四次 Review — test(syscall): test-uid-gid-re-setters 915 PASS

📋 总览

本 PR 纯新增 C 测试代码(16 文件,+1684 行,0 删除),为 setreuid / setregid 两参 syscall 提供 man-first 地毯式测试。9 模块 / 40 测试函数 / 915 断言,覆盖 NOCHG sentinel、self-set、root 任意值、非特权 EPERM、raw vs libc 一致性、D5 saved-set-uid 规则(含反向 corner)、864 case 笛卡尔积矩阵、procfs 可见性、pthread cred 同步、core dump 抑制、errno 保护、supplementary groups 不变量。

✅ 代码质量与正确性

逐模块审查(全部通过):

  1. test_framework.h:简洁宏驱动框架,CHECK/CHECK_ERR/CHECK_OR_BUG 支持硬断言、errno 检查和已知 starry bug 软断言,设计合理。
  2. main.cCOLLECT 宏防御性处理可疑返回值(rc < 0 || rc > 1000000),9 模块串行收集,逻辑正确。
  3. setreuid.c (7 case):覆盖 NOCHG sentinel、self-set、root 任意值、非特权 EPERM、raw vs libc、supplementary groups 不变量。
  4. setregid.c (7 case):镜像 setreuid 测试,含 supplementary groups invariant。
  5. saved_id_semantics.c (6 case):D5 saved-set-uid 规则完整覆盖,特别是 D5 反向 corner case(ruid=-1 且 new.euid == old.ruid 时 saved 不更新),UID 和 GID 双维度。注释详尽说明了为什么需要先 setresuid seed r != s 才能区分 D5 反向行为。
  6. matrix.c (864 case)derive() 函数直接从 man page RE-D1~D5 推导期望值。从 1536 缩减到 864 保留关键边界(0/NOCHG/16-bit max)。state 初始化正确处理了 caps drop 时序。
  7. procfs_visibility.c (5 case):验证 /proc/self/status Uid:/Gid: 行同步,parse_proc_id_line() 使用 fopen+sscanf+fclose 模式安全。procfs (c) 的 seed-then-test 设计(setresuid(0,0,5000)setreuid(-1,0))精准验证 D5 反向在 procfs 的反映。
  8. nptl_sync.c (3 case)_Atomic 变量 + usleep(1000) 轮询,对非 NPTL 实现(starry)优雅降级(exit code 20 → KNOWN-STARRY-LIMITATION)。
  9. core_dump_inhibit.c (3 case):验证 euid 变化后 PR_GET_DUMPABLE 降为 0。对 prctl 不支持的情况有 fallback(exit code 20/21)。
  10. errno_preservation.c (5 case):验证成功路径不误改 errno,覆盖 NOCHG 和 self-set 两条路径。

🔒 安全与死锁分析

无死锁风险、无资源泄漏。所有模块使用 fork() + waitpid() 隔离模式,无共享锁、无 IPC 依赖。nptl_sync.c 的 _Atomic + 轮询无 mutex/condvar 依赖链。

📊 对既有功能的影响

零影响。纯新增代码,4 个 qemu-*.toml(x86_64/aarch64/riscv64/loongarch64)仅在 test_commands 数组追加一行 /usr/bin/test-uid-gid-re-setters。不修改任何 Rust 源代码、Cargo.toml 或构建配置。

🔧 本地验证

cd test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-re-setters/c
mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Debug && make

结果:10 个 C 源文件全部编译通过(GCC 13.3.0,-Wall -Wextra -Werror),零警告零错误。

🔄 合并冲突检查

已验证 git merge --no-commit origin/dev 成功,无冲突。PR 分支与 dev 基线可以自动合并。

🔍 CI 状态

当前 head (3db9c837e) 所有 check runs 结论为 skipped,导致 mergeable_state: unstable。前 3 次 CI 失败均为基础设施问题:

  • 第 1 次:SMP4 test-shm-deadlock timeout flake(#740
  • 第 2 次:cargo registry 网络 transient 错误
  • 第 3 次:self-hosted runner infra 问题

以上均与 PR 改动无关,作者已在 issue comments 中详细说明。本 PR 仅新增 C 测试代码,不触及 Rust 编译、toml 配置逻辑或构建系统。

📝 前次 review 评论处理

前次 review 提出 boundary.cfork() 失败时(pid < 0)缺少错误处理(非阻塞建议)。当前代码仍使用 if (pid > 0) 模式,fork 失败时 test case 静默跳过。这是非阻塞问题,因为:(1) CI 环境 fork 不应失败;(2) 失败时 waitpid_safely 也不会触发。建议后续改进但不阻塞合入。

结论

APPROVE — 高质量 man-first 测试套件。9 模块 915 断言覆盖 setreuid/setregid 完整语义空间。代码结构清晰,注释充分,无死锁风险,零影响现有功能。与 dev 无合并冲突,建议合入。

Powered by mimo-v2.5-pro

@Lfan-ke

Lfan-ke commented May 19, 2026

Copy link
Copy Markdown
Contributor Author

第 4 次 CI 失败说明 (本 PR 改动 0 触及)

3db9c837e retrigger#3 失败原因: test-timerfd-efault-wake 129.69s timeout (toml 120s) — 已立 cluster issue #759 (qemu-smp4 timing flake — test-futex-race + test-timerfd-efault-wake). 该测例在 host Linux 上也 design flake (parked reader 偶发不唤醒), starry SMP4 emulation 慢上加慢必然超时.

4 次 retrigger 历程 (每次不同非本 PR fail):

停止 retrigger (4 种不同 flake 已穷举, 每种均非本 PR scope). 本 sha 3db9c837e 内容自包含: host gcc 实测 915 PASS / 0 fail, 含 (g) supp groups invariant + 2 maicode nit fix + rebase 到最新 dev (含 #717 已 merge + #718 即将 merge 后的兼容). 请 maintainer 视情手动 rerun 或 force merge.

Lfan-ke added a commit to Lfan-ke/tgoskits that referenced this pull request May 19, 2026
Group B: `setuid` / `setgid` 两个直接 setter syscall 的 man-first 地毯式
测试, 本地 477 PASS。对应 rcore-os#726。

测试位置: `test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-direct-setters/c/src/`

模块总览 (10 模块 / 46 个 static void case 函数 / main.c 串联):
`setuid` / `setgid` / `cross_root_unprivileged` / `boundary` / `matrix` /
`procfs_visibility` / `nptl_sync` / `core_dump_inhibit` / `uid32_no_trunc` /
`errno_preservation`。

- `waitpid_safely()` helper — 循环 waitpid 处理 EINTR
- `setuid_idempotent_to_self()` — setuid(geteuid()) 后三字段 (r,e,s) 不变
  (man §DESCRIPTION POSIX_SAVED_IDS)
- `setuid_root_sets_all_three()` — root setuid(target) → r/e/s 全被设为
  target (man §DESCRIPTION root path)
- `setuid_returns_zero_on_success()` — 成功 rc == 0 (不只是 ≥0)
- `setuid_raw_syscall_matches_libc()` — 裸 syscall 与 libc 行为一致
- `setuid_unprivileged_eperm_in_child()` — 非特权 setuid 在 fork 子 →
  EPERM (man §ERRORS EPERM)
- `setuid_raw_vs_libc_failed_path()` — EPERM 失败路径下 raw rc/errno 与
  libc 严格相同
- `setuid_irreversibility()` — root setuid(non-zero) drop 后无法
  setuid(0) 回去 (man §DESCRIPTION 不可逆)
- `setuid_fork_child_independent()` — fork 子 setuid 改动不污染父 cred
- `setuid_self_euid_no_flush()` — 非特权 setuid(euid) 不清除 saved-set-uid
- `setuid_einval_uid_minus_one()` — uid=(uid_t)-1 在非特权下 → EPERM/
  EINVAL (软容忍 starry bug B)

- `waitpid_safely()` helper
- `setgid_idempotent_to_self()` — setgid(getegid()) 三字段不变
- `setgid_root_sets_all_three()` — root setgid(target) → r/e/s GID 全置
- `setgid_returns_zero_on_success()`
- `setgid_raw_syscall_matches_libc()`
- `setgid_unprivileged_eperm_in_child()` — 非特权 setgid(任意) → EPERM
- `setgid_raw_vs_libc_failed_path()`
- `setgid_alone_not_irreversible_caps_intact()` — 仅 setgid drop 不丢
  CAP_SETUID, 仍可 setuid
- `setgid_irreversible_after_setuid_drop()` — setuid drop 后 setgid 不可
  逆 (CAP_SETGID 丢)
- `setgid_fork_child_independent()`
- `setgid_self_egid_no_change()` — setgid(egid) 不动 saved-set-gid

- `waitpid_safely()` helper
- `root_setuid_arbitrary_updates_all()` — root setuid(任意值) → r/e/s
  全跟
- `unprivileged_setuid_self_succeeds()` — 非特权 setuid(self) 成功且字段
  不变
- `unprivileged_setuid_arbitrary_eperm()` — 非特权 setuid(非 {r,e,s}) →
  EPERM

- `setuid_boundary_values_root()` — root 路径下边界 uid:
  0/1/65535/65536/0x7FFFFFFE 全 OK
- `setuid_u32max_sentinel_unprivileged()` — 非特权 setuid((uid_t)-1)
  sentinel → EPERM/EINVAL (man §ERRORS EINVAL)
- `setuid_raw_syscall_returns_negative_errno()` — 裸 syscall 失败时 rc
  为 `-EPERM` (不是 -1+errno)

- `read_uid_cred()` / `read_gid_cred()` / `setup_state()` /
  `waitpid_safely()` helper
- `matrix_one()` — (syscall × state × input × mode) 笛卡尔单格 — 验
  rc/errno + getresuid 后续三槽

- `waitpid_safely_pv()` / `parse_proc_id_line()` helper
- `procfs_uid_baseline_root()` — 未改动时 /proc Uid: == (uid,euid,suid)
  一致 (proc(5) §Uid)
- `procfs_uid_after_setuid_root()` — root setuid 后 procfs 三字段同步
- `procfs_uid_after_setuid_drop()` — drop 后 procfs Uid: 三字段一致更新
- `procfs_gid_after_setgid()` — setgid 后 procfs Gid: 行同步
- `procfs_uid_after_setresuid()` — setresuid 后 procfs 三字段精确同步

- `nptl_setuid_main_visible_to_pthread()` — 主线程 setuid → 子 pthread
  getresuid 看到相同 cred (man §VERSIONS C library/kernel differences)
- `nptl_setgid_pthread_visible_to_main()`
- `nptl_concurrent_setuid_converges()`

- `waitpid_safely_cd()` helper
- `core_dump_baseline()` — 未改动时 PR_GET_DUMPABLE == 1 (man §NOTES)
- `core_dump_setuid_no_euid_change()` — setuid(euid) euid 未变 →
  dumpable 不变
- `core_dump_setuid_changes_euid()` — setuid 改变 euid → PR_GET_DUMPABLE
  → 0 (禁 core)

- `waitpid_safely()` helper
- `setuid_32bit_no_truncate()` — child setuid 大 UID (>16-bit) 后
  getresuid 不被截 (man §HISTORY 16→32-bit)
- `setgid_32bit_no_truncate()`
- `setuid_32bit_via_raw_syscall()`

- `setuid_self_errno_preserved()` / `setgid_self_errno_preserved()` /
  `setuid_root_errno_preserved()` / `setgid_root_errno_preserved()` /
  `raw_setuid_success_errno_preserved()` — 成功路径不动预置 errno

按顺序 COLLECT 10 个模块, 每模块 `*_run()` 返 `__fail`, main 汇总
TOTAL: N fail 收尾。

`setuid` §DESCRIPTION: "setuid() sets the effective user ID of the
calling process. If the calling process is privileged (more precisely:
if the process has the CAP_SETUID capability in its user namespace),
the real UID and saved set-user-ID are also set."

`setuid` §ERRORS:
- EAGAIN — temporary failure allocating kernel data structures
- EINVAL — The user ID specified in uid is not valid in this user
  namespace
- EPERM — The user is not privileged and uid does not match the real
  UID or saved set-user-ID of the calling process

`setuid` §NOTES: "If uid is different from the old effective UID, the
process will be forbidden from leaving core dumps."

`setuid` §VERSIONS (C library/kernel differences): NPTL wrapper 使用
signal-based 技术保证 process-wide thread cred 同步。

`setgid` §ERRORS: EINVAL (gid 不合法) / EPERM (无 CAP_SETGID 且 gid 不
匹配 real GID 或 saved set-group-ID)。

本 test PR 在 starry kernel 上跑时, 暴露以下 starry-vs-Linux 行为差异,
已各自登记为上游 issue, 单文件 C 复现程序在 fork repo 的 `bug-starry-*`
分支内单独维护 (路径 `test-suit/.../bugfix/bug-starry-<name>/c/src/main.c`):

- rcore-os#713 — `setuid((uid_t)-1)` 在 starry 上被错误接受
  (Linux 应返 EINVAL, 由 uid_valid 检查阻断)
- rcore-os#714 — `/proc/self/status` 在 starry loongarch64 上
  vm_write EFAULT (Uid:/Gid:/Groups: 三行均受影响)
- rcore-os#715 — `setgroups(N, list)` 之后 `/proc/self/status`
  的 Groups: 行不立即同步 (starry race)
- rcore-os#716 — apk + curl 组合在 starry loongarch64 上
  600s 超时 ~90% flake (与本测试矩阵 stress 路径相关)

上述 bug 由 rcore-os#717 (fix-uid-gid-bugs, local 修) 与
rcore-os#718 (fix-uid-gid-deep, cross-subsystem) 这两个 fix PR
分别托管 — fix PR 同步把 bug-starry-* 注册进 4 arch bugfix toml 矩阵,
CI 由 FAIL → PASS 转化作为修复验收。

rcore-os#726 当前 bucket: `pass=18 fail=0 skipping=30`。

公式: `2 syscall × 7 state × 16 input × 2 mode = 448`

- syscall 2: `setuid` / `setgid`
- state 7: `root_unmod` / `after_setresuid(0,1000,2000)` /
  `after_setresuid(0,0,1000)` / `unpriv(1000)` /
  `unpriv_3id(1000,2000,3000)` / `after_setgid(1000)` / `mixed_uid_gid`
- input 16: 0 / 1 / 100 / 999 / 1000 / 2000 / 3000 / 65535 / 65536 /
  100001 / 0x7FFFFFFE / NOCHG((uid_t)-1) / current_uid / current_euid /
  current_suid / boundary
- mode 2: libc / raw

为什么这么多维度:
- input 16 值: man §ERRORS EINVAL 触发只在 `(uid_t)-1`; EPERM 触发依赖
  input 是否在 {uid, euid, suid}; root 接受任意值 — 16 值覆盖 small /
  large / boundary (16-bit / 32-bit) / sentinel / self-set / cross-set
- state 7: 同一 `setuid(input)` 在不同 cred 起点上行为不同 — 覆盖 man
  §EPERM 决策树各分支
- mode 2: libc/raw 分离测验 ABI 一致性

每 case 3-5 assertion: rc / errno / 后续 getresuid r/e/s 三槽各验 /
fsuid procfs 检查 → 单 case 平均 4 assert → 总 ~1800 PASS。

切换分支:
```bash
cd <repo-root>
git checkout test-uid-gid-direct-setters
```

本地 Linux (host, WSL2):
```bash
cd <repo-root>/test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-direct-setters/c
rm -rf build && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug && make
sudo ./test-uid-gid-direct-setters
```

starry qemu (4 arch):
```bash
source .starry-env.sh && cargo starry test qemu --arch x86_64      -c syscall
source .starry-env.sh && cargo starry test qemu --arch aarch64     -c syscall
source .starry-env.sh && cargo starry test qemu --arch riscv64     -c syscall
source .starry-env.sh && cargo starry test qemu --arch loongarch64 -c syscall
```

- 相关 PR: stacked 前 Group A (rcore-os#725); stacked 后 Group C/D/E
  (rcore-os#727..rcore-os#729); 衍生 bug-* rcore-os#713 (setuid/setgid no uid_valid),
  fix rcore-os#717 (fix-uid-gid-bugs) 修复该 bug

Signed-off-by: Leo Cheng <chengkelfanke@gmail.com>
Lfan-ke added a commit to Lfan-ke/tgoskits that referenced this pull request May 19, 2026
Group C: `setreuid` / `setregid` 两参 (real + effective) setter syscall 的 man-first 地毯式测试, 本地 915 PASS。对应 rcore-os#727。

测试位置: `test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-re-setters/c/src/`

模块总览 (9 模块 / 40 个 static void case 函数 / main.c 串联): `setreuid` / `setregid` / `saved_id_semantics` / `boundary` / `matrix` / `procfs_visibility` / `nptl_sync` / `core_dump_inhibit` / `errno_preservation`。

- `waitpid_safely()` helper
- `setreuid_both_nochg_idempotent()` — setreuid((uid_t)-1,(uid_t)-1) 不改 r/e/s 三字段 (man §DESCRIPTION sentinel -1)
- `setreuid_ruid_self()` — setreuid(self,-1) 后 real == 原 real
- `setreuid_euid_self()` — setreuid(-1,self) 后 effective == 原 effective
- `setreuid_root_arbitrary()` — root setreuid(r,e) 任意值成功
- `setreuid_unpriv_eperm()` — 非特权越权 (非 {ruid,euid,suid}) → EPERM (man §ERRORS EPERM)
- `setreuid_raw_matches_libc()` — 裸 syscall 与 libc 一致 (rc + 三字段)
- `setreuid_keeps_supp_groups()` — **Linux invariant** (man 未显式但 kernel 行为强制): root → setgroups([7,8,9]) → setreuid(1000,1000) drop → getgroups 应仍 [7,8,9]; host gcc 实测 PASS, 防 starry sys_setreuid 错误清 cred.group_info 回归

- `waitpid_safely()` helper
- `setregid_both_nochg()` / `setregid_rgid_self()` / `setregid_egid_self()` / `setregid_root_arbitrary()` / `setregid_unpriv_eperm()` / `setregid_raw_matches_libc()` / `setregid_keeps_supp_groups()` — 镜像 setreuid 测试 (含 (g) supp groups invariant)

- `waitpid_safely()` helper
- `saved_id_ruid_set_updates_suid()` — ruid != -1 → suid := new euid (D5 规则触发) (man §DESCRIPTION)
- `saved_id_euid_set_to_nonruid_updates_suid()` — euid 改成 ≠ 旧 ruid → suid 更新
- `saved_id_both_set_root()` — root 同时改 r 和 e, suid 更新到新 euid
- `saved_id_both_nochg_no_change()` — (-1,-1) → r/e/s 全无变化
- `saved_id_d5_reverse_corner()` — D5 反向 corner: euid := 旧 ruid 时 saved 不应被更新
- `saved_id_d5_reverse_corner_gid()` — D5 corner GID 侧

- `waitpid_safely()` helper
- `boundary_raw_u32max_nochg()` — 裸 syscall 传 (uid_t)-1 sentinel 走 nochg 路径
- `boundary_root_extreme_values()` — root 极值 0/1000/65535/65536/ (u32max-1) (man §HISTORY 16→32-bit)
- `boundary_unpriv_extreme_eperm()` — 非特权传极值 → EPERM

- `setup_state()` / `read_uid_cred()` / `read_gid_cred()` / `waitpid_safely()` helper
- `matrix_one()` — (syscall × state × ruid × euid × mode) 笛卡尔单格 — 验 rc/errno + 后续 r/e/s 三槽 + D5 saved 规则

- `waitpid_safely_pv()` / `parse_proc_id_line()` helper
- `proc_uid_after_setreuid_full()` — setreuid(r,e) 全改 → /proc Uid: 三字段一致 (proc(5) §Uid + man §DESCRIPTION)
- `proc_uid_after_setreuid_nochg_r()` — setreuid(-1,e) → procfs real 不变、e 同步、s 按 D5
- `proc_uid_after_setreuid_nochg_same()` — setreuid(r,r) same-value → procfs 三字段一致
- `proc_gid_after_setregid_full()` — setregid 全改 → /proc Gid: 行同步
- `proc_compound_setre()` — 组合调用 (setreuid 再 setregid) 后 procfs 三字段全一致

- `nptl_setreuid_main_to_pthread()` — 主线程 setreuid → 子 pthread getresuid 看到相同 cred (man §VERSIONS C library/kernel differences)
- `nptl_setregid_pthread_to_main()`
- `nptl_concurrent_setreuid()`

- `cd_baseline()` — 未改动时 PR_GET_DUMPABLE == 1 (man §NOTES)
- `cd_setreuid_no_euid_change()` — euid 未变 → dumpable 不变
- `cd_setreuid_changes_euid()` — euid 变化 → PR_GET_DUMPABLE → 0

- `setreuid_nochg_errno_preserved()` / `setregid_nochg_errno_preserved()` / `setreuid_self_errno_preserved()` / `setregid_self_errno_preserved()` / `raw_setreuid_success_errno_preserved()`

按顺序 COLLECT 9 个模块, 每模块 `*_run()` 返 `__fail`, main 汇总 TOTAL: N fail。

`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."
- "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." (D5 规则)

`setreuid` §ERRORS:
- EINVAL — One or more of the target user or group IDs is not valid in this user namespace.
- EPERM — The calling process is not privileged and a change other than (i) swapping effective with real, (ii) setting one to the value of the other, (iii) setting effective to saved set-user-ID was specified.

`setreuid` §HISTORY: glibc setreuid/setregid wrapper transparently deals with 16-bit/32-bit variations across kernel versions。

本 test PR 在 starry kernel 上跑时, 暴露以下 starry-vs-Linux 行为差异, 已各自登记为上游 issue, 单文件 C 复现程序在 fork repo 的 `bug-starry-*` 分支内单独维护 (路径 `test-suit/.../bugfix/bug-starry-<name>/c/src/main.c`):

- rcore-os#713 — `setuid((uid_t)-1)` 在 starry 上被错误接受 (Linux 应返 EINVAL, 由 uid_valid 检查阻断)
- rcore-os#714 — `/proc/self/status` 在 starry loongarch64 上 vm_write EFAULT (Uid:/Gid:/Groups: 三行均受影响)
- rcore-os#715 — `setgroups(N, list)` 之后 `/proc/self/status` 的 Groups: 行不立即同步 (starry race)
- rcore-os#716 — apk + curl 组合在 starry loongarch64 上 600s 超时 ~90% flake (与本测试矩阵 stress 路径相关)

上述 bug 由 rcore-os#717 (fix-uid-gid-bugs, local 修) 与 rcore-os#718 (fix-uid-gid-deep, cross-subsystem) 这两个 fix PR 分别托管 — fix PR 同步把 bug-starry-* 注册进 4 arch bugfix toml 矩阵, CI 由 FAIL → PASS 转化作为修复验收。

rcore-os#727 当前 bucket: `pass=18 fail=0 skipping=30`。

原公式: `8 ruid × 8 euid × 6 state × 2 mode × 2 syscall = 1536` 缩减后: `6 ruid × 6 euid × 6 state × 2 mode × 2 syscall = 864` (commit `5248f83fe`)

- ruid 6: `0 / 1000 / 2000 / 65535 / NOCHG / boundary`
- euid 6: 同 ruid (枚举全笛卡尔积)
- state 6: `root_unmod / 3id_distinct / unpriv / mid_priv / after_setuid / NOCHG_baseline`
- mode 2: libc / raw
- syscall 2: setreuid / setregid

为什么 (原) 这么多: man D5 saved 规则的真假组合依赖 (ruid, euid, old.ruid) 三元组关系 → 必须全笛卡尔积才能覆盖; ruid × euid 笛卡尔积是验 D5 规则的最小覆盖单元; × 6 state: EPERM 决策依赖 old cred 起点。

为什么缩减: emulated aarch64 上 fork-heavy 矩阵 1536 case 接近 5 min, 大概率 CI 超时 (timeout=600s)。缩减 8→6 保留关键 boundary (0/NOCHG/16-bit max), 减少笛卡尔积规模到 ~56%。边界覆盖仍完整。

每 case 4-5 assertion ≈ 3500-4500 PASS (864 base)。

切换分支:
```bash
cd <repo-root>
git checkout test-uid-gid-re-setters
```

本地 Linux (host, WSL2):
```bash
cd <repo-root>/test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-re-setters/c
rm -rf build && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug && make
sudo ./test-uid-gid-re-setters
```

starry qemu (4 arch):
```bash
source .starry-env.sh && cargo starry test qemu --arch x86_64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch aarch64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch riscv64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch loongarch64 -c syscall
```

- 相关 PR: stacked 前 Group A/B (rcore-os#725..rcore-os#726); stacked 后 Group D/E (rcore-os#728..rcore-os#729); 衍生 fix rcore-os#718 (fix-uid-gid-deep) PR_SET/GET_DUMPABLE 支撑 core_dump_inhibit 模块

Signed-off-by: Leo Cheng <chengkelfan@qq.com>
@Lfan-ke
Lfan-ke force-pushed the test-uid-gid-re-setters branch from 3db9c83 to c39b8ea Compare May 19, 2026 13:00

@mai-team-app mai-team-app Bot left a comment

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.

🔍 PR #727 第五次 Review — test(syscall): test-uid-gid-re-setters 915 PASS

📋 总览

本 PR 纯新增 C 测试代码(16 文件,+1684 行,0 删除),为 setreuid / setregid 两参 syscall 提供 man-first 地毯式测试。9 模块 / 40 测试函数 / 915 断言,覆盖 NOCHG sentinel、self-set、root 任意值、非特权 EPERM、raw vs libc 一致性、D5 saved-set-uid 规则(含反向 corner)、864 case 笛卡尔积矩阵、procfs 可见性、pthread cred 同步、core dump 抑制、errno 保护、supplementary groups 不变量。

✅ 代码质量与正确性

逐模块审查(全部通过):

  1. test_framework.h:简洁宏驱动框架,CHECK/CHECK_ERR/CHECK_OR_BUG 支持硬断言、errno 检查和已知 starry bug 软断言。#pragma once 防重复包含。
  2. main.cCOLLECT 宏防御性处理可疑返回值(rc < 0 || rc > 1000000),9 模块串行收集,fail 累加。逻辑正确。
  3. setreuid.c (7 case):覆盖 NOCHG sentinel、self-set、root 任意值、非特权 EPERM(验证 ruid 仅 {r,e} 不含 s 的差异)、raw vs libc、supplementary groups 不变量。
  4. setregid.c (7 case):镜像 setreuid 测试,含 supplementary groups invariant。
  5. saved_id_semantics.c (6 case):D5 saved-set-uid 规则完整覆盖,特别是 D5 反向 corner case(ruid=-1 且 new.euid == old.ruid 时 saved 不更新),需先 setresuid seed r ≠ s 才能区分。UID 和 GID 双维度覆盖。
  6. matrix.c (864 case)derive() 函数从 man page RE-D1~D5 推导期望值。从 1536 缩减到 864 保留关键边界(0/NOCHG/16-bit max)。state 初始化正确处理 caps drop 时序。
  7. procfs_visibility.c (5 case):验证 /proc/self/status Uid:/Gid: 行同步。parse_proc_id_line() 使用 fopen+sscanf+fclose 安全。compound 验证(setreuid + setregid 组合后 procfs 一致性)设计精巧。
  8. nptl_sync.c (3 case)_Atomic + usleep(1000) 轮询,对非 NPTL 实现(starry)优雅降级(exit code 20 → KNOWN-STARRY-LIMITATION)。无 mutex/condvar 依赖链。
  9. core_dump_inhibit.c (3 case):验证 euid 变化后 PR_GET_DUMPABLE 降为 0。对 prctl 不支持的情况有 fallback(exit code 20/21)。
  10. errno_preservation.c (5 case):验证成功路径不误改 errno,覆盖 NOCHG 和 self-set 两条路径,libc 和 raw syscall 双模式。

🔒 安全与死锁分析

无死锁风险、无资源泄漏。所有模块使用 fork() + waitpid() 隔离模式,无共享锁、无 IPC 依赖。nptl_sync.c 的 _Atomic + 轮询无 mutex/condvar 依赖链。

📊 对既有功能的影响

零影响。纯新增代码,4 个 qemu-*.toml(x86_64/aarch64/riscv64/loongarch64)仅在 test_commands 数组追加一行 /usr/bin/test-uid-gid-re-setters,位于 test-stat-familytest-umask 之间。不修改任何 Rust 源代码、Cargo.toml 或构建配置。

🔧 本地验证

cd test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-re-setters/c
mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Debug && make

结果:10 个 C 源文件全部编译通过(GCC 13.3.0,-Wall -Wextra -Werror),零警告零错误。

🔄 合并冲突检查

已验证 git merge --no-commit --no-ff origin/dev 成功,无冲突mergeable: truemergeable_state: clean。PR 分支与 dev 基线可自动合并,无需手动 rebase 或冲突处理。

🔍 CI 状态

当前 head (c39b8eab) 所有 check runs 结论为 skipped。作者在 issue comments 中详细说明了 4 次 retrigger 失败历程,每次均是不同类型的基础设施问题:

  • 第 1 次:test-shm-deadlock SMP4 timeout flake(#740
  • 第 2 次:crates.io cargo registry 网络 transient 错误
  • 第 3 次:axvisor self-hosted runner infra 问题
  • 第 4 次:test-timerfd-efault-wake SMP4 timeout(#759

以上均与 PR 改动无关。本 PR 仅新增 C 测试代码,不触及 Rust 编译、toml 配置逻辑或构建系统。

📝 小建议(不阻塞)

  1. waitpid_safely() 在 8 个 .c 文件中重复定义(均为 static)。当前自包含设计可接受,后续模块增长时可考虑提取到 test_framework.h
  2. boundary.cfork() 失败(pid < 0)时使用 if (pid > 0) 模式,fork 失败时 case 静默跳过。建议后续加 else 分支打印 warning。非阻塞问题。

结论

APPROVE — 高质量 man-first 测试套件。9 模块 915 断言覆盖 setreuid/setregid 完整语义空间。代码结构清晰,注释充分,无死锁风险,零影响现有功能。与 dev 无合并冲突。这是第 5 次 review,代码自上次 review 以来未变。建议合入。

Powered by mimo-v2.5-pro

Lfan-ke added a commit to Lfan-ke/tgoskits that referenced this pull request May 21, 2026
Group C: `setreuid` / `setregid` 两参 (real + effective) setter syscall 的 man-first 地毯式测试, 本地 915 PASS。对应 rcore-os#727。

测试位置: `test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-re-setters/c/src/`

模块总览 (9 模块 / 40 个 static void case 函数 / main.c 串联): `setreuid` / `setregid` / `saved_id_semantics` / `boundary` / `matrix` / `procfs_visibility` / `nptl_sync` / `core_dump_inhibit` / `errno_preservation`。

- `waitpid_safely()` helper
- `setreuid_both_nochg_idempotent()` — setreuid((uid_t)-1,(uid_t)-1) 不改 r/e/s 三字段 (man §DESCRIPTION sentinel -1)
- `setreuid_ruid_self()` — setreuid(self,-1) 后 real == 原 real
- `setreuid_euid_self()` — setreuid(-1,self) 后 effective == 原 effective
- `setreuid_root_arbitrary()` — root setreuid(r,e) 任意值成功
- `setreuid_unpriv_eperm()` — 非特权越权 (非 {ruid,euid,suid}) → EPERM (man §ERRORS EPERM)
- `setreuid_raw_matches_libc()` — 裸 syscall 与 libc 一致 (rc + 三字段)
- `setreuid_keeps_supp_groups()` — **Linux invariant** (man 未显式但 kernel 行为强制): root → setgroups([7,8,9]) → setreuid(1000,1000) drop → getgroups 应仍 [7,8,9]; host gcc 实测 PASS, 防 starry sys_setreuid 错误清 cred.group_info 回归

- `waitpid_safely()` helper
- `setregid_both_nochg()` / `setregid_rgid_self()` / `setregid_egid_self()` / `setregid_root_arbitrary()` / `setregid_unpriv_eperm()` / `setregid_raw_matches_libc()` / `setregid_keeps_supp_groups()` — 镜像 setreuid 测试 (含 (g) supp groups invariant)

- `waitpid_safely()` helper
- `saved_id_ruid_set_updates_suid()` — ruid != -1 → suid := new euid (D5 规则触发) (man §DESCRIPTION)
- `saved_id_euid_set_to_nonruid_updates_suid()` — euid 改成 ≠ 旧 ruid → suid 更新
- `saved_id_both_set_root()` — root 同时改 r 和 e, suid 更新到新 euid
- `saved_id_both_nochg_no_change()` — (-1,-1) → r/e/s 全无变化
- `saved_id_d5_reverse_corner()` — D5 反向 corner: euid := 旧 ruid 时 saved 不应被更新
- `saved_id_d5_reverse_corner_gid()` — D5 corner GID 侧

- `waitpid_safely()` helper
- `boundary_raw_u32max_nochg()` — 裸 syscall 传 (uid_t)-1 sentinel 走 nochg 路径
- `boundary_root_extreme_values()` — root 极值 0/1000/65535/65536/ (u32max-1) (man §HISTORY 16→32-bit)
- `boundary_unpriv_extreme_eperm()` — 非特权传极值 → EPERM

- `setup_state()` / `read_uid_cred()` / `read_gid_cred()` / `waitpid_safely()` helper
- `matrix_one()` — (syscall × state × ruid × euid × mode) 笛卡尔单格 — 验 rc/errno + 后续 r/e/s 三槽 + D5 saved 规则

- `waitpid_safely_pv()` / `parse_proc_id_line()` helper
- `proc_uid_after_setreuid_full()` — setreuid(r,e) 全改 → /proc Uid: 三字段一致 (proc(5) §Uid + man §DESCRIPTION)
- `proc_uid_after_setreuid_nochg_r()` — setreuid(-1,e) → procfs real 不变、e 同步、s 按 D5
- `proc_uid_after_setreuid_nochg_same()` — setreuid(r,r) same-value → procfs 三字段一致
- `proc_gid_after_setregid_full()` — setregid 全改 → /proc Gid: 行同步
- `proc_compound_setre()` — 组合调用 (setreuid 再 setregid) 后 procfs 三字段全一致

- `nptl_setreuid_main_to_pthread()` — 主线程 setreuid → 子 pthread getresuid 看到相同 cred (man §VERSIONS C library/kernel differences)
- `nptl_setregid_pthread_to_main()`
- `nptl_concurrent_setreuid()`

- `cd_baseline()` — 未改动时 PR_GET_DUMPABLE == 1 (man §NOTES)
- `cd_setreuid_no_euid_change()` — euid 未变 → dumpable 不变
- `cd_setreuid_changes_euid()` — euid 变化 → PR_GET_DUMPABLE → 0

- `setreuid_nochg_errno_preserved()` / `setregid_nochg_errno_preserved()` / `setreuid_self_errno_preserved()` / `setregid_self_errno_preserved()` / `raw_setreuid_success_errno_preserved()`

按顺序 COLLECT 9 个模块, 每模块 `*_run()` 返 `__fail`, main 汇总 TOTAL: N fail。

`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."
- "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." (D5 规则)

`setreuid` §ERRORS:
- EINVAL — One or more of the target user or group IDs is not valid in this user namespace.
- EPERM — The calling process is not privileged and a change other than (i) swapping effective with real, (ii) setting one to the value of the other, (iii) setting effective to saved set-user-ID was specified.

`setreuid` §HISTORY: glibc setreuid/setregid wrapper transparently deals with 16-bit/32-bit variations across kernel versions。

本 test PR 在 starry kernel 上跑时, 暴露以下 starry-vs-Linux 行为差异, 已各自登记为上游 issue, 单文件 C 复现程序在 fork repo 的 `bug-starry-*` 分支内单独维护 (路径 `test-suit/.../bugfix/bug-starry-<name>/c/src/main.c`):

- rcore-os#713 — `setuid((uid_t)-1)` 在 starry 上被错误接受 (Linux 应返 EINVAL, 由 uid_valid 检查阻断)
- rcore-os#714 — `/proc/self/status` 在 starry loongarch64 上 vm_write EFAULT (Uid:/Gid:/Groups: 三行均受影响)
- rcore-os#715 — `setgroups(N, list)` 之后 `/proc/self/status` 的 Groups: 行不立即同步 (starry race)
- rcore-os#716 — apk + curl 组合在 starry loongarch64 上 600s 超时 ~90% flake (与本测试矩阵 stress 路径相关)

上述 bug 由 rcore-os#717 (fix-uid-gid-bugs, local 修) 与 rcore-os#718 (fix-uid-gid-deep, cross-subsystem) 这两个 fix PR 分别托管 — fix PR 同步把 bug-starry-* 注册进 4 arch bugfix toml 矩阵, CI 由 FAIL → PASS 转化作为修复验收。

rcore-os#727 当前 bucket: `pass=18 fail=0 skipping=30`。

原公式: `8 ruid × 8 euid × 6 state × 2 mode × 2 syscall = 1536` 缩减后: `6 ruid × 6 euid × 6 state × 2 mode × 2 syscall = 864` (commit `5248f83fe`)

- ruid 6: `0 / 1000 / 2000 / 65535 / NOCHG / boundary`
- euid 6: 同 ruid (枚举全笛卡尔积)
- state 6: `root_unmod / 3id_distinct / unpriv / mid_priv / after_setuid / NOCHG_baseline`
- mode 2: libc / raw
- syscall 2: setreuid / setregid

为什么 (原) 这么多: man D5 saved 规则的真假组合依赖 (ruid, euid, old.ruid) 三元组关系 → 必须全笛卡尔积才能覆盖; ruid × euid 笛卡尔积是验 D5 规则的最小覆盖单元; × 6 state: EPERM 决策依赖 old cred 起点。

为什么缩减: emulated aarch64 上 fork-heavy 矩阵 1536 case 接近 5 min, 大概率 CI 超时 (timeout=600s)。缩减 8→6 保留关键 boundary (0/NOCHG/16-bit max), 减少笛卡尔积规模到 ~56%。边界覆盖仍完整。

每 case 4-5 assertion ≈ 3500-4500 PASS (864 base)。

切换分支:
```bash
cd <repo-root>
git checkout test-uid-gid-re-setters
```

本地 Linux (host, WSL2):
```bash
cd <repo-root>/test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-re-setters/c
rm -rf build && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug && make
sudo ./test-uid-gid-re-setters
```

starry qemu (4 arch):
```bash
source .starry-env.sh && cargo starry test qemu --arch x86_64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch aarch64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch riscv64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch loongarch64 -c syscall
```

- 相关 PR: stacked 前 Group A/B (rcore-os#725..rcore-os#726); stacked 后 Group D/E (rcore-os#728..rcore-os#729); 衍生 fix rcore-os#718 (fix-uid-gid-deep) PR_SET/GET_DUMPABLE 支撑 core_dump_inhibit 模块

Signed-off-by: Leo Cheng <chengkelfan@qq.com>
@Lfan-ke
Lfan-ke force-pushed the test-uid-gid-re-setters branch from c39b8ea to bd16875 Compare May 21, 2026 11:00
Lfan-ke added a commit to Lfan-ke/tgoskits that referenced this pull request May 21, 2026
Group B: `setuid` / `setgid` 两个直接 setter syscall 的 man-first 地毯式
测试, 本地 477 PASS。对应 rcore-os#726。

测试位置: `test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-direct-setters/c/src/`

模块总览 (10 模块 / 46 个 static void case 函数 / main.c 串联):
`setuid` / `setgid` / `cross_root_unprivileged` / `boundary` / `matrix` /
`procfs_visibility` / `nptl_sync` / `core_dump_inhibit` / `uid32_no_trunc` /
`errno_preservation`。

- `waitpid_safely()` helper — 循环 waitpid 处理 EINTR
- `setuid_idempotent_to_self()` — setuid(geteuid()) 后三字段 (r,e,s) 不变
  (man §DESCRIPTION POSIX_SAVED_IDS)
- `setuid_root_sets_all_three()` — root setuid(target) → r/e/s 全被设为
  target (man §DESCRIPTION root path)
- `setuid_returns_zero_on_success()` — 成功 rc == 0 (不只是 ≥0)
- `setuid_raw_syscall_matches_libc()` — 裸 syscall 与 libc 行为一致
- `setuid_unprivileged_eperm_in_child()` — 非特权 setuid 在 fork 子 →
  EPERM (man §ERRORS EPERM)
- `setuid_raw_vs_libc_failed_path()` — EPERM 失败路径下 raw rc/errno 与
  libc 严格相同
- `setuid_irreversibility()` — root setuid(non-zero) drop 后无法
  setuid(0) 回去 (man §DESCRIPTION 不可逆)
- `setuid_fork_child_independent()` — fork 子 setuid 改动不污染父 cred
- `setuid_self_euid_no_flush()` — 非特权 setuid(euid) 不清除 saved-set-uid
- `setuid_einval_uid_minus_one()` — uid=(uid_t)-1 在非特权下 → EPERM/
  EINVAL (软容忍 starry bug B)

- `waitpid_safely()` helper
- `setgid_idempotent_to_self()` — setgid(getegid()) 三字段不变
- `setgid_root_sets_all_three()` — root setgid(target) → r/e/s GID 全置
- `setgid_returns_zero_on_success()`
- `setgid_raw_syscall_matches_libc()`
- `setgid_unprivileged_eperm_in_child()` — 非特权 setgid(任意) → EPERM
- `setgid_raw_vs_libc_failed_path()`
- `setgid_alone_not_irreversible_caps_intact()` — 仅 setgid drop 不丢
  CAP_SETUID, 仍可 setuid
- `setgid_irreversible_after_setuid_drop()` — setuid drop 后 setgid 不可
  逆 (CAP_SETGID 丢)
- `setgid_fork_child_independent()`
- `setgid_self_egid_no_change()` — setgid(egid) 不动 saved-set-gid

- `waitpid_safely()` helper
- `root_setuid_arbitrary_updates_all()` — root setuid(任意值) → r/e/s
  全跟
- `unprivileged_setuid_self_succeeds()` — 非特权 setuid(self) 成功且字段
  不变
- `unprivileged_setuid_arbitrary_eperm()` — 非特权 setuid(非 {r,e,s}) →
  EPERM

- `setuid_boundary_values_root()` — root 路径下边界 uid:
  0/1/65535/65536/0x7FFFFFFE 全 OK
- `setuid_u32max_sentinel_unprivileged()` — 非特权 setuid((uid_t)-1)
  sentinel → EPERM/EINVAL (man §ERRORS EINVAL)
- `setuid_raw_syscall_returns_negative_errno()` — 裸 syscall 失败时 rc
  为 `-EPERM` (不是 -1+errno)

- `read_uid_cred()` / `read_gid_cred()` / `setup_state()` /
  `waitpid_safely()` helper
- `matrix_one()` — (syscall × state × input × mode) 笛卡尔单格 — 验
  rc/errno + getresuid 后续三槽

- `waitpid_safely_pv()` / `parse_proc_id_line()` helper
- `procfs_uid_baseline_root()` — 未改动时 /proc Uid: == (uid,euid,suid)
  一致 (proc(5) §Uid)
- `procfs_uid_after_setuid_root()` — root setuid 后 procfs 三字段同步
- `procfs_uid_after_setuid_drop()` — drop 后 procfs Uid: 三字段一致更新
- `procfs_gid_after_setgid()` — setgid 后 procfs Gid: 行同步
- `procfs_uid_after_setresuid()` — setresuid 后 procfs 三字段精确同步

- `nptl_setuid_main_visible_to_pthread()` — 主线程 setuid → 子 pthread
  getresuid 看到相同 cred (man §VERSIONS C library/kernel differences)
- `nptl_setgid_pthread_visible_to_main()`
- `nptl_concurrent_setuid_converges()`

- `waitpid_safely_cd()` helper
- `core_dump_baseline()` — 未改动时 PR_GET_DUMPABLE == 1 (man §NOTES)
- `core_dump_setuid_no_euid_change()` — setuid(euid) euid 未变 →
  dumpable 不变
- `core_dump_setuid_changes_euid()` — setuid 改变 euid → PR_GET_DUMPABLE
  → 0 (禁 core)

- `waitpid_safely()` helper
- `setuid_32bit_no_truncate()` — child setuid 大 UID (>16-bit) 后
  getresuid 不被截 (man §HISTORY 16→32-bit)
- `setgid_32bit_no_truncate()`
- `setuid_32bit_via_raw_syscall()`

- `setuid_self_errno_preserved()` / `setgid_self_errno_preserved()` /
  `setuid_root_errno_preserved()` / `setgid_root_errno_preserved()` /
  `raw_setuid_success_errno_preserved()` — 成功路径不动预置 errno

按顺序 COLLECT 10 个模块, 每模块 `*_run()` 返 `__fail`, main 汇总
TOTAL: N fail 收尾。

`setuid` §DESCRIPTION: "setuid() sets the effective user ID of the
calling process. If the calling process is privileged (more precisely:
if the process has the CAP_SETUID capability in its user namespace),
the real UID and saved set-user-ID are also set."

`setuid` §ERRORS:
- EAGAIN — temporary failure allocating kernel data structures
- EINVAL — The user ID specified in uid is not valid in this user
  namespace
- EPERM — The user is not privileged and uid does not match the real
  UID or saved set-user-ID of the calling process

`setuid` §NOTES: "If uid is different from the old effective UID, the
process will be forbidden from leaving core dumps."

`setuid` §VERSIONS (C library/kernel differences): NPTL wrapper 使用
signal-based 技术保证 process-wide thread cred 同步。

`setgid` §ERRORS: EINVAL (gid 不合法) / EPERM (无 CAP_SETGID 且 gid 不
匹配 real GID 或 saved set-group-ID)。

本 test PR 在 starry kernel 上跑时, 暴露以下 starry-vs-Linux 行为差异,
已各自登记为上游 issue, 单文件 C 复现程序在 fork repo 的 `bug-starry-*`
分支内单独维护 (路径 `test-suit/.../bugfix/bug-starry-<name>/c/src/main.c`):

- rcore-os#713 — `setuid((uid_t)-1)` 在 starry 上被错误接受
  (Linux 应返 EINVAL, 由 uid_valid 检查阻断)
- rcore-os#714 — `/proc/self/status` 在 starry loongarch64 上
  vm_write EFAULT (Uid:/Gid:/Groups: 三行均受影响)
- rcore-os#715 — `setgroups(N, list)` 之后 `/proc/self/status`
  的 Groups: 行不立即同步 (starry race)
- rcore-os#716 — apk + curl 组合在 starry loongarch64 上
  600s 超时 ~90% flake (与本测试矩阵 stress 路径相关)

上述 bug 由 rcore-os#717 (fix-uid-gid-bugs, local 修) 与
rcore-os#718 (fix-uid-gid-deep, cross-subsystem) 这两个 fix PR
分别托管 — fix PR 同步把 bug-starry-* 注册进 4 arch bugfix toml 矩阵,
CI 由 FAIL → PASS 转化作为修复验收。

rcore-os#726 当前 bucket: `pass=18 fail=0 skipping=30`。

公式: `2 syscall × 7 state × 16 input × 2 mode = 448`

- syscall 2: `setuid` / `setgid`
- state 7: `root_unmod` / `after_setresuid(0,1000,2000)` /
  `after_setresuid(0,0,1000)` / `unpriv(1000)` /
  `unpriv_3id(1000,2000,3000)` / `after_setgid(1000)` / `mixed_uid_gid`
- input 16: 0 / 1 / 100 / 999 / 1000 / 2000 / 3000 / 65535 / 65536 /
  100001 / 0x7FFFFFFE / NOCHG((uid_t)-1) / current_uid / current_euid /
  current_suid / boundary
- mode 2: libc / raw

为什么这么多维度:
- input 16 值: man §ERRORS EINVAL 触发只在 `(uid_t)-1`; EPERM 触发依赖
  input 是否在 {uid, euid, suid}; root 接受任意值 — 16 值覆盖 small /
  large / boundary (16-bit / 32-bit) / sentinel / self-set / cross-set
- state 7: 同一 `setuid(input)` 在不同 cred 起点上行为不同 — 覆盖 man
  §EPERM 决策树各分支
- mode 2: libc/raw 分离测验 ABI 一致性

每 case 3-5 assertion: rc / errno / 后续 getresuid r/e/s 三槽各验 /
fsuid procfs 检查 → 单 case 平均 4 assert → 总 ~1800 PASS。

切换分支:
```bash
cd <repo-root>
git checkout test-uid-gid-direct-setters
```

本地 Linux (host, WSL2):
```bash
cd <repo-root>/test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-direct-setters/c
rm -rf build && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug && make
sudo ./test-uid-gid-direct-setters
```

starry qemu (4 arch):
```bash
source .starry-env.sh && cargo starry test qemu --arch x86_64      -c syscall
source .starry-env.sh && cargo starry test qemu --arch aarch64     -c syscall
source .starry-env.sh && cargo starry test qemu --arch riscv64     -c syscall
source .starry-env.sh && cargo starry test qemu --arch loongarch64 -c syscall
```

- 相关 PR: stacked 前 Group A (rcore-os#725); stacked 后 Group C/D/E
  (rcore-os#727..rcore-os#729); 衍生 bug-* rcore-os#713 (setuid/setgid no uid_valid),
  fix rcore-os#717 (fix-uid-gid-bugs) 修复该 bug

Signed-off-by: Leo Cheng <chengkelfanke@gmail.com>
Lfan-ke added a commit to Lfan-ke/tgoskits that referenced this pull request May 21, 2026
Group B: `setuid` / `setgid` 两个直接 setter syscall 的 man-first 地毯式
测试, 本地 477 PASS。对应 rcore-os#726。

测试位置: `test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-direct-setters/c/src/`

模块总览 (10 模块 / 46 个 static void case 函数 / main.c 串联):
`setuid` / `setgid` / `cross_root_unprivileged` / `boundary` / `matrix` /
`procfs_visibility` / `nptl_sync` / `core_dump_inhibit` / `uid32_no_trunc` /
`errno_preservation`。

- `waitpid_safely()` helper — 循环 waitpid 处理 EINTR
- `setuid_idempotent_to_self()` — setuid(geteuid()) 后三字段 (r,e,s) 不变
  (man §DESCRIPTION POSIX_SAVED_IDS)
- `setuid_root_sets_all_three()` — root setuid(target) → r/e/s 全被设为
  target (man §DESCRIPTION root path)
- `setuid_returns_zero_on_success()` — 成功 rc == 0 (不只是 ≥0)
- `setuid_raw_syscall_matches_libc()` — 裸 syscall 与 libc 行为一致
- `setuid_unprivileged_eperm_in_child()` — 非特权 setuid 在 fork 子 →
  EPERM (man §ERRORS EPERM)
- `setuid_raw_vs_libc_failed_path()` — EPERM 失败路径下 raw rc/errno 与
  libc 严格相同
- `setuid_irreversibility()` — root setuid(non-zero) drop 后无法
  setuid(0) 回去 (man §DESCRIPTION 不可逆)
- `setuid_fork_child_independent()` — fork 子 setuid 改动不污染父 cred
- `setuid_self_euid_no_flush()` — 非特权 setuid(euid) 不清除 saved-set-uid
- `setuid_einval_uid_minus_one()` — uid=(uid_t)-1 在非特权下 → EPERM/
  EINVAL (软容忍 starry bug B)

- `waitpid_safely()` helper
- `setgid_idempotent_to_self()` — setgid(getegid()) 三字段不变
- `setgid_root_sets_all_three()` — root setgid(target) → r/e/s GID 全置
- `setgid_returns_zero_on_success()`
- `setgid_raw_syscall_matches_libc()`
- `setgid_unprivileged_eperm_in_child()` — 非特权 setgid(任意) → EPERM
- `setgid_raw_vs_libc_failed_path()`
- `setgid_alone_not_irreversible_caps_intact()` — 仅 setgid drop 不丢
  CAP_SETUID, 仍可 setuid
- `setgid_irreversible_after_setuid_drop()` — setuid drop 后 setgid 不可
  逆 (CAP_SETGID 丢)
- `setgid_fork_child_independent()`
- `setgid_self_egid_no_change()` — setgid(egid) 不动 saved-set-gid

- `waitpid_safely()` helper
- `root_setuid_arbitrary_updates_all()` — root setuid(任意值) → r/e/s
  全跟
- `unprivileged_setuid_self_succeeds()` — 非特权 setuid(self) 成功且字段
  不变
- `unprivileged_setuid_arbitrary_eperm()` — 非特权 setuid(非 {r,e,s}) →
  EPERM

- `setuid_boundary_values_root()` — root 路径下边界 uid:
  0/1/65535/65536/0x7FFFFFFE 全 OK
- `setuid_u32max_sentinel_unprivileged()` — 非特权 setuid((uid_t)-1)
  sentinel → EPERM/EINVAL (man §ERRORS EINVAL)
- `setuid_raw_syscall_returns_negative_errno()` — 裸 syscall 失败时 rc
  为 `-EPERM` (不是 -1+errno)

- `read_uid_cred()` / `read_gid_cred()` / `setup_state()` /
  `waitpid_safely()` helper
- `matrix_one()` — (syscall × state × input × mode) 笛卡尔单格 — 验
  rc/errno + getresuid 后续三槽

- `waitpid_safely_pv()` / `parse_proc_id_line()` helper
- `procfs_uid_baseline_root()` — 未改动时 /proc Uid: == (uid,euid,suid)
  一致 (proc(5) §Uid)
- `procfs_uid_after_setuid_root()` — root setuid 后 procfs 三字段同步
- `procfs_uid_after_setuid_drop()` — drop 后 procfs Uid: 三字段一致更新
- `procfs_gid_after_setgid()` — setgid 后 procfs Gid: 行同步
- `procfs_uid_after_setresuid()` — setresuid 后 procfs 三字段精确同步

- `nptl_setuid_main_visible_to_pthread()` — 主线程 setuid → 子 pthread
  getresuid 看到相同 cred (man §VERSIONS C library/kernel differences)
- `nptl_setgid_pthread_visible_to_main()`
- `nptl_concurrent_setuid_converges()`

- `waitpid_safely_cd()` helper
- `core_dump_baseline()` — 未改动时 PR_GET_DUMPABLE == 1 (man §NOTES)
- `core_dump_setuid_no_euid_change()` — setuid(euid) euid 未变 →
  dumpable 不变
- `core_dump_setuid_changes_euid()` — setuid 改变 euid → PR_GET_DUMPABLE
  → 0 (禁 core)

- `waitpid_safely()` helper
- `setuid_32bit_no_truncate()` — child setuid 大 UID (>16-bit) 后
  getresuid 不被截 (man §HISTORY 16→32-bit)
- `setgid_32bit_no_truncate()`
- `setuid_32bit_via_raw_syscall()`

- `setuid_self_errno_preserved()` / `setgid_self_errno_preserved()` /
  `setuid_root_errno_preserved()` / `setgid_root_errno_preserved()` /
  `raw_setuid_success_errno_preserved()` — 成功路径不动预置 errno

按顺序 COLLECT 10 个模块, 每模块 `*_run()` 返 `__fail`, main 汇总
TOTAL: N fail 收尾。

`setuid` §DESCRIPTION: "setuid() sets the effective user ID of the
calling process. If the calling process is privileged (more precisely:
if the process has the CAP_SETUID capability in its user namespace),
the real UID and saved set-user-ID are also set."

`setuid` §ERRORS:
- EAGAIN — temporary failure allocating kernel data structures
- EINVAL — The user ID specified in uid is not valid in this user
  namespace
- EPERM — The user is not privileged and uid does not match the real
  UID or saved set-user-ID of the calling process

`setuid` §NOTES: "If uid is different from the old effective UID, the
process will be forbidden from leaving core dumps."

`setuid` §VERSIONS (C library/kernel differences): NPTL wrapper 使用
signal-based 技术保证 process-wide thread cred 同步。

`setgid` §ERRORS: EINVAL (gid 不合法) / EPERM (无 CAP_SETGID 且 gid 不
匹配 real GID 或 saved set-group-ID)。

本 test PR 在 starry kernel 上跑时, 暴露以下 starry-vs-Linux 行为差异,
已各自登记为上游 issue, 单文件 C 复现程序在 fork repo 的 `bug-starry-*`
分支内单独维护 (路径 `test-suit/.../bugfix/bug-starry-<name>/c/src/main.c`):

- rcore-os#713 — `setuid((uid_t)-1)` 在 starry 上被错误接受
  (Linux 应返 EINVAL, 由 uid_valid 检查阻断)
- rcore-os#714 — `/proc/self/status` 在 starry loongarch64 上
  vm_write EFAULT (Uid:/Gid:/Groups: 三行均受影响)
- rcore-os#715 — `setgroups(N, list)` 之后 `/proc/self/status`
  的 Groups: 行不立即同步 (starry race)
- rcore-os#716 — apk + curl 组合在 starry loongarch64 上
  600s 超时 ~90% flake (与本测试矩阵 stress 路径相关)

上述 bug 由 rcore-os#717 (fix-uid-gid-bugs, local 修) 与
rcore-os#718 (fix-uid-gid-deep, cross-subsystem) 这两个 fix PR
分别托管 — fix PR 同步把 bug-starry-* 注册进 4 arch bugfix toml 矩阵,
CI 由 FAIL → PASS 转化作为修复验收。

rcore-os#726 当前 bucket: `pass=18 fail=0 skipping=30`。

公式: `2 syscall × 7 state × 16 input × 2 mode = 448`

- syscall 2: `setuid` / `setgid`
- state 7: `root_unmod` / `after_setresuid(0,1000,2000)` /
  `after_setresuid(0,0,1000)` / `unpriv(1000)` /
  `unpriv_3id(1000,2000,3000)` / `after_setgid(1000)` / `mixed_uid_gid`
- input 16: 0 / 1 / 100 / 999 / 1000 / 2000 / 3000 / 65535 / 65536 /
  100001 / 0x7FFFFFFE / NOCHG((uid_t)-1) / current_uid / current_euid /
  current_suid / boundary
- mode 2: libc / raw

为什么这么多维度:
- input 16 值: man §ERRORS EINVAL 触发只在 `(uid_t)-1`; EPERM 触发依赖
  input 是否在 {uid, euid, suid}; root 接受任意值 — 16 值覆盖 small /
  large / boundary (16-bit / 32-bit) / sentinel / self-set / cross-set
- state 7: 同一 `setuid(input)` 在不同 cred 起点上行为不同 — 覆盖 man
  §EPERM 决策树各分支
- mode 2: libc/raw 分离测验 ABI 一致性

每 case 3-5 assertion: rc / errno / 后续 getresuid r/e/s 三槽各验 /
fsuid procfs 检查 → 单 case 平均 4 assert → 总 ~1800 PASS。

切换分支:
```bash
cd <repo-root>
git checkout test-uid-gid-direct-setters
```

本地 Linux (host, WSL2):
```bash
cd <repo-root>/test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-direct-setters/c
rm -rf build && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug && make
sudo ./test-uid-gid-direct-setters
```

starry qemu (4 arch):
```bash
source .starry-env.sh && cargo starry test qemu --arch x86_64      -c syscall
source .starry-env.sh && cargo starry test qemu --arch aarch64     -c syscall
source .starry-env.sh && cargo starry test qemu --arch riscv64     -c syscall
source .starry-env.sh && cargo starry test qemu --arch loongarch64 -c syscall
```

- 相关 PR: stacked 前 Group A (rcore-os#725); stacked 后 Group C/D/E
  (rcore-os#727..rcore-os#729); 衍生 bug-* rcore-os#713 (setuid/setgid no uid_valid),
  fix rcore-os#717 (fix-uid-gid-bugs) 修复该 bug

Signed-off-by: Leo Cheng <chengkelfanke@gmail.com>
Signed-off-by: Leo Cheng <chengkelfan@qq.com>

@mai-team-app mai-team-app Bot left a comment

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.

🔍 PR #727 第六次 Review — test(syscall): test-uid-gid-re-setters 915 PASS

📋 总览

本 PR 纯新增 C 测试代码(16 文件,+1684 行,0 删除),为 setreuid / setregid 两参 syscall 提供 man-first 地毯式测试。9 模块 / 40 测试函数 / 915 断言,覆盖 NOCHG sentinel、self-set、root 任意值、非特权 EPERM、raw vs libc 一致性、D5 saved-set-uid 规则(含反向 corner)、864 case 笛卡尔积矩阵、procfs 可见性、pthread cred 同步、core dump 抑制、errno 保护、supplementary groups 不变量。

当前 head (bd168758d) 已 rebase 到最新 dev (5b41966df),代码内容与前次 review(c39b8eab)完全一致,无新增改动。

✅ 代码质量与正确性

逐模块审查(全部通过):

  1. test_framework.h:简洁宏驱动框架,CHECK/CHECK_ERR/CHECK_OR_BUG 支持硬断言、errno 检查和已知 starry bug 软断言。#pragma once 防重复包含。CHECK_OR_BUG 不计 __fail,支持 test 分支主体绿 + bug-* 暴露问题的两层结构。
  2. main.cCOLLECT 宏防御性处理可疑返回值(rc < 0 || rc > 1000000),9 模块串行收集,fail 累加。逻辑正确。
  3. setreuid.c (7 case):覆盖 NOCHG sentinel、self-set、root 任意值、非特权 EPERM(验证 ruid 仅 {r,e} 不含 s 的差异)、raw vs libc、supplementary groups 不变量(setreuid_keeps_supp_groups() 验证 cred.group_info 不被错误清除)。
  4. setregid.c (7 case):镜像 setreuid 测试,含 setregid_keeps_supp_groups() supplementary groups invariant。
  5. saved_id_semantics.c (6 case):D5 saved-set-uid 规则完整覆盖,特别是 D5 反向 corner case(ruid=-1 且 new.euid == old.ruid 时 saved 不更新),需先 setresuid seed r ≠ s 才能区分。UID 和 GID 双维度覆盖。
  6. matrix.c (864 case)derive() 函数从 man page RE-D1~D5 推导期望值。从 1536 缩减到 864 保留关键边界(0/NOCHG/16-bit max)。state 初始化正确处理 caps drop 时序。
  7. procfs_visibility.c (5 case):验证 /proc/self/status Uid:/Gid: 行同步。parse_proc_id_line() 使用 fopen+sscanf+fclose 安全。procfs (c) 的 seed-then-test 设计(setresuid(0,0,5000)setreuid(-1,0))精准验证 D5 反向在 procfs 的反映。compound 验证(setreuid + setregid 组合后 procfs 一致性)设计精巧。
  8. nptl_sync.c (3 case)_Atomic + usleep(1000) 轮询,对非 NPTL 实现(starry)优雅降级(exit code 20 → KNOWN-STARRY-LIMITATION)。无 mutex/condvar 依赖链。
  9. core_dump_inhibit.c (3 case):验证 euid 变化后 PR_GET_DUMPABLE 降为 0。对 prctl 不支持的情况有 fallback(exit code 20/21)。
  10. errno_preservation.c (5 case):验证成功路径不误改 errno,覆盖 NOCHG 和 self-set 两条路径,libc 和 raw syscall 双模式。针对 starry 常见的 vm_write/cred update 残值 bug 类型。

🔒 安全与死锁分析

无死锁风险、无资源泄漏。所有模块使用 fork() + waitpid() 隔离模式,无共享锁、无 IPC 依赖。nptl_sync.c 的 _Atomic + 轮询无 mutex/condvar 依赖链。

📊 对既有功能的影响

零影响。纯新增代码,4 个 qemu-*.toml(x86_64/aarch64/riscv64/loongarch64)仅在 test_commands 数组追加一行 /usr/bin/test-uid-gid-re-setters。不修改任何 Rust 源代码、Cargo.toml 或构建配置。

🔧 本地验证

cd test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-re-setters/c
mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Debug && make

结果:10 个 C 源文件全部编译通过(GCC 13.3.0,-Wall -Wextra -Werror),零警告零错误。

🔄 合并冲突检查

已验证 git merge --no-commit origin/dev 成功,无冲突Already up to date)。mergeable: truemergeable_state: clean。PR 分支已 rebase 到最新 dev 基线。

🔍 CI 状态

当前 head (bd168758d) 尚无 CI 运行记录(刚 rebase)。前 5 次 CI 失败均为基础设施问题,与 PR 改动无关:

  • 第 1 次:test-shm-deadlock SMP4 timeout flake(#740
  • 第 2 次:crates.io cargo registry 网络 transient 错误
  • 第 3 次:axvisor self-hosted runner infra 问题
  • 第 4 次:test-timerfd-efault-wake SMP4 timeout(#759

本 PR 仅新增 C 测试代码,不触及 Rust 编译、toml 配置逻辑或构建系统。

📝 前次 review 评论处理

  1. boundary.c fork 失败处理(非阻塞,第 3 次 review 提出):boundary_root_extreme_values()boundary_unpriv_extreme_eperm()pid < 0(fork 失败)时 case 静默跳过。建议后续加 if (pid < 0) { CHECK(0, "fork failed"); return; }。不阻塞合入。
  2. waitpid_safely() 重复定义:8 个 .c 文件各自定义相同 static helper。当前自包含设计可接受,后续模块增长时可考虑提取到 test_framework.h。不阻塞。

结论

APPROVE — 高质量 man-first 测试套件。9 模块 915 断言覆盖 setreuid/setregid 完整语义空间。代码结构清晰,注释充分,无死锁风险,零影响现有功能。与 dev 无合并冲突。这是第 6 次 review,PR 已 rebase 到最新 dev。建议合入。

Powered by mimo-v2.5-pro

Lfan-ke added a commit to Lfan-ke/tgoskits that referenced this pull request May 22, 2026
Group B: `setuid` / `setgid` 两个直接 setter syscall 的 man-first 地毯式
测试, 本地 477 PASS。对应 rcore-os#726。

测试位置: `test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-direct-setters/c/src/`

模块总览 (10 模块 / 46 个 static void case 函数 / main.c 串联):
`setuid` / `setgid` / `cross_root_unprivileged` / `boundary` / `matrix` /
`procfs_visibility` / `nptl_sync` / `core_dump_inhibit` / `uid32_no_trunc` /
`errno_preservation`。

- `waitpid_safely()` helper — 循环 waitpid 处理 EINTR
- `setuid_idempotent_to_self()` — setuid(geteuid()) 后三字段 (r,e,s) 不变
  (man §DESCRIPTION POSIX_SAVED_IDS)
- `setuid_root_sets_all_three()` — root setuid(target) → r/e/s 全被设为
  target (man §DESCRIPTION root path)
- `setuid_returns_zero_on_success()` — 成功 rc == 0 (不只是 ≥0)
- `setuid_raw_syscall_matches_libc()` — 裸 syscall 与 libc 行为一致
- `setuid_unprivileged_eperm_in_child()` — 非特权 setuid 在 fork 子 →
  EPERM (man §ERRORS EPERM)
- `setuid_raw_vs_libc_failed_path()` — EPERM 失败路径下 raw rc/errno 与
  libc 严格相同
- `setuid_irreversibility()` — root setuid(non-zero) drop 后无法
  setuid(0) 回去 (man §DESCRIPTION 不可逆)
- `setuid_fork_child_independent()` — fork 子 setuid 改动不污染父 cred
- `setuid_self_euid_no_flush()` — 非特权 setuid(euid) 不清除 saved-set-uid
- `setuid_einval_uid_minus_one()` — uid=(uid_t)-1 在非特权下 → EPERM/
  EINVAL (软容忍 starry bug B)

- `waitpid_safely()` helper
- `setgid_idempotent_to_self()` — setgid(getegid()) 三字段不变
- `setgid_root_sets_all_three()` — root setgid(target) → r/e/s GID 全置
- `setgid_returns_zero_on_success()`
- `setgid_raw_syscall_matches_libc()`
- `setgid_unprivileged_eperm_in_child()` — 非特权 setgid(任意) → EPERM
- `setgid_raw_vs_libc_failed_path()`
- `setgid_alone_not_irreversible_caps_intact()` — 仅 setgid drop 不丢
  CAP_SETUID, 仍可 setuid
- `setgid_irreversible_after_setuid_drop()` — setuid drop 后 setgid 不可
  逆 (CAP_SETGID 丢)
- `setgid_fork_child_independent()`
- `setgid_self_egid_no_change()` — setgid(egid) 不动 saved-set-gid

- `waitpid_safely()` helper
- `root_setuid_arbitrary_updates_all()` — root setuid(任意值) → r/e/s
  全跟
- `unprivileged_setuid_self_succeeds()` — 非特权 setuid(self) 成功且字段
  不变
- `unprivileged_setuid_arbitrary_eperm()` — 非特权 setuid(非 {r,e,s}) →
  EPERM

- `setuid_boundary_values_root()` — root 路径下边界 uid:
  0/1/65535/65536/0x7FFFFFFE 全 OK
- `setuid_u32max_sentinel_unprivileged()` — 非特权 setuid((uid_t)-1)
  sentinel → EPERM/EINVAL (man §ERRORS EINVAL)
- `setuid_raw_syscall_returns_negative_errno()` — 裸 syscall 失败时 rc
  为 `-EPERM` (不是 -1+errno)

- `read_uid_cred()` / `read_gid_cred()` / `setup_state()` /
  `waitpid_safely()` helper
- `matrix_one()` — (syscall × state × input × mode) 笛卡尔单格 — 验
  rc/errno + getresuid 后续三槽

- `waitpid_safely_pv()` / `parse_proc_id_line()` helper
- `procfs_uid_baseline_root()` — 未改动时 /proc Uid: == (uid,euid,suid)
  一致 (proc(5) §Uid)
- `procfs_uid_after_setuid_root()` — root setuid 后 procfs 三字段同步
- `procfs_uid_after_setuid_drop()` — drop 后 procfs Uid: 三字段一致更新
- `procfs_gid_after_setgid()` — setgid 后 procfs Gid: 行同步
- `procfs_uid_after_setresuid()` — setresuid 后 procfs 三字段精确同步

- `nptl_setuid_main_visible_to_pthread()` — 主线程 setuid → 子 pthread
  getresuid 看到相同 cred (man §VERSIONS C library/kernel differences)
- `nptl_setgid_pthread_visible_to_main()`
- `nptl_concurrent_setuid_converges()`

- `waitpid_safely_cd()` helper
- `core_dump_baseline()` — 未改动时 PR_GET_DUMPABLE == 1 (man §NOTES)
- `core_dump_setuid_no_euid_change()` — setuid(euid) euid 未变 →
  dumpable 不变
- `core_dump_setuid_changes_euid()` — setuid 改变 euid → PR_GET_DUMPABLE
  → 0 (禁 core)

- `waitpid_safely()` helper
- `setuid_32bit_no_truncate()` — child setuid 大 UID (>16-bit) 后
  getresuid 不被截 (man §HISTORY 16→32-bit)
- `setgid_32bit_no_truncate()`
- `setuid_32bit_via_raw_syscall()`

- `setuid_self_errno_preserved()` / `setgid_self_errno_preserved()` /
  `setuid_root_errno_preserved()` / `setgid_root_errno_preserved()` /
  `raw_setuid_success_errno_preserved()` — 成功路径不动预置 errno

按顺序 COLLECT 10 个模块, 每模块 `*_run()` 返 `__fail`, main 汇总
TOTAL: N fail 收尾。

`setuid` §DESCRIPTION: "setuid() sets the effective user ID of the
calling process. If the calling process is privileged (more precisely:
if the process has the CAP_SETUID capability in its user namespace),
the real UID and saved set-user-ID are also set."

`setuid` §ERRORS:
- EAGAIN — temporary failure allocating kernel data structures
- EINVAL — The user ID specified in uid is not valid in this user
  namespace
- EPERM — The user is not privileged and uid does not match the real
  UID or saved set-user-ID of the calling process

`setuid` §NOTES: "If uid is different from the old effective UID, the
process will be forbidden from leaving core dumps."

`setuid` §VERSIONS (C library/kernel differences): NPTL wrapper 使用
signal-based 技术保证 process-wide thread cred 同步。

`setgid` §ERRORS: EINVAL (gid 不合法) / EPERM (无 CAP_SETGID 且 gid 不
匹配 real GID 或 saved set-group-ID)。

本 test PR 在 starry kernel 上跑时, 暴露以下 starry-vs-Linux 行为差异,
已各自登记为上游 issue, 单文件 C 复现程序在 fork repo 的 `bug-starry-*`
分支内单独维护 (路径 `test-suit/.../bugfix/bug-starry-<name>/c/src/main.c`):

- rcore-os#713 — `setuid((uid_t)-1)` 在 starry 上被错误接受
  (Linux 应返 EINVAL, 由 uid_valid 检查阻断)
- rcore-os#714 — `/proc/self/status` 在 starry loongarch64 上
  vm_write EFAULT (Uid:/Gid:/Groups: 三行均受影响)
- rcore-os#715 — `setgroups(N, list)` 之后 `/proc/self/status`
  的 Groups: 行不立即同步 (starry race)
- rcore-os#716 — apk + curl 组合在 starry loongarch64 上
  600s 超时 ~90% flake (与本测试矩阵 stress 路径相关)

上述 bug 由 rcore-os#717 (fix-uid-gid-bugs, local 修) 与
rcore-os#718 (fix-uid-gid-deep, cross-subsystem) 这两个 fix PR
分别托管 — fix PR 同步把 bug-starry-* 注册进 4 arch bugfix toml 矩阵,
CI 由 FAIL → PASS 转化作为修复验收。

rcore-os#726 当前 bucket: `pass=18 fail=0 skipping=30`。

公式: `2 syscall × 7 state × 16 input × 2 mode = 448`

- syscall 2: `setuid` / `setgid`
- state 7: `root_unmod` / `after_setresuid(0,1000,2000)` /
  `after_setresuid(0,0,1000)` / `unpriv(1000)` /
  `unpriv_3id(1000,2000,3000)` / `after_setgid(1000)` / `mixed_uid_gid`
- input 16: 0 / 1 / 100 / 999 / 1000 / 2000 / 3000 / 65535 / 65536 /
  100001 / 0x7FFFFFFE / NOCHG((uid_t)-1) / current_uid / current_euid /
  current_suid / boundary
- mode 2: libc / raw

为什么这么多维度:
- input 16 值: man §ERRORS EINVAL 触发只在 `(uid_t)-1`; EPERM 触发依赖
  input 是否在 {uid, euid, suid}; root 接受任意值 — 16 值覆盖 small /
  large / boundary (16-bit / 32-bit) / sentinel / self-set / cross-set
- state 7: 同一 `setuid(input)` 在不同 cred 起点上行为不同 — 覆盖 man
  §EPERM 决策树各分支
- mode 2: libc/raw 分离测验 ABI 一致性

每 case 3-5 assertion: rc / errno / 后续 getresuid r/e/s 三槽各验 /
fsuid procfs 检查 → 单 case 平均 4 assert → 总 ~1800 PASS。

切换分支:
```bash
cd <repo-root>
git checkout test-uid-gid-direct-setters
```

本地 Linux (host, WSL2):
```bash
cd <repo-root>/test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-direct-setters/c
rm -rf build && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug && make
sudo ./test-uid-gid-direct-setters
```

starry qemu (4 arch):
```bash
source .starry-env.sh && cargo starry test qemu --arch x86_64      -c syscall
source .starry-env.sh && cargo starry test qemu --arch aarch64     -c syscall
source .starry-env.sh && cargo starry test qemu --arch riscv64     -c syscall
source .starry-env.sh && cargo starry test qemu --arch loongarch64 -c syscall
```

- 相关 PR: stacked 前 Group A (rcore-os#725); stacked 后 Group C/D/E
  (rcore-os#727..rcore-os#729); 衍生 bug-* rcore-os#713 (setuid/setgid no uid_valid),
  fix rcore-os#717 (fix-uid-gid-bugs) 修复该 bug

Signed-off-by: Leo Cheng <chengkelfanke@gmail.com>
Signed-off-by: Leo Cheng <chengkelfan@qq.com>
Group C: `setreuid` / `setregid` 两参 (real + effective) setter syscall 的 man-first 地毯式测试, 本地 915 PASS。对应 rcore-os#727。

测试位置: `test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-re-setters/c/src/`

模块总览 (9 模块 / 40 个 static void case 函数 / main.c 串联): `setreuid` / `setregid` / `saved_id_semantics` / `boundary` / `matrix` / `procfs_visibility` / `nptl_sync` / `core_dump_inhibit` / `errno_preservation`。

- `waitpid_safely()` helper
- `setreuid_both_nochg_idempotent()` — setreuid((uid_t)-1,(uid_t)-1) 不改 r/e/s 三字段 (man §DESCRIPTION sentinel -1)
- `setreuid_ruid_self()` — setreuid(self,-1) 后 real == 原 real
- `setreuid_euid_self()` — setreuid(-1,self) 后 effective == 原 effective
- `setreuid_root_arbitrary()` — root setreuid(r,e) 任意值成功
- `setreuid_unpriv_eperm()` — 非特权越权 (非 {ruid,euid,suid}) → EPERM (man §ERRORS EPERM)
- `setreuid_raw_matches_libc()` — 裸 syscall 与 libc 一致 (rc + 三字段)
- `setreuid_keeps_supp_groups()` — **Linux invariant** (man 未显式但 kernel 行为强制): root → setgroups([7,8,9]) → setreuid(1000,1000) drop → getgroups 应仍 [7,8,9]; host gcc 实测 PASS, 防 starry sys_setreuid 错误清 cred.group_info 回归

- `waitpid_safely()` helper
- `setregid_both_nochg()` / `setregid_rgid_self()` / `setregid_egid_self()` / `setregid_root_arbitrary()` / `setregid_unpriv_eperm()` / `setregid_raw_matches_libc()` / `setregid_keeps_supp_groups()` — 镜像 setreuid 测试 (含 (g) supp groups invariant)

- `waitpid_safely()` helper
- `saved_id_ruid_set_updates_suid()` — ruid != -1 → suid := new euid (D5 规则触发) (man §DESCRIPTION)
- `saved_id_euid_set_to_nonruid_updates_suid()` — euid 改成 ≠ 旧 ruid → suid 更新
- `saved_id_both_set_root()` — root 同时改 r 和 e, suid 更新到新 euid
- `saved_id_both_nochg_no_change()` — (-1,-1) → r/e/s 全无变化
- `saved_id_d5_reverse_corner()` — D5 反向 corner: euid := 旧 ruid 时 saved 不应被更新
- `saved_id_d5_reverse_corner_gid()` — D5 corner GID 侧

- `waitpid_safely()` helper
- `boundary_raw_u32max_nochg()` — 裸 syscall 传 (uid_t)-1 sentinel 走 nochg 路径
- `boundary_root_extreme_values()` — root 极值 0/1000/65535/65536/ (u32max-1) (man §HISTORY 16→32-bit)
- `boundary_unpriv_extreme_eperm()` — 非特权传极值 → EPERM

- `setup_state()` / `read_uid_cred()` / `read_gid_cred()` / `waitpid_safely()` helper
- `matrix_one()` — (syscall × state × ruid × euid × mode) 笛卡尔单格 — 验 rc/errno + 后续 r/e/s 三槽 + D5 saved 规则

- `waitpid_safely_pv()` / `parse_proc_id_line()` helper
- `proc_uid_after_setreuid_full()` — setreuid(r,e) 全改 → /proc Uid: 三字段一致 (proc(5) §Uid + man §DESCRIPTION)
- `proc_uid_after_setreuid_nochg_r()` — setreuid(-1,e) → procfs real 不变、e 同步、s 按 D5
- `proc_uid_after_setreuid_nochg_same()` — setreuid(r,r) same-value → procfs 三字段一致
- `proc_gid_after_setregid_full()` — setregid 全改 → /proc Gid: 行同步
- `proc_compound_setre()` — 组合调用 (setreuid 再 setregid) 后 procfs 三字段全一致

- `nptl_setreuid_main_to_pthread()` — 主线程 setreuid → 子 pthread getresuid 看到相同 cred (man §VERSIONS C library/kernel differences)
- `nptl_setregid_pthread_to_main()`
- `nptl_concurrent_setreuid()`

- `cd_baseline()` — 未改动时 PR_GET_DUMPABLE == 1 (man §NOTES)
- `cd_setreuid_no_euid_change()` — euid 未变 → dumpable 不变
- `cd_setreuid_changes_euid()` — euid 变化 → PR_GET_DUMPABLE → 0

- `setreuid_nochg_errno_preserved()` / `setregid_nochg_errno_preserved()` / `setreuid_self_errno_preserved()` / `setregid_self_errno_preserved()` / `raw_setreuid_success_errno_preserved()`

按顺序 COLLECT 9 个模块, 每模块 `*_run()` 返 `__fail`, main 汇总 TOTAL: N fail。

`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."
- "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." (D5 规则)

`setreuid` §ERRORS:
- EINVAL — One or more of the target user or group IDs is not valid in this user namespace.
- EPERM — The calling process is not privileged and a change other than (i) swapping effective with real, (ii) setting one to the value of the other, (iii) setting effective to saved set-user-ID was specified.

`setreuid` §HISTORY: glibc setreuid/setregid wrapper transparently deals with 16-bit/32-bit variations across kernel versions。

本 test PR 在 starry kernel 上跑时, 暴露以下 starry-vs-Linux 行为差异, 已各自登记为上游 issue, 单文件 C 复现程序在 fork repo 的 `bug-starry-*` 分支内单独维护 (路径 `test-suit/.../bugfix/bug-starry-<name>/c/src/main.c`):

- rcore-os#713 — `setuid((uid_t)-1)` 在 starry 上被错误接受 (Linux 应返 EINVAL, 由 uid_valid 检查阻断)
- rcore-os#714 — `/proc/self/status` 在 starry loongarch64 上 vm_write EFAULT (Uid:/Gid:/Groups: 三行均受影响)
- rcore-os#715 — `setgroups(N, list)` 之后 `/proc/self/status` 的 Groups: 行不立即同步 (starry race)
- rcore-os#716 — apk + curl 组合在 starry loongarch64 上 600s 超时 ~90% flake (与本测试矩阵 stress 路径相关)

上述 bug 由 rcore-os#717 (fix-uid-gid-bugs, local 修) 与 rcore-os#718 (fix-uid-gid-deep, cross-subsystem) 这两个 fix PR 分别托管 — fix PR 同步把 bug-starry-* 注册进 4 arch bugfix toml 矩阵, CI 由 FAIL → PASS 转化作为修复验收。

rcore-os#727 当前 bucket: `pass=18 fail=0 skipping=30`。

原公式: `8 ruid × 8 euid × 6 state × 2 mode × 2 syscall = 1536` 缩减后: `6 ruid × 6 euid × 6 state × 2 mode × 2 syscall = 864` (commit `5248f83fe`)

- ruid 6: `0 / 1000 / 2000 / 65535 / NOCHG / boundary`
- euid 6: 同 ruid (枚举全笛卡尔积)
- state 6: `root_unmod / 3id_distinct / unpriv / mid_priv / after_setuid / NOCHG_baseline`
- mode 2: libc / raw
- syscall 2: setreuid / setregid

为什么 (原) 这么多: man D5 saved 规则的真假组合依赖 (ruid, euid, old.ruid) 三元组关系 → 必须全笛卡尔积才能覆盖; ruid × euid 笛卡尔积是验 D5 规则的最小覆盖单元; × 6 state: EPERM 决策依赖 old cred 起点。

为什么缩减: emulated aarch64 上 fork-heavy 矩阵 1536 case 接近 5 min, 大概率 CI 超时 (timeout=600s)。缩减 8→6 保留关键 boundary (0/NOCHG/16-bit max), 减少笛卡尔积规模到 ~56%。边界覆盖仍完整。

每 case 4-5 assertion ≈ 3500-4500 PASS (864 base)。

切换分支:
```bash
cd <repo-root>
git checkout test-uid-gid-re-setters
```

本地 Linux (host, WSL2):
```bash
cd <repo-root>/test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-re-setters/c
rm -rf build && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug && make
sudo ./test-uid-gid-re-setters
```

starry qemu (4 arch):
```bash
source .starry-env.sh && cargo starry test qemu --arch x86_64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch aarch64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch riscv64 -c syscall
source .starry-env.sh && cargo starry test qemu --arch loongarch64 -c syscall
```

- 相关 PR: stacked 前 Group A/B (rcore-os#725..rcore-os#726); stacked 后 Group D/E (rcore-os#728..rcore-os#729); 衍生 fix rcore-os#718 (fix-uid-gid-deep) PR_SET/GET_DUMPABLE 支撑 core_dump_inhibit 模块

Signed-off-by: Leo Cheng <chengkelfan@qq.com>
@Lfan-ke

Lfan-ke commented May 22, 2026

Copy link
Copy Markdown
Contributor Author

已 rebase 到最新 dev (22aabf7),并按 #725 review 意见把安装目标改为 usr/bin/starry-test-suit、去掉 qemu-*.toml/usr/bin/test-uid-gid-re-setters 的显式注册(走 syscall grouped case 的 /usr/bin/starry-test-suit/* 自动发现路径)。

@mai-team-app mai-team-app Bot left a comment

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.

PR #727 审查:test(syscall): test-uid-gid-re-setters 915 PASS

概览

本 PR 为 setreuid/setregid 等 UID/GID 重置类系统调用添加了完整的 C 语言测试套件,共 12 个文件、+1680 行,全部为纯新增测试代码,无任何框架或构建配置变更。

模块审查(全部 9 个测试模块)

模块 用例数 评价
setreuid.c 7 覆盖无变更、自身设置、root 任意设置、非特权 EPERM、raw vs libc、补充组不变性,语义正确
setregid.c 7 与 setreuid 对称,GID 侧覆盖完整
saved_id_semantics.c 6 亮点:完整实现了 D5 saved-set-uid 规则,包含关键的 D5 反向边界用例(UID+GID 同时验证),这是 man page 中最易遗漏的场景
boundary.c 3 u32::MAX 哨兵值、root 极端值、非特权 EPERM,边界覆盖充分
matrix.c 864 6×6×6×2×2 笛卡尔积全覆盖,derive() 函数忠实现 man page RE-D1..D5 规则,工程质量极高
procfs_visibility.c 5 /proc/self/status 的 Uid:/Gid: 字段同步验证,确保内核与 procfs 一致性
nptl_sync.c 3 pthread 凭证同步测试,使用 KNOWN-STARRY-LIMITATION 优雅降级处理,设计合理
core_dump_inhibit.c 3 PR_GET_DUMPABLE 在 euid 变更后的行为验证,安全语义正确
errno_preservation.c 5 成功路径 errno 保持不变验证,POSIX 语义正确

本地构建验证

cd test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-re-setters/c
mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Debug && make

使用 GCC 13.3.0,-Wall -Wextra -Werror 全部通过,零警告零错误。CMakeLists.txt 规范使用 C11 标准,安装路径为 usr/bin/starry-test-suit(自动发现机制)。

CI 状态

所有 check-runs 状态为 skipped(基础设施问题),与本 PR 代码无关。

重复性分析

  • 基础分支 (origin/dev) 中不存在 test-uid-gid-re-setters 目录
  • 同系列 PR 对比:#725(getters)、#726(direct setters)、#728(res setters,已合并)、#729(groups)——每个 PR 测试不同的系统调用,无功能重叠
  • 结论:本 PR 是独立的、互补的

测试框架设计

  • test_framework.h:轻量级 CHECK/CHECK_ERR/CHECK_OR_BUG 宏 + __pass/__fail 计数器,简洁实用
  • main.c:COLLECT 宏聚合 9 个模块,结构清晰

非阻塞备注

  • waitpid_safely() 在 8 个文件中重复定义——在纯 C 测试场景下可以接受,若后续模块增多可考虑提取到公共头文件

总结

代码质量高,测试设计以 man page 为第一优先级(man-first design),覆盖了正常路径、边界条件、错误路径和 POSIX 语义细节。D5 反向边界用例和 864 例笛卡尔矩阵是突出亮点。推荐合并

Powered by glm-5.1

@Lfan-ke

Lfan-ke commented May 22, 2026

Copy link
Copy Markdown
Contributor Author

@ZR233 已按 #725 的 review 意见修好(安装目录改 usr/bin/starry-test-suit + 去掉 qemu-*.toml 显式注册 + rebase 到最新 dev 22aabf7,GPG 签名)。本组 6 个 PR(#720 已合 + #725/#726/#727/#729/#730)皆 OK,完整说明见 #725,麻烦重新 review 🙏。

ZR233 pushed a commit that referenced this pull request May 22, 2026
Group B: `setuid` / `setgid` 两个直接 setter syscall 的 man-first 地毯式
测试, 本地 477 PASS。对应 #726。

测试位置: `test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-direct-setters/c/src/`

模块总览 (10 模块 / 46 个 static void case 函数 / main.c 串联):
`setuid` / `setgid` / `cross_root_unprivileged` / `boundary` / `matrix` /
`procfs_visibility` / `nptl_sync` / `core_dump_inhibit` / `uid32_no_trunc` /
`errno_preservation`。

- `waitpid_safely()` helper — 循环 waitpid 处理 EINTR
- `setuid_idempotent_to_self()` — setuid(geteuid()) 后三字段 (r,e,s) 不变
  (man §DESCRIPTION POSIX_SAVED_IDS)
- `setuid_root_sets_all_three()` — root setuid(target) → r/e/s 全被设为
  target (man §DESCRIPTION root path)
- `setuid_returns_zero_on_success()` — 成功 rc == 0 (不只是 ≥0)
- `setuid_raw_syscall_matches_libc()` — 裸 syscall 与 libc 行为一致
- `setuid_unprivileged_eperm_in_child()` — 非特权 setuid 在 fork 子 →
  EPERM (man §ERRORS EPERM)
- `setuid_raw_vs_libc_failed_path()` — EPERM 失败路径下 raw rc/errno 与
  libc 严格相同
- `setuid_irreversibility()` — root setuid(non-zero) drop 后无法
  setuid(0) 回去 (man §DESCRIPTION 不可逆)
- `setuid_fork_child_independent()` — fork 子 setuid 改动不污染父 cred
- `setuid_self_euid_no_flush()` — 非特权 setuid(euid) 不清除 saved-set-uid
- `setuid_einval_uid_minus_one()` — uid=(uid_t)-1 在非特权下 → EPERM/
  EINVAL (软容忍 starry bug B)

- `waitpid_safely()` helper
- `setgid_idempotent_to_self()` — setgid(getegid()) 三字段不变
- `setgid_root_sets_all_three()` — root setgid(target) → r/e/s GID 全置
- `setgid_returns_zero_on_success()`
- `setgid_raw_syscall_matches_libc()`
- `setgid_unprivileged_eperm_in_child()` — 非特权 setgid(任意) → EPERM
- `setgid_raw_vs_libc_failed_path()`
- `setgid_alone_not_irreversible_caps_intact()` — 仅 setgid drop 不丢
  CAP_SETUID, 仍可 setuid
- `setgid_irreversible_after_setuid_drop()` — setuid drop 后 setgid 不可
  逆 (CAP_SETGID 丢)
- `setgid_fork_child_independent()`
- `setgid_self_egid_no_change()` — setgid(egid) 不动 saved-set-gid

- `waitpid_safely()` helper
- `root_setuid_arbitrary_updates_all()` — root setuid(任意值) → r/e/s
  全跟
- `unprivileged_setuid_self_succeeds()` — 非特权 setuid(self) 成功且字段
  不变
- `unprivileged_setuid_arbitrary_eperm()` — 非特权 setuid(非 {r,e,s}) →
  EPERM

- `setuid_boundary_values_root()` — root 路径下边界 uid:
  0/1/65535/65536/0x7FFFFFFE 全 OK
- `setuid_u32max_sentinel_unprivileged()` — 非特权 setuid((uid_t)-1)
  sentinel → EPERM/EINVAL (man §ERRORS EINVAL)
- `setuid_raw_syscall_returns_negative_errno()` — 裸 syscall 失败时 rc
  为 `-EPERM` (不是 -1+errno)

- `read_uid_cred()` / `read_gid_cred()` / `setup_state()` /
  `waitpid_safely()` helper
- `matrix_one()` — (syscall × state × input × mode) 笛卡尔单格 — 验
  rc/errno + getresuid 后续三槽

- `waitpid_safely_pv()` / `parse_proc_id_line()` helper
- `procfs_uid_baseline_root()` — 未改动时 /proc Uid: == (uid,euid,suid)
  一致 (proc(5) §Uid)
- `procfs_uid_after_setuid_root()` — root setuid 后 procfs 三字段同步
- `procfs_uid_after_setuid_drop()` — drop 后 procfs Uid: 三字段一致更新
- `procfs_gid_after_setgid()` — setgid 后 procfs Gid: 行同步
- `procfs_uid_after_setresuid()` — setresuid 后 procfs 三字段精确同步

- `nptl_setuid_main_visible_to_pthread()` — 主线程 setuid → 子 pthread
  getresuid 看到相同 cred (man §VERSIONS C library/kernel differences)
- `nptl_setgid_pthread_visible_to_main()`
- `nptl_concurrent_setuid_converges()`

- `waitpid_safely_cd()` helper
- `core_dump_baseline()` — 未改动时 PR_GET_DUMPABLE == 1 (man §NOTES)
- `core_dump_setuid_no_euid_change()` — setuid(euid) euid 未变 →
  dumpable 不变
- `core_dump_setuid_changes_euid()` — setuid 改变 euid → PR_GET_DUMPABLE
  → 0 (禁 core)

- `waitpid_safely()` helper
- `setuid_32bit_no_truncate()` — child setuid 大 UID (>16-bit) 后
  getresuid 不被截 (man §HISTORY 16→32-bit)
- `setgid_32bit_no_truncate()`
- `setuid_32bit_via_raw_syscall()`

- `setuid_self_errno_preserved()` / `setgid_self_errno_preserved()` /
  `setuid_root_errno_preserved()` / `setgid_root_errno_preserved()` /
  `raw_setuid_success_errno_preserved()` — 成功路径不动预置 errno

按顺序 COLLECT 10 个模块, 每模块 `*_run()` 返 `__fail`, main 汇总
TOTAL: N fail 收尾。

`setuid` §DESCRIPTION: "setuid() sets the effective user ID of the
calling process. If the calling process is privileged (more precisely:
if the process has the CAP_SETUID capability in its user namespace),
the real UID and saved set-user-ID are also set."

`setuid` §ERRORS:
- EAGAIN — temporary failure allocating kernel data structures
- EINVAL — The user ID specified in uid is not valid in this user
  namespace
- EPERM — The user is not privileged and uid does not match the real
  UID or saved set-user-ID of the calling process

`setuid` §NOTES: "If uid is different from the old effective UID, the
process will be forbidden from leaving core dumps."

`setuid` §VERSIONS (C library/kernel differences): NPTL wrapper 使用
signal-based 技术保证 process-wide thread cred 同步。

`setgid` §ERRORS: EINVAL (gid 不合法) / EPERM (无 CAP_SETGID 且 gid 不
匹配 real GID 或 saved set-group-ID)。

本 test PR 在 starry kernel 上跑时, 暴露以下 starry-vs-Linux 行为差异,
已各自登记为上游 issue, 单文件 C 复现程序在 fork repo 的 `bug-starry-*`
分支内单独维护 (路径 `test-suit/.../bugfix/bug-starry-<name>/c/src/main.c`):

- #713 — `setuid((uid_t)-1)` 在 starry 上被错误接受
  (Linux 应返 EINVAL, 由 uid_valid 检查阻断)
- #714 — `/proc/self/status` 在 starry loongarch64 上
  vm_write EFAULT (Uid:/Gid:/Groups: 三行均受影响)
- #715 — `setgroups(N, list)` 之后 `/proc/self/status`
  的 Groups: 行不立即同步 (starry race)
- #716 — apk + curl 组合在 starry loongarch64 上
  600s 超时 ~90% flake (与本测试矩阵 stress 路径相关)

上述 bug 由 #717 (fix-uid-gid-bugs, local 修) 与
#718 (fix-uid-gid-deep, cross-subsystem) 这两个 fix PR
分别托管 — fix PR 同步把 bug-starry-* 注册进 4 arch bugfix toml 矩阵,
CI 由 FAIL → PASS 转化作为修复验收。

#726 当前 bucket: `pass=18 fail=0 skipping=30`。

公式: `2 syscall × 7 state × 16 input × 2 mode = 448`

- syscall 2: `setuid` / `setgid`
- state 7: `root_unmod` / `after_setresuid(0,1000,2000)` /
  `after_setresuid(0,0,1000)` / `unpriv(1000)` /
  `unpriv_3id(1000,2000,3000)` / `after_setgid(1000)` / `mixed_uid_gid`
- input 16: 0 / 1 / 100 / 999 / 1000 / 2000 / 3000 / 65535 / 65536 /
  100001 / 0x7FFFFFFE / NOCHG((uid_t)-1) / current_uid / current_euid /
  current_suid / boundary
- mode 2: libc / raw

为什么这么多维度:
- input 16 值: man §ERRORS EINVAL 触发只在 `(uid_t)-1`; EPERM 触发依赖
  input 是否在 {uid, euid, suid}; root 接受任意值 — 16 值覆盖 small /
  large / boundary (16-bit / 32-bit) / sentinel / self-set / cross-set
- state 7: 同一 `setuid(input)` 在不同 cred 起点上行为不同 — 覆盖 man
  §EPERM 决策树各分支
- mode 2: libc/raw 分离测验 ABI 一致性

每 case 3-5 assertion: rc / errno / 后续 getresuid r/e/s 三槽各验 /
fsuid procfs 检查 → 单 case 平均 4 assert → 总 ~1800 PASS。

切换分支:
```bash
cd <repo-root>
git checkout test-uid-gid-direct-setters
```

本地 Linux (host, WSL2):
```bash
cd <repo-root>/test-suit/starryos/normal/qemu-smp1/syscall/test-uid-gid-direct-setters/c
rm -rf build && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug && make
sudo ./test-uid-gid-direct-setters
```

starry qemu (4 arch):
```bash
source .starry-env.sh && cargo starry test qemu --arch x86_64      -c syscall
source .starry-env.sh && cargo starry test qemu --arch aarch64     -c syscall
source .starry-env.sh && cargo starry test qemu --arch riscv64     -c syscall
source .starry-env.sh && cargo starry test qemu --arch loongarch64 -c syscall
```

- 相关 PR: stacked 前 Group A (#725); stacked 后 Group C/D/E
  (#727..#729); 衍生 bug-* #713 (setuid/setgid no uid_valid),
  fix #717 (fix-uid-gid-bugs) 修复该 bug

Signed-off-by: Leo Cheng <chengkelfanke@gmail.com>
Signed-off-by: Leo Cheng <chengkelfan@qq.com>
@ZR233
ZR233 merged commit 40bcd0a into rcore-os:dev May 22, 2026
41 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants