Skip to content

fix(security): add path traversal protection for sysfs and module operations#682

Merged
deepin-bot[bot] merged 1 commit into
linuxdeepin:masterfrom
add-uos:fix-364585-add-path-traversal-protection
Jun 5, 2026
Merged

fix(security): add path traversal protection for sysfs and module operations#682
deepin-bot[bot] merged 1 commit into
linuxdeepin:masterfrom
add-uos:fix-364585-add-path-traversal-protection

Conversation

@add-uos
Copy link
Copy Markdown
Contributor

@add-uos add-uos commented Jun 4, 2026

Add SecurityUtils module with input validation functions to prevent path traversal attacks. Applied validation to authorizedEnable, removeEnable, installDriver, and blacklist operations.

Log: 添加路径穿越安全防护
PMS: BUG-364585
PMS: BUG-364575
PMS: BUG-364567
PMS: BUG-364565
Influence: 所有涉及 sysfs 路径和内核模块名的操作现在都经过安全校验,防止路径穿越攻击。

…rations

Add SecurityUtils module with input validation functions to prevent path
traversal attacks. Applied validation to authorizedEnable, removeEnable,
installDriver, and blacklist operations.

Log: 添加路径穿越安全防护
PMS: BUG-364585
PMS: BUG-364575
PMS: BUG-364567
PMS: BUG-364565
Influence: 所有涉及 sysfs 路径和内核模块名的操作现在都经过安全校验,防止路径穿越攻击。
Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @add-uos, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@deepin-ci-robot
Copy link
Copy Markdown

deepin pr auto review

这是一次非常优秀的安全加固提交。代码引入了统一的 securityutils 模块,针对 Linux 设备管理器中常见的路径穿越、符号链接穿越和恶意输入等安全风险进行了系统性的拦截。

以下是对本次代码变更的详细审查意见,涵盖语法逻辑、代码质量、代码性能和代码安全四个方面:

一、 语法与逻辑审查

  1. isPathWithinDirectory 逻辑缺陷:目标目录不存在时永远返回 false

    • 问题QFileInfo::canonicalFilePath() 会解析符号链接并返回真实路径,但前提是文件或目录必须物理存在。如果文件不存在,它返回空字符串,导致函数返回 false。在 addModBlackListsetModLoadedOnBoot 中,如果配置文件尚未创建,isPathWithinDirectory 将直接返回 false,导致正常的业务逻辑被拦截。
    • 改进建议:对于可能不存在的目标路径,应使用 QDir::cleanPath() 进行逻辑上的路径规范化,而不是依赖 canonicalFilePath() 解析物理文件系统。或者先检查文件是否存在,不存在则用 cleanPath,存在则用 canonicalFilePath
    bool isPathWithinDirectory(const QString &filePath, const QString &baseDir) {
        if (filePath.isEmpty() || baseDir.isEmpty()) return false;
    
        QFileInfo dirInfo(baseDir);
        QString canonicalDir = dirInfo.canonicalFilePath();
        if (canonicalDir.isEmpty()) return false; // 基准目录必须存在
        if (!canonicalDir.endsWith(QLatin1Char('/'))) canonicalDir += QLatin1Char('/');
    
        QFileInfo fileInfo(filePath);
        QString finalPath;
        // 如果文件存在,解析真实路径(防符号链接);如果不存在,使用逻辑规范化路径(防..穿越)
        if (fileInfo.exists()) {
            finalPath = fileInfo.canonicalFilePath();
        } else {
            finalPath = QDir::cleanPath(fileInfo.absoluteFilePath());
        }
        
        return finalPath.startsWith(canonicalDir);
    }
  2. isSafeSysfsPath 逻辑缺陷:设备热插拔导致校验失败

    • 问题:同样因为 canonicalFilePath() 的特性,如果传入的 sysfs 路径对应的设备刚刚被拔出,/sys 下对应的文件消失,canonicalPath 将为空,函数返回 false。但在设备管理器中,操作一个不存在的设备通常应该由后续的 QFile::open() 来报错,而不是被安全校验误判为“攻击”。
    • 改进建议:如果 relativePath 已经通过了不包含 .. 的校验,且以 / 开头(或为合法相对路径),当 canonicalPath 为空时,可以降级使用 QDir::cleanPath 再次验证,确保其没有越界即可。
  3. validateModNameForInstallkernelRelease 未校验

    • 问题:函数内部对 modName 做了严格的白名单校验,但 kernelRelease 是通过 Utils::kernelRelease() 动态获取的。如果该函数被恶意劫持或返回异常值(如包含 ../),路径拼接依然可能穿越。
    • 改进建议:增加对 kernelRelease 的基本合法性校验(如:只允许字母、数字、点、横杠)。
    static const QRegularExpression kValidKernelRelease(QLatin1String("^[a-zA-Z0-9._-]+$"));
    if (!kValidKernelRelease.match(kernelRelease).hasMatch()) {
        errMsg = "Invalid kernel release version";
        return false;
    }

