Linux cred内核credential与commit_creds切换 Linux cred内核credential与commit_creds切换struct cred是Linux内核中管理进程凭证的核心数据结构位于include/linux/cred.h。每个进程的task_struct中维护了两个cred指针real_cred和cred。real_cred用于可执行文件访问权限的底层凭证cred是当前系统调用使用的有效凭证。这种双指针设计允许set*id系列系统调用在提交新凭证之前先进行权限验证。struct cred完整定义如下cstruct cred {atomic_t usage;kuid_t uid; /* 真实UID */kgid_t gid; /* 真实GID */kuid_t suid; /* 保存的UID */kgid_t sgid; /* 保存的GID */kuid_t euid; /* 有效UID */kgid_t egid; /* 有效GID */kuid_t fsuid; /* VFS操作使用的UID */kgid_t fsgid; /* VFS操作使用的GID */unsigned securebits; /* 安全位掩码 */kernel_cap_t cap_inheritable; /* 可继承capability */kernel_cap_t cap_permitted; /* 允许的capability */kernel_cap_t cap_effective; /* 有效的capability */kernel_cap_t cap_bset; /* capability边界集 */kernel_cap_t cap_ambient; /* ambient capability */unsigned char jit_keyring;struct key __rcu *session_keyring;struct key __rcu *process_keyring;struct key __rcu *thread_keyring;struct key __rcu *request_key_auth;#ifdef CONFIG_SECURITYvoid *security; /* LSM模块私有数据指针 */#endifstruct user_struct *user;struct group_info *group_info;struct rcu_head rcu; /* RCU回调用于延迟释放 */} __fastpath_aligned;usage字段是原子计数控制cred对象的生命周期。当usage降为零时触发RCU回调释放内存。prepare_creds()从current-cred复制一份副本返回的新cred对象usage初始化为1cstruct cred *prepare_creds(void){struct task_struct *task current;const struct cred *old;struct cred *new;new kmem_cache_alloc(cred_jar, GFP_KERNEL);if (!new)return NULL;old task-cred;memcpy(new, old, sizeof(struct cred));atomic_set(new-usage, 1);/* 增加各个子对象的引用计数 */get_uid(new-user);get_group_info(new-group_info);get_cred(new-ucred);/* 非原子地设置新旧security blob */if (security_prepare_creds(new, old, GFP_KERNEL_ACCOUNT) 0)goto error;validate_creds(new);return new;error:put_cred(new);return NULL;}commit_creds()执行最终的凭证切换这是整个cred机制的核心路径。它用RCU指针赋值替换task中的凭证指针确保正在运行的其它CPU看到一致的cred视图cint commit_creds(struct cred *new){struct task_struct *task current;const struct cred *old task-real_cred;/* new必须是通过prepare_creds分配的有效对象 */BUG_ON(atomic_read(new-usage) 1);BUG_ON(!same_cred(task-real_cred, task-cred));/* 执行审计和安全框架的hook */security_cred_commit(new, old);audit_log_task_set_cred(task, new);/* RCU同步赋值保证cred指针切换的原子可见性 */rcu_assign_pointer(task-real_cred, new);rcu_assign_pointer(task-cred, new);/* 递减旧cred引用计数 */if (old)put_cred(old);return 0;}put_cred()递减引用计数当检测到usage降为零时通过call_rcu注册延迟释放回调cvoid put_cred(const struct cred *cred){if (atomic_dec_and_test((cred)-usage))call_rcu(cred-rcu, put_cred_rcu);}static void put_cred_rcu(struct rcu_head *rcu){struct cred *cred container_of(rcu, struct cred, rcu);/* 安全模块释放私有数据 */if (cred-security)security_cred_free(cred);/* 释放子对象引用 */free_uid(cred-user);put_group_info(cred-group_info);put_cred_rcu(cred-ucred);/* 返回slab缓存 */kmem_cache_free(cred_jar, cred);}override_creds()和revert_creds()用于内核线程临时切换凭证执行特定操作典型的场景是VFS层打开文件时临时获取文件系统的权限cconst struct cred *override_creds(const struct cred *overriden){const struct cred *old current-cred;rcu_assign_pointer(current-cred, overriden);return old;}void revert_creds(const struct cred *old){const struct cred *overridden current-cred;rcu_assign_pointer(current-cred, old);put_cred(overridden);}cred对象通过slab缓存cred_jar分配配合RCU机制实现了高性能的凭证切换路径。每次execve()、setuid()、capset()系统调用都会经过prepare_creds修改特定字段然后commit_creds提交这保证了凭证修改的原子性和一致性。