Files
MapLibreParticleLab/docs/报告/STEP-119_SymbolRetained生命周期修复_Codex完成报告_v1.md
tei 343ca2fa68
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled
fix: secure Symbol Retained drawable lifetime and record investigations
2026-10-12 23:27:06 +09:00

15 KiB
Raw Permalink Blame History

STEP-119 SymbolRetained 生命周期修复 Codex 完成报告 v1

日期:2026-10-12(JST)。状态:IMPLEMENTED / WAITING_RDC_VALIDATION。接续此前因用量限制中断的同一任务,四个既有修改经静态复审及明确缺口修正后交付。没有运行编译或测试,整体 STEP-119 仍未验收,不报告整体 PASS。

1. 模型、环境与 Git 基线

  • 用户本轮明确指定 GPT-6.1 Sol High,即 gpt-6.1-sol / high。当前工具没有执行器模型/推理参数元数据;轻量查找 /Users/tei/.codex/sessions/2026/10/12 返回目录不存在,实际运行参数无法独立核验。记录用户指定值,不把它写成已从执行器日志核实。未主动升级模型、启动第二实例或子代理。
  • Mac.lan,Darwin 24.6.0,x86_64;目录 /Users/tei/dev/MapLibreParticleLab。
  • 开工/交付 branch=particle/main,HEAD=badb515d590fbdf221751b6677de3da0a5650d7e。基线为四个已有 tracked 修改及 STEP-110/111 两份 untracked 任务书;不从头复写,不改分支/worktree/HEAD。
  • git submodule status 已只读检查,没有变更前缀;无子模块修改。未 commit/push/tag/fetch/pull/rebase/reset/clean,未回读远端 HEAD(本轮禁止远端操作)。
  • 只读当前 v25 Source Lock:Android/RN 13.6.1-particle.25,iOS 6.29.0-particle.25,冻结构建源 f32f6f45fd359007e8b3c77911b66d9e7fc93848。发行记录与制品未修改,本轮未生成制品,不能以现有 v25 包验证本工作区修复。
  • 首先读取 Astra 原文 /tmp/fzg1_astra_guidance_20261012.md,再读取仓库规则、当前资料和现有 diff。当前用户续作授权优先于旧任务中的历史基线/跨项目调查和 Git 收尾安排。

2. 实际修改文件及生命周期不变量

文件 实际变更与不变量
src/mln/gfx/symbol_drawable_data.hpp 内部数据保存强类型 atlas 与实际原生 atlas tweaker;创建时两者来自同一份 atlas,增加内部 Drawable 类型标记。它们不拥有 RenderTile 或底层 Tile。
src/mln/renderer/layers/render_symbol_layer.hpp 私有同源/绑定/整组复用/采集辅助方法及当前地址→ID/atlas 映射;仅保存当前代次,不建立历史容器列表。friend 测试入口不修改公共 API。
src/mln/renderer/layers/render_symbol_layer.cpp prepare 从同次 source 获取 raw owner 与原 Y 排序列表,先校验成员地址再读 ID/atlas,保留 fading tile。成功复用先检查 tile/pass 整组,再全部刷新当前 RenderTile/owner;Bucket、atlas、layer tweaker、数据/类型或成员失配整组移除重建,atlas 缺失移除旧绘制。Retained 从强引用 atlas 取证,旧指针只作当前地址映射查找,使用 Drawable 自己的 ID;类型未知/绑定不符/未上传立即拒绝。owner 只放入 resources,不序列化 owner 地址。
test/renderer/retained_underlay_cache.test.cpp 13 项 SymbolRetainedLifetime 源码调用生产 prepare/bind/rebind/retained 辅助路径与原生 atlas tweaker;真实类接口/签名和友元访问逐项静态核对。atlas-only TileRenderData 显式覆盖空 Bucket/LayerRenderData 查询,避免基类 assert;补组内类型失配、Text/IconRGBA/IconSDF/Halo 稳定身份、未知类型及证书释放检查。
docs/50_Step记录/STEP-119_SymbolRetained生命周期修复_v1.md(新增) 本阶段目标、基线、完成/待验清单及交接。
docs/报告/STEP-119_SymbolRetained生命周期修复_Codex完成报告_v1.md(新增) 本报告,记录事实、准确命令、风险和停止条件。
粒子开发记录.md 仅末尾追加本次实现与未验证事实,保留历史内容。