二、 代码质量审查

  1. 正则表达式性能与生命周期优化

    • 问题isValidModName 中使用了 static const QRegularExpression。在 Qt5 中,QRegularExpression 编译正则表达式有一定开销,使用 static 避免重复编译是好的。但在 Qt6 中,建议使用全局常量或 Q_GLOBAL_STATIC,并且在 Qt 5.4+ 中,可以通过 optimize() 方法进一步优化执行效率。
    • 改进建议
    // 在 cpp 文件顶部定义
    static const QRegularExpression kValidModNameRegex = []() {
        QRegularExpression re(QLatin1String("^[a-zA-Z0-9_-]+$"));
        re.optimize(); // 预编译优化,提升匹配性能
        return re;
    }();
  2. 错误日志信息泄露风险

    • 问题:在 validateModNameForInstall 中,errMsg = QString("Invalid module name: %1").arg(modName); 如果这个 errMsg 最终通过 DBus 或界面展示给用户,恶意构造的输入可能会被渲染,导致潜在的注入问题(虽然 Qt 日志和普通 UI 相对安全,但遵循安全最佳实践,不应将原始输入直接反馈)。
    • 改进建议:日志中可以记录原始输入(qCWarning),但返回给上层调用者的 errMsg 建议脱敏或泛化:
    qCWarning(appLog) << "Invalid module name rejected:" << modName; // 日志里记录
    errMsg = "Invalid module name provided"; // 返回给调用者的信息泛化

三、 代码性能审查

  1. 高频调用的 sysfs 操作性能损耗
    • 分析isSafeSysfsPath 中调用了 canonicalFilePath(),这会触发一次真实的文件系统 stat/lstat 系统调用。对于设备遍历等可能批量调用的场景,会产生性能损耗。
    • 结论:由于这是涉及系统安全控制(启用/禁用设备)的操作,不属于微秒级高频热点路径,为了安全牺牲极小的性能是完全可以接受且必要的。保持现状即可。

四、 代码安全审查

  1. isSafeSysfsPath 的相对路径规范问题

    • 问题:如果 relativePath 传入的是 /../etc/passwd,当前逻辑 fullPath = "/sys" + "/../etc/passwd" 变为 /sys/../etc/passwd。虽然 canonicalFilePath() 会将其解析为 /etc/passwd 并被 startsWith("/sys/") 拦截,但依赖文件系统解析来防御攻击属于“被动防御”。
    • 改进建议:在字符串拼接前,进行更严格的格式断言。sysfs 的相对路径不应该以 / 开头,且不应包含连续的 //
    if (relativePath.startsWith(QLatin1Char('/')) || relativePath.contains(QLatin1String("//"))) {
        return false;
    }
  2. TOCTOU (Time-of-Check to Time-of-Use) 竞态条件

    • 分析isPathWithinDirectory 检查时文件不存在(或合法),但在真正 QFile::open 写入前,攻击者通过符号链接劫持了路径。这是经典的 TOCTOU 问题。
    • 防范建议:在 Linux 系统编程中,防御 TOCTOU 的最有效方式是:不直接打开拼接的路径,而是打开安全的父目录,使用 openat() 配合 O_NOFOLLOW 标志打开文件。但在 Qt 层面没有直接封装这种安全打开方式。
    • 折中方案:当前的 canonicalFilePath() 检查已经极大提高了攻击门槛。作为补充,可以在写入文件时检查文件描述符是否为符号链接(如果在 Qt 层难以实现,至少确保创建文件时不使用现有文件的符号链接)。对于设备管理器这种通常只有 root 权限运行的进程,当前防护级别已属优秀。

总结

本次提交的代码结构清晰,将安全校验逻辑抽离为独立模块的做法非常值得肯定,有效弥补了底层业务逻辑中对路径穿越防范的不足。主要需要修正的是 QFileInfo::canonicalFilePath() 对不存在文件返回空字符串导致的业务逻辑阻断问题,建议针对不存在的文件采用 QDir::cleanPath() 进行逻辑路径校验作为降级方案。其余安全与性能方面表现良好。

@deepin-ci-robot
Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: add-uos, lzwind

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@add-uos
Copy link
Copy Markdown
Contributor Author

add-uos commented Jun 5, 2026

/forcemerge

@deepin-bot
Copy link
Copy Markdown
Contributor

deepin-bot Bot commented Jun 5, 2026

This pr force merged! (status: unstable)

@deepin-bot deepin-bot Bot merged commit b0e883f into linuxdeepin:master Jun 5, 2026
19 of 21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants