macOS 首次执行为何变慢:一则 Gatekeeper 排障记录及其边界

原作者:Yukang(CatCoding)。原文:《macOS 奇怪的安全扫码机制》,研究页记录日期为 2026 年 2 月 12 日。本文经授权整理,保留原作者的观察、排查顺序和推测,并明确区分三者。作者没有列出确切 macOS 与 VS Code 版本,本次也没有复现这些耗时。

作者在运行 Rust 编译器测试时发现速度异常缓慢,CPU 资源没有充分使用。几年前他遇到过相近现象,并发表过相关排查文章;这次现象虽然相似,解决过程却不同。他不确定变化来自 macOS 更新还是 VS Code 更新,因此先把问题缩小为“新建可执行脚本的第一次运行”。

macOS案例的对照关系:VS Code首次直接执行较慢、再次执行较快;Warp或显式/bin/sh较快;FDA更改后及撤销后都快,无法分离权限与缓存效应
原创对照示意图。数值来自原作者个案,箭头表达排查顺序,不代表已证明的内部因果机制。

从新文件的首次、再次执行开始

原脚本循环十次,每次用 openssl rand -hex 10 生成随机文件名,在 /tmp/speed_test 下写入只打印 Hello 的 shell 脚本,赋予执行权限,然后分别测量同一文件的第一次与第二次直接执行。TIMEFORMAT="%R" 用于输出墙钟耗时,脚本输出重定向到 /dev/null,计时结果从标准错误捕获。

原文开头有递归删除固定目录的 rm -rf /tmp/speed_test,并使用未加引号的 $FILE。这里不提供一段可一键执行的破坏性原样副本。下面是编者安全改写:新建独立临时目录、不删除任何目录,只给当前用户执行权限,并对路径加引号。逻辑仍是十个新文件各运行两次,但它只经过静态审核,未运行。

#!/bin/bash
set -eu
test_dir=$(mktemp -d "${TMPDIR:-/tmp}/wwj-speed-test.XXXXXX")
printf '测试目录:%s\n' "$test_dir"

for i in {1..10}; do
    filename=$(openssl rand -hex 10)
    file="$test_dir/$filename.sh"
    printf '#!/bin/sh\necho Hello\n' > "$file"
    chmod u+x "$file"

    first=$(TIMEFORMAT="%R"; (time "$file" > /dev/null) 2>&1)
    second=$(TIMEFORMAT="%R"; (time "$file" > /dev/null) 2>&1)
    printf '第一次: %s  第二次: %s\n' "$first" "$second"
done
# 不自动删除:仅在确认路径属于本次隔离目录后自行清理。

它仍会创建文件并运行新生成的脚本,只适合使用者自己批准的隔离开发环境。随机命名减少的是重用同一路径的干扰,不能保证系统的所有安全缓存、父进程状态或内容缓存都被重置。

原作者在 VS Code 集成终端中记录到以下输出:

第一次: 0.525  第二次: 0.007
第一次: 0.290  第二次: 0.009
第一次: 0.280  第二次: 0.007
第一次: 0.272  第二次: 0.008
第一次: 0.307  第二次: 0.008

作者概括为相差约 30–50 倍;这是原文描述,各行实际比值并不相同。换到 Warp 终端运行同类脚本时,两次都在 0.006 秒左右。该对照提示终端或其所处安全上下文可能有关,但没有证明“所有 VS Code 都慢”或“换终端普遍提升 30–50 倍”。

用日志缩小到 syspolicyd

作者确认已把 VS Code 加入“系统设置 → 隐私与安全性 → 开发者工具”,认为这不像上次的 SIP 问题。他也认为 0.2–0.5 秒的延迟不像普通磁盘缓存差异,于是查看最近两分钟的安全策略日志:

log show --predicate 'subsystem == "com.apple.syspolicy.exec"' \
  --last 2m --style compact

原日志包含 GK performScan、NSOSStatusErrorDomain Code=-67062 和 GK evaluateScanResult,且作者观察到每次执行新文件都会出现扫描记录:

GK performScan: PST: (path: 8d0e4c2de41c3e77), (team: (null)), (id: (null)), (bundle_id: (null))
Error Domain=NSOSStatusErrorDomain Code=-67062
GK evaluateScanResult: 2, PST: (path: 8d0e4c2de41c3e77), ... (bundle_id: NOT_A_BUNDLE), 0, 0, 1, 0, 7, 7, 0

这些是原文摘录,不是本次读取的机器日志。日志中的字段或错误码不能脱离系统版本单独解释为一个确定故障。对实际机器排查时,应匹配执行时间、文件和进程;导出日志前注意其中可能包含路径、应用标识及其他个人信息。