两个新文档路径创建前已确认不存在,同主题无旧版本,选择 v1;使用独占创建,未覆盖旧报告。前三个生产文件及测试均沿用开工时修改;本续作生产补丁仅增加 Retained 对未知 SymbolType 的拒绝,测试补丁只处理上述明确缺口及覆盖不足。

所有权与稳定性

  1. Drawable 只由通过同源校验的 owner/元素绑定。复用检查不从旧 RenderTile 读取 ID/Bucket/atlas;任何一个 Text/Icon/Halo 成员不兼容时整组移除,避免 updateTile() 的“任一成功”留下半新半旧组。
  2. 同 Bucket/atlas 保持 Drawable 和 tweaker 身份,仅刷新 owner/RenderTile 与既有动态属性;source 每次产生新容器不应自行造成每帧重建或证书字节变化。
  3. 当前容器拥有 RenderTile;RenderTile 独占 renderData;真实 GeometryTileRenderData 强持有 LayoutResult/atlas,LayoutResult 负责槽位释放。证据 resources 保活容器,但不调用 result.retain(renderTilesOwner),因此 owner 地址不进入比较字节。证书 candidate/completed 替换或 invalidate 后旧证据可退出,未新增无界容器。
  4. Retained 先验证当前地址/保存的 ID、内部数据类型、atlas 来源、实际 tweaker 和 shader,再检查真实 glyph/icon 纹理绑定及上传状态。任何缺证立即返回 incomplete,不沿失效裸指针追读 renderData/atlas。
  5. RenderTile 内 Tile& 的寿命独立;保活容器不保活 Tile,不允许把旧 Tile 上的 getOverscaledTileID/holdForFade 当成安全操作。测试分别计数 Tile 与 TileRenderData,旧 Tile 销毁后仅比较旧地址及读取 atlas 强引用。
  6. 同 atlas 的 texture 替换经原生 tweaker 更新实际绑定,前后证据不同;原地像素更新即使上传后指针不变,仍依赖真实 content epoch。既有 Build 前/后及提交后复核、证书版本/R cache/prepared Fast 保护保留,未改变准入顺序或放宽任何判据。

3. 静态审查与测试源码范围

13 项生命周期源码覆盖:连续 prepare 后刷新/释放旧 renderData、owner/元素错配、atlas 替换整组清理与新纹理绑定、组内单成员失配、atlas 缺失、同 atlas texture 更换及重新 Build/Reuse 状态、未上传原地更新拒绝、owner 身份不进入完整空组结果字节、Tile 与 renderData 寿命分离、Layer 移除及证书失效释放 atlas、同名 Source 替换、tile 退出清理、Text/Icon/SDF/Halo 连续三代保持身份、未知 Symbol 类型拒绝(其中相关情形合并在同一用例)。

测试使用真实 RenderSource/Tile/TileRenderData/RenderTile、SymbolBucket、Drawable/TileLayerGroup 接口和生产辅助方法;GPU Texture/Drawable/Shader 使用 CPU 替身。真实 API 的纯虚函数、构造参数、Mutable→Immutable 转换、SymbolLayer.baseImpl 可见性及派生类 friend 访问继承 protected 成员已静态核对,没有用悬空指针解引用构造普通反例。

覆盖限制:atlas-only fixture 不生成真实 Placement/顶点/GPU 命令;atlasResult 是生产采集的局部证据,不能代表完整渲染证书。整组失配测试走生产移除入口,但重新创建由 fixture 添加 Drawable,仍需 RDC 实际 update/builder/GPU 链验证。tile 退出测试直接调用正常 update 使用的清理入口,没有伪造 Context 或声称执行完整 update。owner 稳定证书测试走完整生产空组采集;非空 Text/Icon/Halo 稳定性只检查绑定输入和身份。原地 image patch 上传后的 epoch、Style/context 清理引用既有 Step119SymbolResults.RealTileGlyphImageStyleContextCallbacksRejectBuiltCertificate 等回调测试源码,不等同真实槽位释放、Source/Style/context 完整运行验收。

4. 已做及未做检查

已做:必要文件阅读、开工 Git/机器/版本及 diff 审查;真实 C++ 接口/生命周期调用静态核对;确认测试文件在 test/CMakeLists.txt 已登记;源码静态计数 SymbolRetainedLifetime 为13项;新增文档版本/路径检查;STEP-110/111 SHA-256 保护检查;开发记录追加与文件范围检查。

