
SerenityOS getuid/getgid 系统调用解析从 LibC 封装到内核 Credentials【免费下载链接】serenityThe Serenity Operating System 项目地址: https://gitcode.com/GitHub_Trending/se/serenity本篇指南基于 SerenityOSSerenity 操作系统仓库中getgid(2) 手册页及其关联文档讲解getuid()与getgid()这两个获取真实用户 ID / 组 ID系统调用的完整实现链路从 LibC 头文件声明 与 封装实现到 内核系统调用处理函数 与 Credentials 凭据对象并结合 SUID/有效/保存 ID 概览手册页 说明real ID在权限模型中的确切含义帮助读者理解 SerenityOS 用户身份体系中最基础的一组接口。1. 手册页内容回顾Name / Synopsis / Description原始手册页位于 Base/usr/share/man/man2/getgid.md尽管文件名是getgid.md其 Name 部分同时覆盖两个函数getuid, getgid - get real user / group idSynopsis 部分给出了头文件与函数原型#include unistd.h uid_t getuid(void); gid_t getgid(void);Description 部分只有一句话但信息量很大Returns the real user or group id.——两个函数都返回**真实real**的用户 ID 或组 ID而不是有效effectiveID。这正是它与geteuid()/getegid()的本质区别。手册页的 See also 部分指向了完整的用户/组 ID API 家族在仓库中均真实存在手册页仓库路径setuid_overview(7)Base/usr/share/man/man7/setuid_overview.mdgeteuid(2) /getegid(2)Base/usr/share/man/man2/geteuid.mdgetresuid(2) /getresgid(2)Base/usr/share/man/man2/getresuid.mdseteuid(2) /setegid(2)Base/usr/share/man/man2/seteuid.mdsetuid(2) /setgid(2)Base/usr/share/man/man2/setuid.mdsetresuid(2) /setresgid(2)Base/usr/share/man/man2/setresuid.md2. LibC 层声明与无参数系统调用封装在 LibC 的 unistd.h 中四个get函数集中声明在一起uid_t geteuid(void); // 有效用户 ID gid_t getegid(void); // 有效组 ID uid_t getuid(void); // 真实用户 ID gid_t getgid(void); // 真实组 ID紧随其后的getresuid()/getresgid()/setresuid()/setresgid()等则用于读写三个 IDreal/effective/saved的组合与手册页 See also 的引用完全对应。unistd.cpp 给出了实现每个函数都是对单一系统调用的直接封装无缓存、无锁// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getuid.html uid_t getuid() { return syscall(SC_getuid); } // https://pubs.opengroup.org/onlinepubs/9699919799/functions/getgid.html gid_t getgid() { return syscall(SC_getgid); }可以看到源码注释标注了 POSIXOpen Group规范出处说明 SerenityOS 有意遵循getuid/getgid的标准语义syscall(SC_xxx)是无参系统调用入口返回值直接作为 ID 传递。注意该实现永远不会返回错误值——uid_t/gid_t是无符号类型且每次调用都真正陷入内核读取当前凭据。3. 内核层系统调用与 Credentials 凭据对象两个系统调用的内核实现在 Kernel/Syscalls/getuid.cpp同一文件还实现了geteuid/getegid/getresuid/getresgid/getgroupsErrorOrFlatPtr Process::sys$getuid() { VERIFY_NO_PROCESS_BIG_LOCK(this); TRY(require_promise(Pledge::stdio)); auto credentials this-credentials(); return credentials-uid().value(); } ErrorOrFlatPtr Process::sys$getgid() { VERIFY_NO_PROCESS_BIG_LOCK(this); TRY(require_promise(Pledge::stdio)); auto credentials this-credentials(); return credentials-gid().value(); }从源码结构看每次getuid()调用都包含三个步骤VERIFY_NO_PROCESS_BIG_LOCK(this)断言调用点已持有 Process Big LockSerenityOS 内核的经典锁模型保证读取凭据期间进程状态不被并发修改require_promise(Pledge::stdio)SerenityOS 的pledge(2) 能力收缩机制——调用者必须已在pledge()中声明stdio能力否则系统调用失败。这意味着读取自身用户 ID被归类为基础标准 I/O 能力的一部分而非独立的特权this-credentials()-uid()从进程的Credentials对象读取真实 UID/GID。Credentials类定义在 Kernel/Security/Credentials.h是一个AtomicRefCounted的不可变值对象。同一系统调用文件中可以看到它同时持有uid()/euid()/suid()与gid()/egid()/sgid()六元组以及extra_gids()补充组列表供getgroups()使用。sys$geteuid()返回的是credentials-euid()sys$getresuid()则把三个值逐个copy_to_user写回用户态——这印证了手册页家族中每个函数只是 Credentials 中不同字段的只读投影。4. Real ID 的确切含义与 effective / saved ID 的三角关系getuid()/getgid()返回的 real ID 之所以值得单列手册页是因为它可能在 SUID/SGID 场景下与 effective ID 不同。这一点在 setuid_overview(7) 手册页 中有完整阐述其核心事实链如下每个进程有一组用户 ID 和组 ID新进程默认继承父进程的 IDSUID 二进制的动机让用户执行原本需要更高权限的任务而不必授予完整权限。手册页举的两个例子是/bin/ping被设为 SUID root因此任何进程执行它时都以 root 运行、可以发送网络包以及传统 Unix 的passwd命令通过 SUID 位获得写/etc/shadow的能力手册页注明 SerenityOS 目前尚不支持用户密码SUID 二进制执行时的三 ID 分配父进程的 ID 成为 real ID二进制文件属主成为 effective ID 和 saved ID。有效effectiveID 用于权限检查real ID 则保留进程是谁启动的这一身份事实——getuid()读到的正是这个值临时降权 / 恢复提权通过setresuid(-1, new_uid, geteuid())把 effective ID 降到普通用户、把原特权 ID 存入 saved ID随后用getresuid()setresuid(-1, suid, -1)恢复。手册页并提醒若 SUID root 二进制把用户输入处理得不安全攻击者可在夺权后直接seteuid(0)恢复特权永久降权把 real ID 同时复制进 effective 与 saved IDsetresuid(new_uid, new_uid, new_uid)此后非 root 进程无法再改回。手册页指出在 SerenityOS 上这通常等价于setuid(new_uid)组 ID 的对称处理因为改用户 ID 会影响权限降权时应先放弃组特权再放弃用户特权临时降权时恢复顺序相反。手册页最后还给出了选型建议setresuid()/setresgid()/getresuid()/getresgid()语义最清晰、最灵活历史上遗留的大量 get/set 函数只是不同语义的封装——getuid()/getgid()就是其中最简单的只读 real ID特例。5. 仓库中的实际使用场景在 SerenityOS 自身代码中getuid()/getgid()的典型用途是以启动者身份复位特权。LibC 的 posix_spawn 子进程入口 处理POSIX_SPAWN_RESETIDS标志时if (flags POSIX_SPAWN_RESETIDS) { if (seteuid(getuid()) 0) { perror(posix_spawn seteuid); _exit(127); } if (setegid(getgid()) 0) { perror(posix_spawn setegid); _exit(127); } }即若父进程带着 SUID 提升的 effective ID 启动子进程POSIX_SPAWN_RESETIDS要求把 effective ID 重置回真实 ID——getuid()/getgid()正好提供了这个真实身份的基准值。另一处用法在 unistd.cpp 中通过getpwuid(getuid())以真实 UID 查询口令数据库条目属于用 real ID 做身份相关查询的常见模式。6. 小结接口语义与验证路径语义getuid()返回进程的真实用户 IDgetgid()返回真实组 ID在 SUID/SGID 场景下二者与geteuid()/getegid()可能不同权限检查依据的是 effective ID见 setuid_overview(7)。无参数、无失败路径LibC 封装是纯系统调用转发Kernel/Syscalls/getuid.cpp 中唯一的失败来源是pledge未声明stdio能力。验证路径可在 Base/usr/share/man/man2/getgid.md 对照手册页 → Userland/Libraries/LibC/unistd.cpp 查看封装 → Kernel/Syscalls/getuid.cpp 查看内核处理 → Kernel/Security/Credentials.h 查看凭据数据结构整条链路均无中间层。适用前提以上结论基于当前仓库快照uid_t/gid_t的取值范围、SUID 位的具体行为如 exec 路径上的 effective ID 切换以Kernel/Syscalls/下execve与setuid相关实现为准。【免费下载链接】serenityThe Serenity Operating System 项目地址: https://gitcode.com/GitHub_Trending/se/serenity创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考