I was fuzzing the tmfile (tm2) model loader and hit a large cluster of crashes on malformed .tmfile inputs. They all trace back to one root cause: the tm2 serializer trusts the offsets and counts stored inside the file without checking them against the file size.
The loader resolves almost every table, tensor, string and buffer by doing mem_base + <offset>, or by indexing an offsets array with a count/id read from the file. None of those are bounds-checked, so a crafted file can point any of them outside the mapped buffer. The result is an out-of-bounds read or a wild-pointer segfault during create_graph(..., "tengine", file), before any inference runs.
Confirmed on current tengine-lite HEAD (5ec1c38), x86-64, ASan. A run over the shipped benchmark models produced 266 crashing inputs across about 15 distinct sites in source/serializer/tmfile/tm2_serializer.c, all the same class. A few representative ones:
load_graph_tensors: tm_tensor = (TM2_Tensor*)(mem_base + v_tensors->offsets[i]) then tm_tensor->data_type (line ~182) reads a wild pointer when offsets[i] is out of range.
- buffer resolve:
v_buffers->offsets[tm_tensor->buffer_id] (line ~238) indexes the offsets array with buffer_id straight from the file, with no check that buffer_id < v_buffers->v_num.
- tensor name:
strdup_name(mem_base + tm_str->offset_data, tm_str->size) (line ~196) uses both the offset and the length from the file, unchecked.
Minimal example that crashes on load: a tensor whose stored offset points past the end of the file. I have a set of small .tmfile PoCs (one per site, 10-80 KB each) and can attach them or share privately, whichever you prefer.
The fix is to validate every file-derived offset, count and index against the mapped length before dereferencing. I have a patch that adds a small bounds-checked accessor and routes the loads through it, and I'll open a PR against tengine-lite shortly. Happy to adjust it to whatever style you want.
I was fuzzing the tmfile (tm2) model loader and hit a large cluster of crashes on malformed
.tmfileinputs. They all trace back to one root cause: the tm2 serializer trusts the offsets and counts stored inside the file without checking them against the file size.The loader resolves almost every table, tensor, string and buffer by doing
mem_base + <offset>, or by indexing an offsets array with a count/id read from the file. None of those are bounds-checked, so a crafted file can point any of them outside the mapped buffer. The result is an out-of-bounds read or a wild-pointer segfault duringcreate_graph(..., "tengine", file), before any inference runs.Confirmed on current
tengine-liteHEAD (5ec1c38), x86-64, ASan. A run over the shipped benchmark models produced 266 crashing inputs across about 15 distinct sites insource/serializer/tmfile/tm2_serializer.c, all the same class. A few representative ones:load_graph_tensors:tm_tensor = (TM2_Tensor*)(mem_base + v_tensors->offsets[i])thentm_tensor->data_type(line ~182) reads a wild pointer whenoffsets[i]is out of range.v_buffers->offsets[tm_tensor->buffer_id](line ~238) indexes the offsets array withbuffer_idstraight from the file, with no check thatbuffer_id < v_buffers->v_num.strdup_name(mem_base + tm_str->offset_data, tm_str->size)(line ~196) uses both the offset and the length from the file, unchecked.Minimal example that crashes on load: a tensor whose stored offset points past the end of the file. I have a set of small
.tmfilePoCs (one per site, 10-80 KB each) and can attach them or share privately, whichever you prefer.The fix is to validate every file-derived offset, count and index against the mapped length before dereferencing. I have a patch that adds a small bounds-checked accessor and routes the loads through it, and I'll open a PR against
tengine-liteshortly. Happy to adjust it to whatever style you want.