四个开发工具的信任边界:回放导出与 CarParams 注入

作者:

在

结论:四个开发工具的信任等级并不相同:print_flags.py 只读枚举,test_fw_query_on_routes.py 只读已记录固件,run_process_on_route.py 会把新旧消息合并导出,而 set_car_params.py 会写入当前参数库。前两者可分析,后两者带副作用,其中最危险的是把 CarParams 写入下放给任意插件。

工具清单与副作用等级

工具 源码链路 静态结论
test_fw_query_on_routes.py 参数可为单一路段或文件路径;文件分支取每行再交 SegmentRange,未指定段则拼 /0,随后按 QLOG 读 carParams 并分别做精确/模糊固件匹配。 原主入口探针确认列表行尾换行未剥离,正则在内部 try 前抛断言并中断整批;读取器构造失败也在 try 前传播。脚本只读已记录固件,不向 ECU 发查询。
批量统计与不匹配输出 以 dongle_id 去重,在读路段前就把它加入集合;对不匹配固件按车型、地址去重汇总,最终打印精确/未识别/模糊计数。 假消息探针确认首个路段没有 carParams 时,同设备第二路段仍被跳过;不支持的 panda 类型会提前结束,随后又报告无 CarParams。输出含 VIN 与固件字节,需视为敏感诊断数据。
run_process_on_route.py 默认从 CONFIGS 选全部进程,再减去被排除的两个模型进程;读完整路段调用回放,只按实际产生的消息类型删除旧输入,再将新旧消息按时间合并并保存。 探针确认零回放输出会保留并重排全部旧消息,未知白名单会选出空配置但仍读取和导出原日志。文件名进程部分来自集合迭代顺序,不是稳定标识。导出不调用车型日志净化器,副本可能携带原始 CAN/VIN。
set_car_params.py 有路段时只读第一条 qlog 中第一条 carParams;无参数时新建空白消息,只写两个纵向标志;随后为每个键新建 Params(),按三个键顺序阻塞写入。 探针确认缺少 carParams 时在任何写入前抛 IndexError;第二键抛异常时第一键已调用成功、第三键未调用。原 Python Params.put() 会丢弃底层负错误码,故底层写盘失败可能不抛异常。本层无回滚或读回,不校验平台、VIN、safety 或当前车辆身份。
print_flags.py 遍历 BRANDS 与各平台配置标志,按标志列出车型名称。 Platforms 与归档的 CAR 枚举均未定义脚本调用的 with_flags();存在非零标志配置,非零假枚举时原主体抛 AttributeError,零标志枚举则无输出。

run_process_on_route:替换与导出的边界

该脚本默认从 CONFIGS 选全部进程,再减去被排除的两个模型进程,读完整路段后调用 replay_process(),只按本轮实际产生的消息类型删除旧输入,再把新旧消息按 logMonoTime 合并并 save_log()。

假依赖主入口探针确认:零回放输出会保留并重排全部旧消息;未知白名单会选出空配置但仍读取和导出原日志。另外,文件名中进程部分来自集合迭代顺序,不是稳定标识;save_log() 不调用车型日志净化器,副本可能携带原始 CAN/VIN。因此不能以脚本名称替代对进程副作用的审查。

回放导出还有一个隐含前提:所选进程必须在当前代码版本下可复现,否则混合出的日志既不等于原始输入,也不等于一次真实回放;这正是需要把代码版本与被替换主题一并写入导出元数据的原因。

test_fw_query_on_routes:批量审计的失败面

该脚本的参数可为单一路段或文件路径;文件分支用 list(open(...)) 直接取每行,再交 SegmentRange;未指定段则拼 /0,随后按 QLOG 读取 carParams 并分别做精确/模糊固件匹配。探针确认列表行尾换行未剥离,正则在内部 try 前抛断言并中断整批;读取器构造失败同样在 try 前传播。

统计侧以 dongle_id 去重,且在读路段之前就把该键加入集合;对不匹配固件按车型、地址去重汇总,最终打印精确、未识别与模糊匹配计数。需要提醒的是:首个路段没有 carParams 时,同设备第二路段仍被跳过;不支持的 panda 类型会提前结束,随后又报告无 CarParams。这些输出包含 VIN 与固件字节,应视为敏感诊断数据。

