DragonOS/docs/kernel/syscall/sys_capget_capset.md

171 lines
6.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# sys_capget / sys_capset 设计说明
本文简要介绍 DragonOS 中 sys_capget 和 sys_capset 的设计与实现要点,覆盖版本协商、用户态数据结构、能力位集规则、以及调用流程。
来源代码:
- kernel/src/process/syscall/sys_cap_get_set.rs
- kernel/src/process/cred.rs
## 概述
- DragonOS 对齐 Linux 的 capability 接口,支持用户态通过 capget/capset 读取或设置进程的能力集。
- 能力集包括:
- cap_effective (pE):当前进程实际生效的能力
- cap_permitted (pP):进程被赋予的能力上限
- cap_inheritable (pI):可被子进程继承的能力
- cap_bsetbounding set限制可获得能力的上界仅用于规则约束不在本接口直接读写
- cap_ambientambient set不由 capset 修改)
- 能力位宽DragonOS 使用 64 位存储,但当前仅支持低 41 位CAP_FULL_SET = (1<<41)-1高位截断
## 用户态数据结构与版本
Linux 的用户态结构对齐
```c
// header: cap_user_header_t
struct CapUserHeader {
uint32_t version; // 版本号
int32_t pid; // 目标进程: 0=当前进程,其他=指定pid
};
// data: cap_user_data_t 数组元素
struct CapUserData {
uint32_t effective;
uint32_t permitted;
uint32_t inheritable;
}
```
- 版本常量
- _LINUX_CAPABILITY_VERSION_1 = 0x19980330
- _LINUX_CAPABILITY_VERSION_2 = 0x20071026已废弃
- _LINUX_CAPABILITY_VERSION_3 = 0x20080522
- DragonOS 内核支持版本_KERNEL_CAPABILITY_VERSION = v3
- 每版本拷贝的 u32 数量
- v1: 1 仅低 32
- v2/v3: 2 32 + 32
聚合/拆分规则
- capset: 从用户传入的 CapUserData[0..tocopy) 聚合为 u64高位截断到 41
- capget: 根据请求版本返回 1 组或 2 u32高位通过右移 32 获得
## 版本协商与探测行为
- capget:
- 若版本未知写回 header.version 为内核支持版本v3并返回
- data==NULL返回 0用于探测
- data!=NULL返回 EINVAL
- 若版本合法返回请求版本对应数量的 u32 v1:1组v2/v3:2组data==NULL 时也返回 0
- capset:
- 若版本未知直接返回 EINVAL不承担探测职责 Linux 更一致
- data 不能为空NULL 返回 EFAULT)。
## 目标进程选择与 pid 语义
- capget:
- pid < 0EINVAL
- pid == 0使用当前进程
- pid != 0查找目标任务找不到返回 ESRCH
- capset:
- pid < 0EPERM不允许负 pid 目标
- pid == 0 pid == 当前进程 pid允许
- pid != 当前进程 pidEPERM仅允许修改自身
## 能力集规则capset
- pE_old = effective
- pP_old = permitted
- pI_old = inheritable
- bset = bounding set
- pE_new, pP_new, pI_new 由用户数据聚合得出已按 41 位掩码截断
约束
1) pE_new pP_new
若存在 pE_new 中的位不在 pP_newEPERM
2) pP_new pP_old不允许提升 permitted
pP_new 中存在不属于 pP_old 的位EPERM
3) pI_new 限幅对齐 Linux CAP_SETPCAP bset 约束
- 如果当前进程具有 CAP_SETPCAP_BIT pE_old 生效集合中
pI_new (pI_old pP_old) bset
若超出EPERM
- 如果不具有
pI_new (pI_old pP_old) pI_new (pI_old bset)
任一超出EPERM
注意
- ambient 能力不由 capset 修改保持不变
- 通过克隆旧 cred更新 pE/pP/pI 原子替换到 PCBpcb.set_cred)。
## 流程图
capget 主要流程
```
[读取 header(version,pid)]
|
[版本合法?]
/ \
否 是
| |
[写回 header.version=v3] [pid 选择]
| |-- pid<0 -> EINVAL
[data==NULL?] |-- pid==0 -> 当前进程 cred
/ \ |-- pid!=0 -> 查找目标任务
是 否 | |- 未找到 -> ESRCH
| | | |- 找到 -> 目标 cred
返回 0 EINVAL |
[拆分 e/p/i 为低/高 32 位]
[data==NULL?]
/ \
是 否
| |
返回 0 写回用户缓冲区,返回 0
```
capset 主要流程
```
[读取 header(version,pid)]
|
[版本合法?]
/ \
否 是
| |
EINVAL [data==NULL?]
/ \
是 否
| |
EFAULT [pid 检查]
|- pid<0 -> EPERM
|- pid!=self -> EPERM
|- pid==self -> [读取用户数据并聚合 pE/pP/pI]
[规则1: pE_new ⊆ pP_new?] 否 -> EPERM
[规则2: pP_new ⊆ pP_old?] 否 -> EPERM
[规则3: pI_new 受 CAP_SETPCAP/bset 限制?] 否 -> EPERM
[克隆 cred 更新 pE/pP/pI]
[pcb.set_cred 原子替换]
返回 0
```
## 能力位宽与掩码
聚合时对 e/p/i 应用掩码
- mask = CAPFlags::CAP_FULL_SET.bits() = (1<<41)-1
- 高位被截断保证跨版本兼容性与当前实现的一致性
## 设计取舍与对齐
- capget 对未知版本支持探测语义写回支持版本并在 data==NULL 时返回 0
- capset 不承担探测未知版本直接 EINVAL更贴近 Linux 行为
- pid 约束更严格capset 仅允许修改当前进程避免跨进程权限修改
- 规则遵循 Linux 能力模型不允许提升 permittedeffective 必须受限于 permittedinheritable CAP_SETPCAP bset 限制
## 未来工作
- 完善 ambient 能力与 bounding set 的更多接口当前不在 capset 中修改 ambient)。
- 引入更完整的能力位定义与权限检查接口
- 文档与测试用例对齐更多边界条件如用户命名空间影响)。