以下的要求不是强制性的, 未按模板评论时对issue无任何影响
issue处理注意事项:
1. 当前issue受影响的分支提交pr时, 须在pr描述中填写当前issue编号进行关联, 否则无法关闭当前issue;
2. 模板内容需要填写完整, 无论是受影响或者不受影响都需要填写完整内容,未引入的分支不需要填写, 否则无法关闭当前issue;
3. 以下为模板中需要填写完整的内容, 请复制到评论区回复, 注: 内容的标题名称(影响性分析说明, 缺陷严重等级, 受影响版本排查(受影响/不受影响), 修复是否涉及abi变化(是/否))不能省略,省略后defect-manager将无法正常解析填写内容.
评论区可能使用到的指令说明:
| 指令 | 指令说明 | 使用权限 |
|---|---|---|
| /check-issue | 触发defect-manager校验 | 不限 |
| /reason xxx | /reason +挂起或取消条件 | 不限 |
影响性分析说明:
缺陷严重等级:(Critical/High/Moderate/Low)
缺陷根因说明:
受影响版本排查(受影响/不受影响):
- openEuler-20.03-LTS-SP4:
- openEuler-22.03-LTS-SP4:
- openEuler-24.03-LTS-SP1:
- openEuler-24.03-LTS-SP3:
- openEuler-24.03-LTS-SP4:
abi变化(是/否):
- openEuler-20.03-LTS-SP4:
- openEuler-22.03-LTS-SP4:
- openEuler-24.03-LTS-SP1:
- openEuler-24.03-LTS-SP3:
- openEuler-24.03-LTS-SP4:
缺陷issue处理具体操作请参考:
https://atomgit.com/openeuler/cve-manager/blob/master/cve-vulner-manager/doc/md/defect-manager-manual.md
pr关联issue具体操作请参考:
https://docs.atomgit.com/docs/help/home/org_project/pullrequests/pr-related-issue


Welcome To openEuler Community
Hey @yangna1310 , thanks for your contribution to the community.
Bot Usage Manual
I'm the Bot here serving you. You can find the instructions on how to interact with me at Here . That means you can comment below every pull request or issue to trigger Bot Commands. You can self-configure the PR merge rules for this repository. For more details, please refer to Here.
Contact Guide
If you have any questions, please contact the SIG: iSulad ,
and any of the maintainers: @Yekelu, @chantsztung, @haomintsai, @jingxiaolu ,