已执行四源码路径 scoped git diff --check -- src/mln/gfx/symbol_drawable_data.hpp src/mln/renderer/layers/render_symbol_layer.cpp src/mln/renderer/layers/render_symbol_layer.hpp test/renderer/retained_underlay_cache.test.cpp:exit=0,stdout/stderr 空。最终全工作区及七任务路径空白检查由本轮落盘后再次执行,结果见下方交付记录。空白检查不证明 C++ 编译或测试通过。

未执行:CMake 配置、Native/NDK 编译、-fsyntax-only、链接、GTest、Metal/iOS/GPU运行、Gradle/APK、ADB/安装、Jest、Bazel/codegen、发行、Git写操作及远端读取。没有新编译失败日志、设备日志、截图、APK/SO SHA 或 Build/Reuse/Fast 数据;既有 libuv 环境失败只作为历史限制,未重试也未修改环境。

保护的未跟踪任务书 SHA-256:

  • STEP-110:33037df71b4b30aa7956d59f4b99f77af3085405b24111bc0fe11ec857a93db0。
  • STEP-111:f3a5806f25384bf46055db2edfea0d21bfbc4124184bc813b46e38b8079c1ec4。

5. RDC 准确编译与定向测试命令(Codex 未执行)

独立审查本报告和实际 diff 后,在同一工作区逐条执行;每条保存 command/exit/耗时/完整日志路径,失败立即停止下一步,回传首个真实 error、target 与必要上下文。PATH 找不到 cmake 时可使用既有 /usr/local/opt/cmake/bin/cmake;不得安装升级依赖或关闭宏绕过 libuv/工具链错误。

cd /Users/tei/dev/MapLibreParticleLab
cmake --preset macos-metal
cmake --build build-macos-metal --target mbgl-test-runner --parallel 3
build-macos-metal/mbgl-test-runner --gtest_filter='SymbolRetainedLifetime.*:Step119SymbolResults.*' --gtest_list_tests
build-macos-metal/mbgl-test-runner --gtest_filter='SymbolRetainedLifetime.*:Step119SymbolResults.*:RetainedUnderlayCache.*:RenderOrchestratorError.*:RenderOrchestratorTeardown.*:CrossTileSymbolLayerIndex.*' --gtest_color=no

必须发现本轮13项 SymbolRetainedLifetime 与既有13项 Step119SymbolResults(后者分布两个已登记文件),真实执行数量非零。零匹配、旧 runner、仅语法检查不能写 GTest PASS。Metal 与 Android OpenGL 分别保留编译及运行事实;Android 实际 compile_commands 的类型核对须保持原 -Werror,不加 -Wno-error。本轮未生成额外编译脚本。

Vulkan 至少取得生产修改的兼容性编译证据;已有 macOS Vulkan 环境可用以下真实 preset/target,环境缺失则记未验证/阻塞,不升级环境:

cmake --preset macos-vulkan
cmake --build build-macos-vulkan --target mbgl-core --parallel 3

Vulkan Symbol provenance 既有保守边界保持,编译成功不等于其 Symbol retained 运行通过。可在既有 sanitizer 配置下补查 UAF,不改造构建体系;Host Metal/GTest 也不替代 iOS 真机。

6. 后续 FZ-G1 验收命令与证据(Codex 未执行)

前置门槛:独立审计/Native验证完成、后续 Git 来源锁和 Android/设备操作获得授权。当前 tracked dirty,verifier 的 SOURCE_NOT_LOCKED 会拒绝本工作区;本轮禁止提交,故这是后续验收前置条件,不能改 verifier、伪造 build-info 或用旧 APK。本报告仅交命令,不代执行 Android/ADB,也不代授权 Git。

用户/RDC 按其阶段授权完成一次标准新 APK 构建与安装,固定 FZG1-2、serial=192.168.200.139:5555、OpenGL Debug/x86_64:

cd /Users/tei/dev/MapLibreParticleLab
./tools/particle_android_validation/build_validation_apk.sh
adb -s 192.168.200.139:5555 install -r artifacts/maplibre-particle/android-validation/MapLibreAndroidTestApp-validation-opengl-debug-x86_64.apk
python3 -B tools/particle_android_validation/verify_step113_fast_path.py --serial 192.168.200.139:5555 --preflight-only
python3 -B tools/particle_android_validation/verify_step113_fast_path.py --serial 192.168.200.139:5555 --scenario STATIC_RC_NO_TIDE --seconds 90
python3 -B tools/particle_android_validation/verify_step113_fast_path.py --serial 192.168.200.139:5555 --scenario RCRR_C_NARROW_NO_TIDE --seconds 90
python3 -B tools/particle_android_validation/verify_step113_fast_path.py --serial 192.168.200.139:5555 --scenario STATIC_RC --seconds 90

