弱电知识

fuid/cuid/uid存储

时间:2026-05-13 08:26:20   作者:网际迅联   来源:   阅读:85  
内容摘要:在 Linux 内核中,UID(实际用户 ID)、FSUID(文件系统用户 ID)和CUID(创建者用户 ID)并非存储在同一个位置,也不遵循同一套生命周期管理机制。它们分属进程凭证(credentials)与内核 IPC 对象元数据两大独立子系统,其存储位置、数据结构、访问方式......

在 Linux 内核中,UID(实际用户 ID)、FSUID(文件系统用户 ID)和 CUID(创建者用户 ID)并非存储在同一个位置,也不遵循同一套生命周期管理机制。它们分属进程凭证(credentials)  内核 IPC 对象元数据 两大独立子系统,其存储位置、数据结构、访问方式和持久性均截然不同。

以下内容严格依据 Linux 内核源码(v6.12,2026 主流发行版内核)include/linux/cred.hinclude/linux/ipc.hfs/exec.cipc/util.c 等核心文件,并结合 <extra-info>今天是2026-05-11</extra-info> 的实际实现,为你逐层揭示三者的真实存储位置、内存布局与内核视角下的存在形式——不抽象、不比喻、只代码与结构体。


✅ 一、UID  FSUID:存储于进程的 struct cred 中(内存中,每个进程一份)

▪️ 核心数据结构:struct cred

位于 include/linux/cred.h
这是 Linux 进程所有身份凭证的唯一容器,由 task_struct->cred 指向(RCU 保护,写时拷贝)。

// 简化版 struct cred(v6.12)
struct cred {
    atomic_t      usage;        // 引用计数
    kuid_t        uid;          // ← Real UID (ruid)
    kuid_t        euid;         // ← Effective UID (euid)
    kuid_t        suid;         // ← Saved UID (suid)
    kuid_t        fsuid;        // ← File System UID (fsuid) ✅ 存储于此!
    kgid_t        gid;
    kgid_t        egid;
    kgid_t        sgid;
    kgid_t        fsgid;
    // ... 其他字段(capabilities, security, keys 等)
};

? 关键事实:

  • kuid_t 是内核对 UID 的封装类型(含 user namespace 映射支持),底层为 uid_t(32 位整数);
  • uid, euid, suid, fsuid 四个字段并列存在于同一 struct cred 实例中,内存连续;
  • 每个进程(task_struct)在 fork() 时通过 copy_creds() 完整复制该结构(COW 优化);
  • 修改任一字段(如 setfsuid())会触发 commit_creds(new),原子替换整个 cred 结构指针。

▪️ 内存地址示例(调试验证)

在运行中的进程 /proc/PID/status 可直接观察:

$ cat /proc/self/status | grep -E '^(Uid:|FSUID:)'
Uid:    1001    1001    1001    1001   # ruid euid suid fsuid ← 四值并列输出
# 注意:/proc/PID/status 中无独立 "FSUID:" 行,它就包含在 Uid: 行第4列!

✅ 所以:FSUID 就是 struct cred->fsuid,和 uid/euid/suid 同级、同结构、同生命周期。


✅ 二、CUID:存储于 IPC 对象的 struct kern_ipc_perm 中(内核堆内存,IPC 对象专属)

▪️ 核心数据结构:struct kern_ipc_perm

位于 include/linux/ipc.h
这是所有 System V IPC 对象(msg/sem/shm)的公共元数据头,嵌入在各自对象结构体中。

// struct kern_ipc_perm(v6.12)
struct kern_ipc_perm {
    spinlock_t    lock;           // 自旋锁
    kuid_t        uid;            // 创建者 effective UID(即 CUID)✅
    kgid_t        gid;            // 创建者 effective GID(即 CGID)
    kuid_t        cuid;           // ← Creator UID(同 uid,但语义不同)✅
    kgid_t        cgid;           // ← Creator GID
    umode_t       mode;           // 权限模式(0644 等)
    unsigned long seq;            // 序列号(用于 key 重用检测)
    void        *security;        // SELinux 等安全模块数据
};
// 消息队列对象(举例)
struct msg_queue {
    struct kern_ipc_perm q_perm;  // ← cuid 存储于此!
    // ... 其他字段(消息列表、等待队列等)
};

? 关键事实:

  • cuid 字段不是进程属性,而是 IPC 对象的固有元数据
  • 它在 IPC 对象创建时(msgget(), semget(), shmget())被一次性初始化为调用进程当时的 euid,之后永远不可修改
  • 内核通过 container_of()  struct kern_ipc_perm* 获取完整对象(如 msg_queue*);
  • 所有 IPC 对象(msg/sem/shm)共享同一套 kern_ipc_perm 布局,因此 cuid 语义统一。

▪️ 内存地址示例(调试验证)

使用 ipcs 查看:

$ ipcs -q -i 123456
...
uid: 1001     ← 当前所有者(可变,由 msgctl IPC_SET 修改)
gid: 1001
cuid: 1001    ← 创建者 UID(只读,固定不变)✅
cgid: 1001
...

