尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Blender 损坏 .blend 文件测试集解析:invalid_blendfiles 的构造方法与防御机制

Blender 损坏 .blend 文件测试集解析:invalid_blendfiles 的构造方法与防御机制 图形学3D渲染桌面应用音视频【免费下载链接】blenderOfficial mirror of Blender项目地址https://gitcode.com/gh_mirrors/bl/blender点击查看免费下载导读tests/files/invalid_blendfiles/是 Blender 仓库中专门存放已知损坏 .blend 文件的目录。本文以目录内的 README.md 为骨架逐文件剖析六个损坏样本的成因、构造手法与底层原理并结合 blender 读取器源码、SDNA 解析源码 与 回归测试脚本 验证每类损坏的检测路径。读完本文你将理解 .blend 文件块File Block与 SDNA 结构描述在加载时的校验机制掌握如何为 Blender 构造坏文件作为模糊测试或健壮性回归素材。一、目录定位为什么仓库要故意收藏坏文件.blend是 Blender 的原生二进制格式加载器需要同时解析文件头、文件块链表BHead与内嵌的 SDNA 结构描述。任何一环被篡改都可能让重建逻辑越界读写轻则崩溃重则构成任意内存破坏。因此 Blender 专门在测试仓库中保留一批已知无效、且曾经触发过真实缺陷的文件作为回归测试的靶子。README 开篇即说明了该目录的定位This folder contains blendfiles that are known to be invalid (either from unknown reasons, or on purpose). This file gathers as much information as possible about each of them.即目录内文件或因未知原因损坏、或被人为故意改坏README 则尽可能完整地记录每一个文件的来龙去脉。六个样本可分为两类类别文件触发缺陷篡改 ID 文件码invalid_ca_la_id_code.blend未知原因写入异常 IDcode损坏 SDNA/文件块invalid_sdna_struct_size.blend重建越过块末尾堆缓冲区溢出invalid_block_count.blend重建越过块末尾堆缓冲区溢出invalid_block_struct_index.blend越界读取 SDNA 结构数组崩溃invalid_global_block.blend空指针解引用崩溃invalid_window_workspace_hook.blend释放无效文件时解引用部分读取的窗口崩溃其中后五个样本的预期拒绝行为被固定在 tests/python/bl_blendfile_versioning.py 中持续回归验证。二、invalid_ca_la_id_code.blend篡改写入侧 IDcode 的构造样本该文件是从默认启动场景factory startup生成的方法是在写出阶段改写Camera与Lamp两个数据块data-block的 ID 文件码IDcode。README 给出了完整补丁作用于当时的 writefile.cc--- a/source/blender/blenloader/intern/writefile.cc b/source/blender/blenloader/intern/writefile.cc -1894,7 1894,14 void BLO_write_struct_list_by_name(BlendWriter *writer, const char *struct_name, void blo_write_id_struct(BlendWriter *writer, int struct_id, const void *id_address, const ID *id) { - writestruct_at_address_nr(writer-wd, GS(id-name), struct_id, 1, id_address, id); int filecode GS(id-name); if (filecode ID_CA) { filecode MAKE_ID2(!, ?); } else if (filecode ID_LA) { filecode BLEND_MAKE_ID(!, ?, L, A); } writestruct_at_address_nr(writer-wd, filecode, struct_id, 1, id_address, id); }关键点在于补丁只替换了文件中的 IDcode而struct_id指向 SDNA 结构的索引保持不变——即文件内容与 SDNA 描述错位。构造中用到的两个宏语义不同MAKE_ID2(!, ?)生成一个 2 字符的 ID 文件码用于 Camera原本ID_CABLEND_MAKE_ID(!, ?, L, A)生成 4 字符的 ID 文件码用于 Lamp原本ID_LA。这种文件码与结构类型不匹配的样本正是为了验证读取器遇到 IDcode 与 SDNA 结构不一致时能否安全拒绝而不是将错就错地按错误类型重建数据块。从源码结构看它也是六个样本中唯一由写入侧构造的其余样本全部直接改动文件字节。三、Corrupt SDNA五个文件块级损坏样本README 指出后五个样本的共性构造手法Factory-startup saves with a few bytes altered to corrupt the file header (DNA1) or a file-block header. Each crashed the reader before its fix; the expected rejection is checked intests/python/bl_blendfile_versioning.py.即以出厂启动场景保存的文件为基础仅篡改少量字节分别损坏文件头中的DNA1段或某个文件块头file-block header。修复前每个样本都能让读取器崩溃修复后读取器必须稳定拒绝且拒绝行为由测试脚本锁定。3.1invalid_sdna_struct_size.blendSDNA 结构尺寸自相矛盾该样本将某个内嵌结构的TLEN结构总长度字段增大使其不再等于各成员尺寸之和。README 记录的缺陷后果是重建过程越过块末尾触发堆缓冲区溢出heap-buffer-overflow。读取器侧对这类损坏的拦截位于 dna_genfile.cc 的sdna_struct_size_check()static bool sdna_struct_size_check(const SDNA *sdna, const SDNA_Struct *struct_info, const char **r_error_message) { int64_t members_size 0; for (const SDNA_StructMember member : Span(struct_info-members, struct_info-members_num)) { const StringRef member_name sdna-members[member.member_index]; const int64_t array_num sdna-members_array_num[member.member_index]; const int64_t member_size ispointer(member_name) ? sdna-pointer_size : sdna-types_size[member.type_index]; members_size member_size * array_num; } if (members_size ! sdna-types_size[struct_info-type_index]) [[unlikely]] { *r_error_message Invalid struct size in SDNA file; return false; } return true; }该函数逐个成员累加尺寸指针按pointer_size计、数组按array_num缩放若与声明的结构总尺寸不一致即返回错误信息Invalid struct size in SDNA file。函数注释点明了设计依据Blender 自身的 DNA 永远满足尺寸成员之和makesdna使用显式_pad成员而非隐式填充因此一旦不匹配必然意味着尺寸被篡改——而尺寸被篡改会直接导致后续结构重建以错误步长越过文件数据末尾。测试脚本 bl_blendfile_versioning.py 对该样本的预期是# invalid_blendfiles/invalid_sdna_struct_size.blend # A structs DNA size no longer matches the sum of its members. invalid_sdna_struct_size.blend: ( (OSError, RuntimeError), Invalid struct size in SDNA file ),3.2invalid_block_count.blend文件块元素计数越界该样本把某个文件块的元素计数nr改为0x7fffffff。文件块头中的nr表示该块包含多少个结构实例重建逻辑会以结构尺寸 × nr为步长遍历块内数据于是这个天文数字直接让遍历越过块末尾同样触发堆缓冲区溢出。读取器在 readfile.cc 的read_struct()中做了防御/* The block must be large enough to hold the number of structs it claims, otherwise * reconstruction (which strides the block by the file struct size) reads past its end. * Written as a division to avoid overflow in nr * struct_size. */ const int64_t old_struct_size DNA_struct_size(fd-filesdna.get(), bh-SDNAnr); if (bh-nr 0 || (old_struct_size ! 0 bh-nr bh-len / old_struct_size)) [[unlikely]] { fd-flags ~FD_FLAGS_FILE_OK; if (fd-bmain) { blo_readfile_invalidate(fd, fd-bmain, Corrupt .blend file, invalid block count); } return nullptr; }注意实现细节校验用除法bh-len / old_struct_size而非乘法比较正是注释中强调的避免nr * struct_size溢出同时nr 0的负数情况也被拦截。违反时通过blo_readfile_invalidate()使文件失效错误信息为Corrupt .blend file, invalid block count测试脚本断言完全一致。3.3invalid_block_struct_index.blendSDNA 结构索引越界该样本把文件块的 SDNA 结构索引SDNAnr改为0x40000000远超文件内 SDNA 结构数组的长度修复前读取器会越界读取 SDNA 结构数组并崩溃。read_struct()中对应的防护在索引使用之前就位/* A corrupt file could reference a struct index outside the files SDNA, used below to index * the compare-flags, reconstruction alignment tables. */ if (bh-SDNAnr 0 || bh-SDNAnr fd-filesdna-structs.size()) [[unlikely]] { fd-flags ~FD_FLAGS_FILE_OK; if (fd-bmain) { blo_readfile_invalidate(fd, fd-bmain, Corrupt .blend file, invalid block struct index); } return nullptr; }该索引随后会被用于访问 compare-flags、重建与对齐三张表因此必须在任何索引操作前完成边界检查。测试脚本期望错误信息Corrupt .blend file, invalid block struct index。3.4invalid_global_block.blend全局块GLOB上的同类损坏该样本与invalid_block_struct_index.blend同构但损坏点落在必需的全局GLOB块上全局块同样引用了越界的 SDNA 结构索引。其独特之处在于缺陷形态不同——修复前失败的读取返回null后被直接解引用属于空指针解引用crash。测试脚本对它的期望错误信息为is corrupt, unable to read对应 readfile.cc 中BKE_reportf(reports, RPT_ERROR, Blend file %s is corrupt, unable to read, fd-relabase);全局块GLOB是文件加载流程中最早被强制读取的块之一其内容决定文件基本参数的解析因此它损坏时连常规的块结构索引错误路径都不再适用直接以文件损坏、无法读取整体拒绝。3.5invalid_window_workspace_hook.blend窗口子块上的同类损坏最后一个样本把同样的越界SDNAnr放到窗口Window的WorkSpaceInstanceHook子块上。README 记录的缺陷是在释放free被判定无效的文件时解引用了一个只被部分读取的窗口对象导致崩溃。这说明了损坏样本的另一类价值——不仅读取路径要健壮错误处理与资源清理路径invalidate 之后的 free 流程同样需要防护。测试脚本对它期望的错误信息同样是Corrupt .blend file, invalid block struct index与 3.3 样本共用同一条检测分支但覆盖的是完全不同的执行路径窗口子块 vs 普通数据块。四、回归测试如何锁定这些预期拒绝五个 Corrupt SDNA 样本的期望行为全部登记在 bl_blendfile_versioning.py 的invalid_paths字典中该字典同时收录了其他目录下的损坏文件如 Big-Endian 文件与各类已知坏文件。其核心机制在invalid_path_exception_process()def invalid_path_exception_process(self, bfp, exception): expected_failure self.invalid_paths.get(os.path.basename(bfp), None) if not expected_failure: raise exception # Check expected exception type(s). if not isinstance(exception, expected_failure[0]): raise exception # Check expected exception (partial) message. if expected_failure[1] not in str(exception): raise exception print(f\tExpected failure: {exception}, flushTrue)它同时校验两点异常类型必须是元组(OSError, RuntimeError)之一读取器以报告/异常形式拒绝异常消息部分匹配必须包含字典中登记的预期错误信息子串如Invalid struct size in SDNA file、Corrupt .blend file, invalid block count等。两条任一不满足都会让测试失败从而确保文件必须被拒绝、且必须以预期的具体理由被拒绝——既防止回归到能打开坏文件的错误行为也防止拒绝理由漂移掩盖实现变更。测试执行方式见脚本头部注释# ./blender.bin --background --python tests/python/bl_blendfile_versioning.py ..该测试会递归扫描tests/files/下所有.blend对每个文件执行打开open、链接link、追加append、另存重载save reload与全局撤销global undo等操作对登记在invalid_paths中的文件上述任一操作抛出预期异常即视为通过。脚本还通过--slice-range/--slice-index支持并行分片处理见generate_slice_indices()以适应测试仓库中数千个.blend文件的规模。此外脚本头部还留有一条来自开发者的警告部分文件在 Linux/macOS 上会看似随机地失败添加新文件需谨慎这从侧面说明坏文件测试天然存在平台敏感性。五、构造自己的损坏样本方法与边界综合 README 与源码构造同类样本的通用流程是基础文件以出厂启动场景保存一个干净的.blend对应 README 所述 Factory-startup saves定位目标用十六进制编辑器定位DNA1段文件头中描述 SDNA 的区块或目标文件块BHead头定向篡改改TLENSDNA 结构长度→ 复现invalid_sdna_struct_size改文件块头的nr元素计数→ 复现invalid_block_count改文件块头的SDNAnrSDNA 结构索引→ 复现invalid_block_struct_index改在GLOB块上即复现invalid_global_block改在窗口的WorkSpaceInstanceHook子块上即复现invalid_window_workspace_hook验证拒绝在bl_blendfile_versioning.py中登记文件名与预期错误信息后运行测试或在命令行手动打开确认报错信息。需要说明的边界README 对invalid_ca_la_id_code.blend仅注明unknown reasons, or on purpose未在测试脚本中登记预期拒绝信息说明该文件当前的失败原因尚未完全明确脚本顶部警告部分文件看似随机失败或与此类不确定性相关其余五个样本的成因、缺陷形态与检测路径则均已通过源码与测试双重确认。六、小结一份坏文件档案的工程价值tests/files/invalid_blendfiles/及其 README 展示了 Blender 在文件格式健壮性上的两个工程实践以真实缺陷为素材每个坏文件都对应一个曾经真实发生的崩溃堆缓冲区溢出、越界读取、空指针解引用、释放路径解引用比随机模糊数据更能精准覆盖回归点以文档测试双重锁定README 记录怎么改坏的、会触发什么缺陷bl_blendfile_versioning.py 记录必须报什么错而 readfile.cc 与 dna_genfile.cc 则提供拦截这些损坏的具体实现——三者共同构成构造 → 修复 → 回归的完整闭环。对于任何需要解析 Blender 文件格式或构建健壮二进制解析器的开发者这份档案既是格式内部结构的反面教材也是验证自身解析器边界检查是否完备的现成测试集。赞分享图形学3D渲染桌面应用音视频【免费下载链接】blenderOfficial mirror of Blender项目地址https://gitcode.com/gh_mirrors/bl/blender点击查看免费下载相关推荐IOPaint Windows 快速安装教程4 个高频卡点逐个击破本地 AI 修图工具装完即用IOPaint Windows 快速安装教程4 个高频卡点逐个击破本地 AI 修图工具装完即用 IOPaint 是一款开源的本地 AI 修图工具擦除图片里人工智能AI 应用计算机视觉图像处理媒体生成后端终极指南如何使用pdf-lib修复损坏的PDF文件终极指南如何使用pdf lib修复损坏的PDF文件 在处理PDF文件时遇到损坏或无法打开的文档是很常见的问题。pdf lib作为一个强大的JavaScrip开发工具mera-mix-4x7B未来路线图AI模型轻量化发展趋势mera mix 4x7B未来路线图AI模型轻量化发展趋势 mera mix 4x7B作为基于Mixtral架构的AI模型正引领着AI模型轻量化的发展趋势。创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表