【缺陷描述】:请补充详细的缺陷问题现象描述
syscontainer-tools代码安全扫描问题
一、缺陷信息
发现 26 个问题(🔴 致命 2 · 🟡 重要 7 · 🟢 建议 13 · ⚠️ 疑似 4)
其中 2 个 🔴 致命问题(udev 规则注入导致宿主机 root 命令执行、cgroup 路径穿越导致宿主机任意文件写入)可被容器侧/容器配置侧输入触发,构成容器逃逸风险,必须优先修复。
🔴 [致命] pkg/udevd/udevd_controller.go:41-44 — udev 规则注入导致宿主机 root 命令执行
CWE: CWE-78(操作系统命令注入)/ CWE-74(注入)
规则: R02-注入(命令注入 / 模板注入)
描述:
Rule.ToUdevRuleString()将r.Name(设备 PathOnHost 基名)、r.CtrDevName(设备 PathInContainer)、r.TrimContainerID()直接以fmt.Sprintf拼入 udev 规则字符串的KERNEL=="..."与RUN{program}+="..."双引号字段,未做任何转义。udev 规则以换行分隔、以"界定字符串、以,分隔键。若CtrDevName或Name含"、,或\n,即可闭合RUN+="..."字段并注入额外的 udev 键(如新的RUN+="/bin/sh ...")或新规则行。该规则由pkg/udevd/udevd.go:38-61 saveRules()写入/etc/udev/rules.d/99-syscontainer-tools.rules(root:0600),随后reloadConfig()执行udevadm control --reload使其生效。当宿主机发生块设备分区 add/remove 事件时,udev 守护进程以 root 身份执行被注入的RUN程序。影响: 容器逃逸 → 宿主机 root 任意命令执行。攻击者只需控制设备路径字符串(含换行或
",RUN+="),即可在下次分区热插拔时以 root 触发任意命令。CVSS: 8.5(High,AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H)
数据来源: 容器设备配置(
device_hook.json中的PathInContainer/PathOnHost)→ 由syscontainer-tools add-deviceCLI 或 hook 流程写入;底层来自libdevice.ParseMapping(仅校验绝对路径,不校验特殊字符)。风险传播路径: 容器配置/CLI → ParseMapping(仅 IsAbs+Clean)→ DeviceMapping → hookConfig.Devices → AddDevices → udevdCtrl.AddRule → ToDisk → saveRules 写文件 → udevadm reload → 分区事件 → udev root 执行注入命令。
调用栈
另一入口:
libdevice/libdevice.go:147-160(CLIadd-device直接添加磁盘设备规则)。问题代码
// 文件: pkg/udevd/udevd_controller.go:41-44 func (r *Rule) ToUdevRuleString() string { return fmt.Sprintf("KERNEL==\"%s*\",ACTION==\"add|remove\", ENV{DEVTYPE}==\"partition\", SUBSYSTEM==\"block\", RUN{program}+=\"%s $env{ACTION} %s $name %s %s\"", filepath.Base(r.Name), programe, r.TrimContainerID(), r.CtrDevName, filepath.Base(r.Name)) }调用链分析
"/,/\nudevadm control --reload使规则生效RUN→ 命令执行修复建议
// 修复:对拼入 udev 规则的所有用户可控字段做严格白名单校验 + 转义。 // 1) 在 ParseMapping 中限制设备路径字符集(禁止 " , \n \r \\ $ ` 等元字符): // var devPathRe = regexp.MustCompile(`^/[A-Za-z0-9_./\-]+$`) // 2) ToUdevRuleString 中对字段做 udev 安全转义(移除/拒绝 " 与换行): func (r *Rule) ToUdevRuleString() string { name := sanitizeUdevToken(filepath.Base(r.Name)) ctr := sanitizeUdevToken(r.CtrDevName) cid := sanitizeUdevToken(r.TrimContainerID()) return fmt.Sprintf(`KERNEL=="%s*",ACTION=="add|remove",ENV{DEVTYPE}=="partition",SUBSYSTEM=="block",RUN{program}+="%s $env{ACTION} %s $name %s %s"`, name, programe, cid, ctr, name) } func sanitizeUdevToken(s string) string { // 拒绝任何双引号、反斜杠、换行;仅保留安全字符 if strings.ContainsAny(s, `"\`+"\n\r") { return "" // 或上层返回 error 拒绝该规则 } return s } // 3) 上层 AddRule 应在 token 非法时返回 error 而非静默写入。🔴 [致命] libdevice/cgroup.go:58-59 — cgroup 路径穿越导致宿主机任意文件写入/截断
CWE: CWE-22(路径穿越)/ CWE-73(外部控制文件路径)
规则: R02-注入(路径遍历)/ R01-内存与文件边界
描述:
FindCgroupPath(pid, subsystem, innerPath)当filepath.IsAbs(innerPath)为真时,直接返回filepath.Join(cgroupRoot, filepath.Base(mnt), innerPath)。filepath.Join会清理..段,若innerPath为含..的绝对路径(如/../../../../tmp/evil),结果被清洗为/tmp/evil,完全逃逸 cgroup 根目录。随后UpdateCgroupPermission(cgroup.go:81)、UpdateCgroupDeviceReadIOPS/Bps/Weight(cgroup.go:103-172)以filepath.Join(cgroupPath, "devices.allow"|"blkio.throttle.*")为目标,调用ioutil.WriteFile(path, []byte(value), 0600)写入设备控制字符串。ioutil.WriteFile使用O_WRONLY|O_CREATE|O_TRUNC,可在宿主机任意目录创建/截断文件(文件名固定为devices.allow等),以 root 身份执行。innerPath源自spec.Linux.CgroupsPath(容器 OCI spec,容器创建者可控)。影响: 容器侧可控输入 → 宿主机 root 任意目录文件创建/截断。结合固定文件名可向
/etc/cron.d/、/etc/sudoers.d/等目录投放devices.allow文件(cron 会尝试解析 cron.d 下所有文件),或在可预测路径截断关键文件造成 DoS。属宿主机边界突破。CVSS: 7.5(High,AV:L/AC:L/PR:L/UI:N/S:C/C:N/I:H/A:H)
数据来源:
spec.Linux.CgroupsPath(容器 OCI 配置,由 isulad 依据用户创建请求生成)→ 经hooks/syscontainer-hooks/prestart.go:78(AddDevices)与prestart.go:189(UpdateQos)以及libdevice/libdevice.go:88(add-device CLI 经c.GetCgroupPath())传入。风险传播路径: 容器 spec.Linux.CgroupsPath(含
..)→ FindCgroupPath(IsAbs 分支,Join 清洗后逃逸)→ UpdateCgroup* → ioutil.WriteFile(O_TRUNC) → 宿主机任意目录文件创建/截断(root)。调用栈
CLI 入口:
device.go:125→libdevice.AddDevice→libdevice.go:88-93(c.GetCgroupPath()返回 isula inspect 的 CgroupParent,亦可能含异常字符)。问题代码
// 文件: libdevice/cgroup.go:47-74 func FindCgroupPath(pid, subsystem, innerPath string) (string, error) { cgroupRoot, err := cgroups.FindCgroupMountpointDir() ... mnt, root, err := cgroups.FindCgroupMountpointAndRoot(subsystem) ... if filepath.IsAbs(innerPath) { // 仅判断绝对,未防 .. return filepath.Join(cgroupRoot, filepath.Base(mnt), innerPath), nil // Join 清洗 .. 后逃逸 } ... } // 文件: libdevice/cgroup.go:77-91 func UpdateCgroupPermission(CgroupBase string, device *types.Device, isAddDevice bool) error { ... path = filepath.Join(CgroupBase, "devices.allow") // CgroupBase 已被穿越 if err := ioutil.WriteFile(path, []byte(value), 0600); err != nil { // O_TRUNC 写入宿主机 return err } return nil }调用链分析
spec.Linux.CgroupsPath..时 Join 后逃逸 cgroupRoot修复建议
// 修复:对 innerPath 做严格净化,禁止任何 .. 段并限制在 cgroup 层级内。 func FindCgroupPath(pid, subsystem, innerPath string) (string, error) { cgroupRoot, err := cgroups.FindCgroupMountpointDir() if err != nil { return "", err } mnt, root, err := cgroups.FindCgroupMountpointAndRoot(subsystem) if err != nil { return "", err } if filepath.IsAbs(innerPath) { // 关键修复:先 Clean,再校验不含 .. 且必须位于子系统挂载点之下 clean := filepath.Clean(innerPath) if strings.Contains(clean, "..") { return "", fmt.Errorf("invalid cgroups path: %q contains parent reference", innerPath) } base := filepath.Join(cgroupRoot, filepath.Base(mnt)) joined := filepath.Join(base, clean) // 二次校验结果仍位于 base 之下 if !strings.HasPrefix(joined+string(filepath.Separator), base+string(filepath.Separator)) && joined != base { return "", fmt.Errorf("invalid cgroups path escapes %q", base) } return joined, nil } // ... 非 absolute 分支保持原逻辑 }🟡 [重要] libdevice/container_work.go:112-115 — sysctl key 路径穿越(容器命名空间内任意文件写)
CWE: CWE-22(路径穿越)
规则: R02-注入(路径遍历)/ R01-文件边界
描述:
writeSystemProperty(key, value)将 sysctl key 中的.替换为/,再path.Join("/proc/sys", keyPath)后ioutil.WriteFile(..., 0644)。path.Join会清洗..,若 key 含..(如../../etc/crontab),目标路径逃逸/proc/sys,写入容器内任意文件(0644)。影响: 容器 spec.Linux.Sysctl 的 key 可控 → 容器命名空间内任意文件写入/创建,绕过 sysctl 限制。实际写入发生在容器 mnt 命名空间(reexec 子进程),影响范围限于容器 fs,故降为重要;但破坏了 sysctl 边界,且若未来重构为宿主机侧写入则升级为致命。
CVSS: 4.5(Medium,AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L)
数据来源:
spec.Linux.Sysctl(容器 OCI spec)→prestart.go:360AdjustUserns 遍历传入driver.UpdateSysctl(pid, &types.Sysctl{key, value})→ 经 nsexec reexec 进入容器 mnt ns →doUpdateSysctl→writeSystemProperty。风险传播路径: 容器 spec.Linux.Sysctl key 含
..→ writeSystemProperty → path.Join 清洗逃逸 /proc/sys → ioutil.WriteFile 0644 容器内任意文件。调用栈
问题代码
// 文件: libdevice/container_work.go:110-115 func writeSystemProperty(key, value string) error { keyPath := strings.Replace(key, ".", "/", -1) return ioutil.WriteFile(path.Join("/proc/sys", keyPath), []byte(value), 0644) // key 含 .. 则逃逸 }修复建议
// 修复:校验 key 仅含合法 sysctl 字符(字母/数字/`.`/`_`/`-`),禁止 .. 与绝对路径。 func writeSystemProperty(key, value string) error { if !sysctlKeyRe.MatchString(key) { // `^[A-Za-z0-9._\-]+$` return fmt.Errorf("invalid sysctl key: %q", key) } keyPath := strings.Replace(key, ".", "/", -1) full := path.Join("/proc/sys", keyPath) if !strings.HasPrefix(full, "/proc/sys/") && full != "/proc/sys" { return fmt.Errorf("sysctl key %q escapes /proc/sys", key) } return ioutil.WriteFile(full, []byte(value), 0644) }🟡 [重要] main.go:113 / hooks/syscontainer-hooks/main.go:165 — 运行目录以 0666 创建(权限不当)
CWE: CWE-732(关键资源权限不当)
规则: R08-依赖与配置(文件权限)/ R06-隐私
描述:
os.MkdirAll(config.IsuladToolsDir, 0666)以 0666 创建/run/syscontainer-tools。目录 0666 缺少执行位(无法被遍历进入),且对"组/其他"开放写位。同样代码在 hook main.go:165 重复出现。该目录存放 netns 挂载点文件(/run/syscontainer-tools/netns/<id>,0600)、udevd 锁文件、master 共享路径等敏感资源。影响: 目录权限设置错误:无执行位导致目录本身不可用(依赖默认 umask 与内核对目录需 x 位的处理产生混乱行为);且若 umask 较松,"其他"用户可写入该目录条目,存在符号链接替换/竞争风险。
CVSS: 3.7(Low)
数据来源: 硬编码
config.IsuladToolsDir = "/run/syscontainer-tools"(config/config.go:33)。调用栈
问题代码
// 文件: main.go:113 if err := os.MkdirAll(config.IsuladToolsDir, 0666); err != nil { logrus.Errorf("failed to set syscontainer-tools dir: %v", err) }修复建议
// 修复:目录需要执行位,且敏感运行目录应限制为 root 仅可读写执行。 if err := os.MkdirAll(config.IsuladToolsDir, 0750); err != nil { return fmt.Errorf("failed to set syscontainer-tools dir: %v", err) // 同时应返回错误而非仅记录 }🟡 [重要] utils/selinux.go:168-178 — NewContext 索引越界 panic(DoS)
CWE: CWE-754(对异常条件的不当检查)/ CWE-476
规则: R01-内存安全(越界访问)/ R05-错误处理
描述:
NewContext(scon)用strings.SplitN(scon, ":", 4)后直接访问con[0]/con[1]/con[2]/con[3],未校验切片长度 ≥ 4。若scon(来自selinux.Getfilecon读取的 xattr)为格式异常的短 SELinux 标签(字段少于 4 个),触发 index out of range panic,进程崩溃。影响: relabel 流程崩溃 → 容器 prestart hook 失败 → 容器启动失败(DoS);或
syscontainer-tools relabelCLI 崩溃。SELinux 标签可因文件系统损坏、非 SELinux 文件(xattr 为空/非标准)而异常。CVSS: 4.0(Medium,AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)
数据来源:
selinux.Getfilecon(...)返回的 xattr 字符串 →relabel.go:99(relabel CLI)与hooks/syscontainer-hooks/relabel.go:205(prestart relabel hook)。调用栈
问题代码
// 文件: utils/selinux.go:168-178 func NewContext(scon string) SELinuxContext { c := make(SELinuxContext) if len(scon) != 0 { con := strings.SplitN(scon, ":", 4) c["user"] = con[0] // 未校验 len(con) c["role"] = con[1] c["type"] = con[2] c["level"] = con[3] // len(con)<4 时 panic } return c }修复建议
func NewContext(scon string) SELinuxContext { c := make(SELinuxContext) if len(scon) != 0 { con := strings.SplitN(scon, ":", 4) if len(con) < 4 { logrus.Warnf("malformed selinux context: %q", scon) return c // 或返回 error 由调用方决定 } c["user"], c["role"], c["type"], c["level"] = con[0], con[1], con[2], con[3] } return c }🟡 [重要] libdevice/devices_unix.go:209-210 — FindSubPartition 切片越界 panic(DoS)
CWE: CWE-754 / CWE-125(越界读取)
规则: R01-内存安全(切片边界)
描述:
FindSubPartition将lsblk输出按\n切分后执行subDevNames := rawString[1 : len(rawString)-1],未校验len(rawString) > 1。当lsblk输出为空或仅 1 行(设备无子分区、lsblk 异常、设备名无效导致无输出)时,rawString[1 : len-1]触发 slice bounds out of range panic。对照types/device.go:154-158 GetBaseDevName已有if len(rawString) <= 1守卫,本函数缺失,属一致性缺陷。影响:
add-device --follow-partition或remove-device --follow-partition在异常 lsblk 输出下崩溃,工具进程退出(DoS),设备管理操作中断。CVSS: 4.0(Medium,AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H)
数据来源:
lsblk -n -p -r -o NAME <device.PathOnHost>输出 → 受设备 PathOnHost(CLI,绝对路径)及宿主机块设备状态影响。调用栈
另一入口:
libdevice/libdevice.go:289(RemoveDevice follow-partition)→config.FindSubPartition(不直接调 lsblk,但关联)。问题代码
// 文件: libdevice/devices_unix.go:200-211 func FindSubPartition(device *types.Device) []*types.Device { var subDevices []*types.Device cmd := exec.Command("lsblk", "-n", "-p", "-r", "-o", "NAME", device.PathOnHost) out, err := cmd.CombinedOutput() if err != nil { logrus.Errorf("Failed to lsblk %s : %v", string(out), err) return subDevices } rawString := strings.Split(string(out), "\n") subDevNames := rawString[1 : len(rawString)-1] // 缺少 len>1 守卫 → panic ... }修复建议
rawString := strings.Split(string(out), "\n") // 至少需包含表头行 + 一个数据行 + 末尾空串 if len(rawString) < 3 { return subDevices // 无子分区,安全返回 } subDevNames := rawString[1 : len(rawString)-1]🟡 [重要] relabel.go:93-94,103,75 — SELinux relabel 错误被吞没,安全标签可能未生效
CWE: CWE-754 / CWE-252(未检查返回值)
规则: R05-错误处理
描述:
relabel()中三处错误处理缺陷:(1)relabel.go:93-94if attr, err = selinux.Getfilecon(hostSystemd); err != nil { return nil }—— 出错时返回 nil(成功),掩盖失败;(2)relabel.go:103selinux.Setfilecon(context.path, con.Get())返回值被忽略;(3)relabel.go:75写 file_contexts.local 的错误仅 log 不返回。影响: SELinux relabel 可能在静默失败下报告"success"(relabel.go:137 打印 "SELinux relabel success"),导致容器在 SELinux 标签未正确设置的情况下运行,安全隔离被削弱而管理员不知情。
CVSS: 4.0(Medium,AV:L/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N)
数据来源:
selinux.Getfilecon/Setfilecon对/lib/systemd/systemd、path/isulad的 xattr 操作。调用栈
问题代码
// 文件: relabel.go:93-104 if attr, err = selinux.Getfilecon(hostSystemd); err != nil { return nil // 错误被吞,返回成功 } ... for _, context := range modifyContexts { con.SetType(context.conType) logrus.Infof("%s [%s]", context.path, con.Get()) selinux.Setfilecon(context.path, con.Get()) // 返回值未检查 }修复建议
if attr, err = selinux.Getfilecon(hostSystemd); err != nil { return fmt.Errorf("getfilecon %s failed: %v", hostSystemd, err) // 返回错误 } ... if err := selinux.Setfilecon(context.path, con.Get()); err != nil { return fmt.Errorf("setfilecon %s failed: %v", context.path, err) }🟡 [重要] hooks/syscontainer-hooks/prestart.go:306-342 — 容器可控宿主机内核模块加载
CWE: CWE-269(不当特权管理)/ CWE-863(授权不当)
规则: R02-注入(命令执行)/ R08-依赖与配置
描述:
DynLoadModule从spec.Process.Env中解析KERNEL_MODULES=环境变量,对每个模块名做isValidModuleName(正则^[A-Za-z0-9\-\_]*$)校验后,在宿主机执行exec.Command("modprobe", "-a", modules...)。模块名虽经字符白名单校验,但容器(或容器镜像/配置)可指定任意已安装的内核模块名,触发宿主机加载该模块。影响: 容器侧(拥有设置自身 Env 的权限)可诱导宿主机加载任意已安装内核模块,包括已知存在漏洞的模块,扩大宿主机攻击面、触发宿主机内核模块初始化副作用(资源消耗、设备创建等)。
CVSS: 5.5(Medium,AV:L/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:L)
数据来源:
spec.Process.Env(容器 OCI spec,容器创建者/镜像可控)→prestart.go:309遍历。调用栈
问题代码
// 文件: hooks/syscontainer-hooks/prestart.go:306-342 func DynLoadModule(state *configs.HookState, hookConfig *hconfig.ContainerHookConfig, spec *specs.Spec) error { kernelModules := []string{"-a"} for _, env := range spec.Process.Env { if strings.Contains(env, "KERNEL_MODULES=") { ... envModules := strings.Split(envValue[1], ",") for _, module := range envModules { if isValidModuleName(module) { kernelModules = append(kernelModules, module) // 容器提供模块名 } } } } ... cmd := exec.Command("modprobe", kernelModules...) // 宿主机加载 err := cmd.Run() ... }修复建议
// 修复:KERNEL_MODULES 不应由容器 spec 控制;改为宿主机管理员配置(如 /etc/syscontainer-tools/modules.conf)。 // 若必须支持容器指定,则维护宿主机允许加载模块白名单,仅允许白名单内模块;并审计每次 modprobe 调用。 var allowedModules = map[string]bool{"vxlan": true, "br_netfilter": true} // 管理员维护 for _, module := range envModules { if !allowedModules[module] { logrus.Warnf("reject kernel module %q not in allowlist", module) continue } kernelModules = append(kernelModules, module) }🟡 [重要] main.go:123 / hooks/syscontainer-hooks/main.go:52 — 日志路径符号链接跟随(root 追加写)
CWE: CWE-59(链接跟随不当)
规则: R06-隐私与数据 / R01-文件边界
描述:
os.OpenFile(logpath, os.O_CREATE|os.O_WRONLY|os.O_APPEND|os.O_SYNC, 0600)以 root 身份打开--log指定路径,OpenFile默认跟随符号链接。若logpath是指向敏感文件(如/etc/crontab、/etc/sudoers)的符号链接,日志内容将以 O_APPEND 追加到该敏感文件。影响: 拥有设置
--log的管理员若误指向符号链接,可污染目标文件;或在共享目录中遭预置符号链接攻击(若运行环境存在可被低权限用户写入的目录)。CVSS: 4.5(Medium,AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L)
数据来源: CLI
--log标志(main.go:69)与 hook--log标志(hooks main.go:152)。调用栈
修复建议
// 修复:使用 O_NOFOLLOW 拒绝符号链接,并校验父目录所有权。 fd, err := syscall.Open(logpath, syscall.O_CREAT|syscall.O_WRONLY|syscall.O_APPEND|syscall.O_NOFOLLOW, 0600) if err != nil { return err } logfile := os.NewFile(uintptr(fd), logpath) // 进一步:os.Lstat 校验非 symlink、属主为 root。🟢 [建议] pkg/udevd/udevd_controller.go:48 — TrimContainerID 越界 panic(防御性)
规则: R01-内存安全 / R05-错误处理
描述:
r.Container[:8]未校验len(r.Container) >= 8,若容器 ID 短于 8 字符则 panic。isulad 正常输出 64 位十六进制 ID 不受影响,但来自state.ID(HookState)或异常配置的短 ID 可触发崩溃。调用栈:
udevd_controller.go:139/155(AddRule/RemoveRule 比较)→udevd_controller.go:48TrimContainerID。修复建议:
if len(r.Container) < 8 { return r.Container }; return r.Container[:8],并在 AddRule 前校验 Container 非空。🟢 [建议] utils/selinux.go:138-143 — IsSystemdInit 弱子串匹配
规则: R07-代码质量 / R02-注入(逻辑绕过)
描述:
strings.Contains(initPath, "systemd")仅判断解析后路径是否包含 "systemd" 子串。任何路径名含 "systemd"(如/opt/systemd-backdoor/sbin/init)即被判为 systemd init,进入 systemd relabel 分支。调用栈:
hooks/syscontainer-hooks/relabel.go:141/274(relabel/prepoststop)→utils/selinux.go:139。修复建议: 改为精确匹配:
filepath.Base(initPath) == "systemd"或initPath == "/lib/systemd/systemd"。🟢 [建议] utils/selinux.go:61 / 91 — SeconfigGet/Set 解析器脆弱
规则: R07-代码质量 / R05-错误处理
描述:
strings.Split(txt, "=")要求恰好 2 字段,含=的值(如KEY=a=b)被丢弃;未 trim 空白,SELINUX = permissive形式无法匹配;SeconfigSet 非原子读-改-写(TOCTOU)。影响: SELinux 配置解析/写入丢值或失效,可能使 SELinux 模式判断错误。
修复建议: 使用
strings.SplitN(txt, "=", 2)+strings.TrimSpace;SeconfigSet 写临时文件后原子 rename。🟢 [建议] container/container.go:151-153 — GetCgroupPath 字符串切片解析脆弱
规则: R07-代码质量 / R05-错误处理
描述:
cgroupPath[1 : len(cgroupPath)-1]假设 isula inspect 输出为带引号 JSON 字符串,对null/非引号输出误解析(如null→ul),得到错误 cgroup 路径。影响: cgroup 路径错误导致后续设备 cgroup 权限更新失败或写错位置(与 🔴-2 叠加放大风险)。
修复建议: 用
strconv.Unquote或json.Unmarshal解析 isula 输出;显式处理null/空值。🟢 [建议] relabel.go:45-51 — relabelIsuladBinary 死代码(含潜在路径拼接风险)
规则: R07-代码质量(死代码)/ R02-注入(潜在)
描述:
relabelIsuladBinary(path, bin)定义后全项目无调用(setUpSelinuxLabel仅调用relabel(path))。其内fmt.Sprintf("%s/%s", path, bin)将 CLI 输入拼入semanage fcontext -a参数,若将来被启用且 path/bin 含特殊字符,存在 SELinux 上下文规则注入/路径穿越隐患。修复建议: 删除该死代码;如确需启用,对 path/bin 做字符白名单校验并使用
filepath.Join。🟢 [建议] hooks/syscontainer-hooks/relabel.go:181,275,279 / utils/selinux.go:130,139 — 路径字符串拼接而非 filepath.Join
规则: R02-注入(路径)/ R07-代码质量
描述: 多处以
s.Root + path、state.Root + systemdServiceFile字符串拼接构造路径,而非filepath.Join。若Root含尾部斜杠或path为异常形式,产生//或脆弱路径;且对..无防护(依赖上游 Root 可信)。修复建议: 统一改用
filepath.Join(s.Root, path),并对 Root 做基础校验。🟢 [建议] utils/utils.go:166-186 — RandomID 弱回退 + RandomFile 死代码
规则: R03-加密与认证(弱随机)/ R07-代码质量
描述:
RandomID在crypto/rand失败时回退为time.Now().UnixNano()(可预测);RandomFile存在 Stat-后-使用的 TOCTOU 竞争,且经 grep 确认全项目无调用(死代码)。修复建议: 删除未使用的
RandomFile;RandomID失败时应返回 error 而非可预测回退。🟢 [建议] utils/utils.go:82-83 — 默认 syslog 端口错误(541 应为 514)
规则: R08-依赖与配置 / R07-代码质量
描述:
syslogDefaultUDPService = "localhost:541"、syslogDefaultTCPService = "localhost:541",标准 syslog 端口为 514,541 非标准,默认回退时 syslog 转发失效。修复建议: 改为
localhost:514。🟢 [建议] pkg/ethtool/ethtool.go:64,82,100 — CGo C.CString 与 C 函数边界未验证
规则: R01-内存安全(CGo)/ R08-依赖
描述: 三处
cName := C.CString(etool.name)后传入 C 函数setNetDeviceTSO/TX/SG。unsafe.Pointer仅用于C.free(本身无害),但若 C 侧ethtool.h将设备名拷入固定缓冲(如strncpy到 IFNAMSIZ 未截断),存在缓冲溢出隐患。Go 侧未限制etool.name长度。修复建议: Go 侧校验
len(name) < 16(IFNAMSIZ);审查ethtool.h确保strncpy/snprintf带长度限制。🟢 [建议] libdevice/libdevice.go:197-200,360-363,446-449,512-515 — 错误聚合丢失上下文
规则: R05-错误处理 / R07-代码质量
描述: 多处将
retErr切片经fmt.Sprint拼成单字符串再errors.New,丢失错误包装与errors.Is/As能力,难以定位具体设备失败。修复建议: 使用
errors.Join(retErr...)(Go 1.20+)或自定义 multi-error 类型保留链。🟢 [建议] main.go:165 / hooks/syscontainer-hooks/main.go:150 — 忽略 SIGTERM 无法优雅停止
规则: R07-代码质量 / R05-错误处理
描述:
signal.Ignore(SIGINT, SIGHUP, SIGQUIT, SIGTERM)使工具无法被 init/systemd 优雅终止,强杀可能留下未清理的挂载/锁文件。修复建议: 仅忽略 SIGINT(前台交互),保留 SIGTERM 处理以执行清理。
🟢 [建议] pkg/udevd/udevd.go:93-117 — loadRules 解析脆弱
规则: R07-代码质量 / R05-错误处理
描述:
loadRules按,与"分割 udev 规则,以固定下标(cmdArray[2]/cmdArray[4])取容器 ID 与设备名,格式异常时静默continue,与 🔴-1 注入点同源,且解析/生成不对称。修复建议: 用结构化 udev 规则模型序列化/反序列化,避免字符串下标硬编码。
🟢 [建议] apply-patch:16-21 — 构建脚本未引用变量(潜在命令注入)
规则: R02-注入(shell)/ R08-依赖与配置
描述:
patch -p1 -F1 -s < $line中$line未加引号,若series.conf中某行含空格或$(...)/反引号,将触发单词拆分或命令替换。该脚本为构建期维护者文件,非运行时,风险受限。修复建议:
patch -p1 -F1 -s < "$line"并校验行内容为合法 patch 文件名。⚠️ 疑似[重要] libnetwork/bridge/bridge.go:38 / libnetwork/bridge/ovs/driver.go:43,53 — ovs-vsctl 参数注入(bridge 名)
置信度: 55%
描述:
exec.Command("ovs-vsctl", "br-exists"|"add-port", bridge, netif)中bridge未校验是否以-开头,理论上可被--version/--option=...形式注入 ovs-vsctl 选项。netif因先经netlink.LinkByName校验为真实链路名而缓解。建议: 对 bridge 名做接口名白名单校验(
^[A-Za-z0-9._\-]{1,15}$),并在 exec 参数中插入--分隔符:exec.Command("ovs-vsctl", "br-exists", "--", bridge)。⚠️ 疑似[重要] types/device.go:149,163 / libdevice/devices_unix.go:203 / libdevice/binds_unix.go:117 — lsblk/df 参数注入(设备/路径名)
置信度: 40%
描述: 多处
exec.Command("lsblk"|"df", ..., deviceName|path)将设备路径作为参数传入,若该值以-开头可被解释为工具选项。多数路径经ParseMapping校验为绝对路径(以/开头)而缓解,但binds_unix.go:117的path(来自findPathDevice)与Qos.Path校验较弱。建议: 在 exec 调用前统一校验路径以
/开头,或加入--分隔参数。⚠️ 疑似[建议] utils/hooks.go:138-144 — GetUIDGid 对 spec.Linux nil 解引用
置信度: 50%
描述:
for _, namespace := range spec.Linux.Namespaces假设spec.Linux非 nil;若容器 config.json 缺少linux字段,spec.Linux为 nil 则 panic。isulad 正常生成 Linux 字段而缓解。建议: 增加
if spec.Linux == nil { return -1, -1 }守卫。⚠️ 疑似[建议] libdevice/binds_unix.go:138-139 — findPathDevice df 输出解析脆弱
置信度: 45%
描述:
devs := strings.Split(line, " ")后取devs[0],df -P输出含多空格对齐时会产生空串元素,导致DeviceFromPath("")误判。非 panic 但解析不可靠。建议: 使用
strings.Fields(line)取首字段,并校验非空。【缺陷所属的os版本】
24.03-LTS-next
【内核版本】
kernel6.6
【缺陷所属软件及版本号】
syscontainer-tools
【环境信息】
【问题复现步骤】
【实际结果】
【期望结果】
【其他相关附件信息】
【缺陷详情及分析指导参考链接】
二、缺陷分析结构反馈
影响性分析说明:
缺陷严重等级:(Critical/High/Moderate/Low)
缺陷根因说明:
受影响版本排查(受影响/不受影响):
openEuler-20.03-LTS-SP4
openEuler-22.03-LTS-SP4
openEuler-24.03-LTS-SP1
openEuler-24.03-LTS-SP3
openEuler-24.03-LTS-SP4
修复是否涉及abi变化(是/否):
openEuler-20.03-LTS-SP4
openEuler-22.03-LTS-SP4
openEuler-24.03-LTS-SP1
openEuler-24.03-LTS-SP3
openEuler-24.03-LTS-SP4