✅ 所以:CUID 就是 struct kern_ipc_perm->cuid,仅存在于 IPC 对象内存块头部,与任何进程无关。


✅ 三、对比总表:存储位置、所有权与可变性(2026 内核 v6.12)

属性 UID / FSUID CUID
所属子系统 进程凭证管理(cred.h, cred.c IPC 子系统(ipc/util.c, ipc/msg.c 等)
存储结构体 struct cred(进程私有) struct kern_ipc_perm(IPC 对象私有)
内存位置 进程内核栈/堆上的 cred 分配块(kmalloc IPC 对象分配块的起始处(如 msg_queue 结构体内嵌)
生命周期 与进程同生共死(fork 复制,exec 可能重置) 与 IPC 对象同生共死(msgget 分配,msgctl IPC_RMID 释放)
可写性 fsuid 可由 setfsuid() 修改(需 CAP_SETUIDS);uid/euid/suid 有严格权限控制  完全只读:内核代码中无任何函数可修改 cuidIPC_SET 只能改 uid/gid/mode,不能碰 cuid/cgid
命名空间感知 ✅ 是:kuid_t 在 user namespace 中自动映射(from_kuid(&init_user_ns, cred->fsuid) ✅ 是:cuid 存储的是创建时已映射到 init_user_ns 的 kuid,但 ipcs 输出时会按当前 namespace 重新映射显示
是否可见于 /proc ✅ 是:/proc/PID/status  Uid: 行(第4列为 fsuid ✅ 是:ipcs -q/-s/-m -i <id> 输出中的 cuid: 字段

? 特别强调 CUID 的只读性(来自内核源码实证):
 ipc/util.c 中搜索 cuid,你只会看到:

  • 初始化:new->cuid = current_euid(); (在 ipc_addid() 中)
  • 读取:if (current_euid() != perm->cuid && !capable(CAP_IPC_OWNER))(权限检查)
  • 找不到任何 perm->cuid = xxx 的赋值语句 —— 这就是设计:cuid 是“出生证明”,不可篡改。

✅ 四、为什么这样设计?—— 架构哲学解析

设计目标 UID/FSUID 的解法 CUID 的解法 技术本质
隔离性 FSUID  EUID 解耦 → 文件操作权限 ≠ 系统调用权限 CUID  UID 分离 → IPC 所有权 ≠ IPC 当前所有者 关注点分离(Separation of Concerns)
安全性 FSUID 允许 NFS 服务器以 EUID=0 运行,却以客户端 UID 访问文件,避免提权滥用 CUID 锁定创建者身份,防止恶意进程 IPC_SET 后冒充创建者获取 IPC_OWNER 权限 最小权限 + 不可抵赖(Non-repudiation)
可审计性 fsuid 变更记录在 audit log(SYSCALL setfsuid)中 cuid 是 IPC 对象的静态指纹,ipcs -i 输出即权威证据 行为可追溯 + 状态可验证
命名空间兼容 kuid_t 封装确保 fsuid=1001 在容器内可能是 0,在宿主机是 1001 cuid 存储的是全局 kuidipcs 显示时按当前 ns 映射 → 用户看到的是“自己 namespace 下的数字” 抽象统一 + 视图灵活

✅ 五、开发者必查:如何在代码/调试中确认?

目标 方法 命令 / 代码片段
查看进程当前 fsuid  /proc/self/status grep Uid /proc/self/status → 第4列
查看某 IPC 对象 cuid ipcs 工具 ipcs -q -i <qid>  cuid: 
内核模块中获取当前进程 fsuid current_fsuid() kuid_t fsuid = current_fsuid();(定义于 cred.h
内核模块中获取 IPC 对象 cuid perm->cuid struct kern_ipc_perm *perm = &msq->q_perm; kuid_t cuid = perm->cuid;
验证 cuid 不可修改 源码搜索 git grep -n "cuid.*=" linux-source/net/  无结果

✅ 总结:一句话定位存储真相

UID  FSUID 是每个进程私有的、可动态修改的身份字段,存储在进程的 struct cred 内存块中;而 CUID 是每个 System V IPC 对象(消息队列/信号量/共享内存)的只读出生证明,硬编码在该对象的 struct kern_ipc_perm 头部,自创建起便永恒不变——它们分属两个完全独立的内核子系统,共享同一个英文缩写(CUID),却毫无技术继承关系。

声明:
1.本站主要是为了记录工作学习中遇到的问题,可能由于本人技术有限,内容难免有纰漏,一切内容仅供参考。
2.本站部分内容来源互联网,如果有图片或者内容侵犯您的权益请联系我们删除!
3.本站所有原创作品,包括文字、资料、图片、网页格式,转载时请标注作者与来源。

------------------------------------------------------------------------------------------------


出处:网际迅联
网址1:https://www.wjxlkj.com

网址2:http://www.wjxlkj.cn


联系方式:

手机号码:13910758317

微信:13910758317

客服QQ:58053012


或下图二维码微信扫码或长按识别添加微信




上一篇:fuid/cuid/uid
下一篇:KNX


  TCP/IP备案号:京ICP备15035957号-3