正向缓存保持开启,每次稳定后须真实 Build>0、Reuse>0、FastDelta>0,完整120帧24Gate mismatch=0;原63层 STATIC_RC 缺 tide 继续作为安全拒绝负对照,验证器可能返回 NO_FAST_OBSERVED/exit2,不能当正向 PASS,也不能添加假 tide。正向静止90秒不是生命周期验收。

RDC/用户还需反复 pan/zoom/rotate、跨 tile/world wrap、Source同名替换、Style清理/重载、glyph/icon/atlas更新、后台恢复/context reset;每次变化保存失效 → Full → 新 Build → 成功 Full Reuse → Fast 的顺序证据,Text/Icon/SDF/Halo 不消失、残影或错位。相同相机的 Full/Fast 图像对照、资源随循环最终释放均须实测。保持正式 Style/Source 和公共契约,不以禁用 Retained、每帧重建或放宽24Gate替代验证。

必须保存:新锁定源码 HEAD/diff、build-info/完整构建日志、本地及安装 APK SHA-256、实际 libmaplibre.so Build ID与匹配符号、测试发现/执行数、Build/Reuse/Full/Fast/Invalidate 区间增量、完整24Gate快照、操作前后截图及视觉判断、资源数量/释放证据和全部未验证项。证据写新目录,不覆盖 ignored 历史证据。

异常先保存 crash 日志/bugreport,不先清日志;由获授权执行者执行:

STEP119_EVIDENCE_DIR=$(mktemp -d /tmp/step119-symbol-lifetime-XXXXXX)
adb -s 192.168.200.139:5555 logcat -b crash -d > "$STEP119_EVIDENCE_DIR/crash-logcat.txt"
adb -s 192.168.200.139:5555 bugreport "$STEP119_EVIDENCE_DIR/bugreport.zip"

7. 风险、证据缺口与停止门槛

  • 缺原始寄存器 tombstone;原 Build ID 485459ba44f806ef0a0c08633c717bfd5d82ad8f 和匹配符号应保留,不能断言 0x8cdc7c 唯一原因是本 UAF 链。
  • 当前模型运行参数没有可独立回读的执行器元数据,RDC报告须补该身份事实;不伪造“已核实”。
  • 真实 C++ 编译、GTest、Metal/OpenGL/Vulkan、底层 Tile 寿命及真实 atlas 槽位/像素变化、异步线程边界、释放/内存开销均未验收。静态检查只能排除部分明显问题。
  • 若必须改变公共契约、重构全部 Tile 所有权、永久禁用缓存、放宽测试/证书或修改发行物才能继续,停止扩大修改并标 BLOCKED。
  • 编译/链接/GTest失败、SIGSEGV、24Gate mismatch、Fast永久归零、资源循环单调增长、Symbol视觉异常均保留现场,不能通过降门槛、旧APK或隐藏失败制造 PASS。

本轮实现交接到此,后续由 RDC 独立验证;失败按 Codex 修复 → RDC 验证闭环返回,不继续后续开发阶段。

8. 最终交付静态检查记录

  • 完整命令 git diff --check:exit=0,stdout/stderr 空。
  • 七个任务文件显式路径的 git diff --check -- ...:exit=0,stdout/stderr 空。新建 Step/报告尚未跟踪,Git diff 不包含其内容;另以轻量 Python 文件检查确认两份 UTF-8 文档状态、末尾换行及无行尾空白,不借此声称 Git 已检查 untracked 内容。
  • git status --short:仅四个接续源码、开发记录、两份新增本任务文档及两份原有保护任务书;没有其它修改。branch/HEAD 与开工一致。
  • 最终 STEP-110/111 SHA-256 与开工值一致;开发记录内容以 HEAD 原文的完整字节为前缀,只有末尾追加。
  • 静态计数:生命周期13项,既有 Step119SymbolResults 为9+4=13项。没有发现/执行 GTest,不能声称实际26项通过。
  • 没有新的编译/测试/设备验收结论;状态维持 IMPLEMENTED / WAITING_RDC_VALIDATION。