set_car_params:三键写入无补偿

有路段时,脚本只读取第一条 qlog 中第一条 carParams;无参数时新建空白消息,只把 openpilotLongitudinalControl=True、alphaLongitudinalAvailable=False 写入。随后为每个键新建 Params(),按 CarParams、CarParamsCache、CarParamsPersistent 的顺序阻塞写入。

假 Params 探针确认:缺少 carParams 时在任何写入前抛 IndexError;第二键抛异常时第一键已调用成功、第三键未调用。但原 Python Params.put() 会丢弃底层负错误码,故底层写盘失败可能不抛异常,脚本仍继续写第三键。本层没有回滚或读回,也不校验平台、VIN、safety 或当前车辆身份。另据键属性,前两者会被 manager 启动清理,CarParamsPersistent 持久化。

键 写入时机 生命周期
CarParams 三键顺序写入的第一键 被 manager 启动清理。
CarParamsCache 第二键 被 manager 启动清理。
CarParamsPersistent 第三键 持久化。

插件化:写入权归谁

print_flags.py 的只读枚举也提示了一件事:即便修复了缺少 with_flags() 的问题,它仍只枚举本地声明,不证明硬件或 safety 已启用。实际车型能力仍须从车型接口、DBC 与 panda safety 交叉验证。这也说明开发工具的结论天然是局部的:它们验证的是脚本自身的控制流,而不是目标车辆的参数正确性。

探针 覆盖的契约
标志枚举探针 核对 Platforms 与各品牌方法清单,确认缺少 with_flags() 的异常与零输出分支。
参数注入主入口探针 验证默认两个标志、三键写入顺序、只取首个 carParams,以及第二键异常后不触发第三键。
固件审计主入口探针 验证行尾换行、读取器构造异常、同设备去重及不支持 panda 后的状态报告。
回放导出主入口探针 确认剔除被排除的模型进程、只替换实际产生的类型、零输出仍导出全部旧消息。

批量固件审计应先把路段列表解析、去重与错误计数做成显式结果;精确/模糊候选只能用于平台审核,不自动修改固件库。回放导出的日志需携带输入路段、所选进程、代码版本、被替换主题、生成主题和隐私标记,避免把混合旧/新消息误当统一来源。CarParams 的写入应归受控车辆身份服务所有,要求平台/线束/DBC/safety 绑定、三键一致性门和读回验证,不能下放给任意插件。

常见问题

为什么 set_car_params.py 不能在车端随意试跑?

因为它会写入当前参数库:按 CarParams、CarParamsCache、CarParamsPersistent 三键顺序阻塞写入,且本层没有回滚或读回、不校验平台/VIN/safety/车辆身份。底层写盘失败还可能因负错误码被丢弃而不抛异常。

回放导出会混入旧消息吗?

会。探针确认零回放输出时,全部旧消息仍会进入导出并按时间排序;脚本只按本轮实际产生的消息类型删除旧输入。导出还不调用车型日志净化器,副本可能携带原始 CAN/VIN。

为什么固件审计脚本可能整批中断?

因为文件分支取每行时行尾换行未剥离,SegmentRange 的正则在内部 try 之前就抛断言并中断整批;读取器构造失败也在 try 前传播,同样会中断。

print_flags.py 能证明某车型能力已启用吗?

不能。它只遍历本地品牌与平台配置来列出车型名称,且归档中并未定义它调用的 with_flags();即使修复,它仍只枚举本地声明,不证明硬件或 safety 已启用。

延伸阅读

参考资料

  • 项目 02 —— openpilot-research/DEV_FW_REPLAY_CARPARAMS_TOOLS_TRUST_BOUNDARY_DEEP_DIVE.md(取用了四个开发工具的入口、数据流与失败边界及探针结论)。
  • 项目 02 —— openpilot-research/PROCESS_REPLAY_VALIDATION_DEEP_DIVE.md(仅用于确认回放进程与验证的边界)。

说明:本文为对固定源码快照的静态结构分析,未运行完整测试套件、未连接硬件、未验证目标车型的真实参数组合。文中所有阈值、行号索引与配置均出自源文档,标注为未验证的结论保留其原有边界。

更新记录

首次发布 2026-10-11 最近核对:2026-10-11

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注