直接执行和显式解释执行

作者进一步把计时命令改为显式调用解释器:

(time /bin/sh "$FILE" > /dev/null) 2>&1

这里的引号是编者补充,原文使用未引号化的变量。作者记录:直接 ./script.sh 约 0.248 秒,/bin/sh ./script.sh 约 0.006 秒。这两种方式的执行路径不同:前者让脚本路径进入直接执行流程,后者是已运行的 shell 读取脚本内容。

作者据此将延迟关联到 Gatekeeper,提出从 execve、AppleSystemPolicy.kext 的 MACF hook(mpo_proc_notify_exec_complete)到 syspolicyd 的解释。他原文也明确承认,具体内部流程来自实验推断和社区逆向,而不是 Apple 的公开设计说明。不能把“显式解释运行这次快”扩张成“解释器运行的任意代码都不接受安全检查”,更不能把这种差异当作运行未知脚本的方法。

Apple 的 Gatekeeper 与运行时保护文档说明了签名、公证、完整性及已知恶意内容检查等保护职责,但没有为本个案中 FDA、负责进程和所谓快速路径之间的每一步关系背书。

赋予 Full Disk Access 后发生了什么

接着,作者在“隐私与安全性 → 完全磁盘访问权限”中给 VS Code 开启 Full Disk Access(FDA),重启 VS Code,再运行脚本。记录变成:

第一次: 0.005  第二次: 0.005
第一次: 0.005  第二次: 0.005
第一次: 0.006  第二次: 0.006

作者称延迟消失,并且日志里不再出现此前的 performScan。他用 TCC 权限 kTCCServiceSystemPolicyAllFiles、responsible process(负责进程)以及高信任来源来解释现象,推测 VS Code 的子进程因此走了快速评估路径;他还把 Warp 的表现关联到开发工具信任列表。

这些内部解释需继续保留“作者推测”的限定。尤其是“FDA 是最高级别信任授权”“FDA 会跳过完整扫描”等表述,没有在本次官方资料中得到证实。FDA 的实际后果包括扩大应用对敏感文件的访问面;不能为了未经确认的性能收益,默认让所有编辑器或终端获取该权限。

撤销权限后仍然快:为什么这不是因果证明

原作者随后移除 VS Code 的 FDA 权限,重启应用,再跑脚本,仍然很快。他指出 /var/db/SystemPolicyConfiguration/ExecPolicy 这个 SQLite 数据库(其机器当时约 35 MB),并查询内核统计:

sysctl security.mac.asp.stats.cache_entry_count
# 原作者输出:security.mac.asp.stats.cache_entry_count: 4700

作者将其理解为信任结果被持久化,并称系统会记住过去的决定。但这个实验顺序没有把权限变化与缓存状态独立控制:第一次授权时已经改变了运行历史,撤销后也没有恢复同样的起点。因此“FDA 造成提速”与“缓存或其他状态造成提速”的贡献无法被严格分开;查询到缓存条目数量,也不能证明具体哪一条缓存导致结果。

原文猜测重启或更极端地清理 ExecPolicy 数据库可能帮助复现。本文保留这一猜测的来源,但不提供删除安全数据库的命令,也不建议据此清理它。那不是已验证的复现步骤,会改变系统安全状态。

把这个案例变成可复核的排查方法

原文最后建议先检查开发者工具列表,再尝试 FDA,并给出查看最近扫描记录的命令:

log show --predicate 'subsystem == "com.apple.syspolicy.exec"' \
  --last 5m --style compact | grep performScan

作为编者补充,更稳妥的顺序是先记录 macOS、终端、VS Code 版本及现有权限;用无秘密、无网络操作的最小测试比较首次与再次执行,交叉安排终端和执行方式;再把计时与安全日志时间对应起来。若要评估权限影响,应设计能控制缓存、顺序及重新启动条件的试验,不把一次“变快”作为普遍结论。

本稿没有修改 FDA、Developer Tools、Gatekeeper、SIP 或任何安全数据库,没有读取用户机器日志,也没有执行生成脚本。对原文的修改包括移除递归删除步骤、使用独立临时目录、补路径引号,并把机制断言恢复为有边界的个案推断。没有发现其他脚本问题不等于无漏洞。

原文参考还包括 Scott Knight 的 syspolicyd 研究与 Howard Oakley 的 macOS 研究,它们是原作者理解内部机制的线索。保留 Yukang / CatCoding 的作者归属;配图为原创对照图,不是伪造的测试或系统截图。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容