| # Circle External Buffer Offset/Size OOB Read Candidate |
|
|
| Date: 2026-06-26 |
|
|
| Target: Huntr model format `circle` ($1,500 lane) |
|
|
| Source snapshot: |
|
|
| - Repo: `research/triage/audits/circle-2026-06-26/ONE` |
| - Commit: `de7f4736dc4c4f5e47f72a4022a9aa9ac6d1ad1a` |
| - Last commit: `de7f4736 [exo] Fix exception throw for unknown dialect (#16483)` |
|
|
| ## Verdict |
|
|
| Submission candidate. |
|
|
| I found a verifier-clean Circle file that causes an ASan heap-buffer-overflow in Samsung ONE's `circledump` path. The bug is an external-buffer bounds check gap: `mio_circle::Reader::buffer_info()` checks only `offset` with `std::vector::at()`, while attacker-controlled `size` is returned to callers without validating `offset + size <= raw_file_size`. |
|
|
| The strongest demonstrated sink is now the real `circledump::dump_buffer()` code path, compiled directly with the current generated Circle schema and ASan. A production runtime loader impact would still strengthen severity, but this is no longer just a Reader harness. |
|
|
| ## Bug Shape |
|
|
| Circle's schema permits buffers whose tensor data lives outside the FlatBuffer region: |
|
|
| - [circle_schema.fbs](/home/bradd/.openclaw/workspace/research/triage/audits/circle-2026-06-26/ONE/runtime/libs/circle-schema/circle_schema.fbs:1730) |
| - Fields: `offset: ulong`, `size: ulong` |
| - Comment: offset is relative to the beginning of the file and valid if greater than `1`. |
|
|
| The vulnerable reader path: |
|
|
| - [Reader.cpp](/home/bradd/.openclaw/workspace/research/triage/audits/circle-2026-06-26/ONE/compiler/mio-circle/src/Reader.cpp:101) |
| - `buffer_info()` sets `buff_data = &_rawdata->at(buffer_offset)` at line 125. |
| - It returns `buffer->size()` at line 127. |
| - It does not validate that the returned byte span is inside `_rawdata`. |
|
|
| The known caller/sink: |
|
|
| - [Dump.cpp](/home/bradd/.openclaw/workspace/research/triage/audits/circle-2026-06-26/ONE/compiler/circledump/src/Dump.cpp:396) |
| - `circledump` calls `reader.buffer_info(i, &buff_data, ext_offset)` and then `dump_buffer(os, buff_data, size, 16)`. |
| - `dump_buffer()` reads `buffer[i]` for up to 16 bytes. |
|
|
| ## Artifacts |
|
|
| - PoC model: [external_offset_oob.circle](/home/bradd/.openclaw/workspace/research/triage/audits/circle-2026-06-26/poc/external_offset_oob.circle) |
| - SHA256: `20962ae3e01d2489f1ba6766f4d804414e5e5cb490589a23aa986f97abe0abd5` |
| - Size: 232 bytes |
| - External buffer: `offset=231`, `size=4096` |
| - Generator: [make_circle_external_oob.cpp](/home/bradd/.openclaw/workspace/research/triage/audits/circle-2026-06-26/poc/make_circle_external_oob.cpp:1) |
| - Direct upstream Reader probe: [circle_reader_external_oob_probe.cpp](/home/bradd/.openclaw/workspace/research/triage/audits/circle-2026-06-26/poc/circle_reader_external_oob_probe.cpp:1) |
| - ASan stderr: [asan-reader-oob.stderr.txt](/home/bradd/.openclaw/workspace/research/triage/audits/circle-2026-06-26/poc/asan-reader-oob.stderr.txt:1) |
| - Direct `circledump` core driver: [circledump_direct_driver.cpp](/home/bradd/.openclaw/workspace/research/triage/audits/circle-2026-06-26/poc/circledump_direct_driver.cpp:1) |
| - `circledump` ASan stderr: [asan-circledump-direct.stderr.txt](/home/bradd/.openclaw/workspace/research/triage/audits/circle-2026-06-26/poc/asan-circledump-direct.stderr.txt:1) |
| - MFV scan output: `/tmp/mfv-scan-circle-external-oob` |
|
|
| ## Reproduction |
|
|
| Use FlatBuffers 23.5.26 headers because the generated Circle header asserts that version. I used `/tmp/flatbuffers-23.5.26/include`. |
|
|
| ```bash |
| cd /home/bradd/.openclaw/workspace |
| |
| clang++ -std=c++17 -O1 -g -fsanitize=address,undefined -fno-omit-frame-pointer \ |
| -I/tmp/flatbuffers-23.5.26/include \ |
| -Iresearch/triage/audits/circle-2026-06-26/ONE/onert-micro/externals/gen/circle-generated \ |
| research/triage/audits/circle-2026-06-26/poc/make_circle_external_oob.cpp \ |
| -o /tmp/make_circle_external_oob |
| |
| /tmp/make_circle_external_oob \ |
| research/triage/audits/circle-2026-06-26/poc/external_offset_oob.circle |
| |
| rm -rf /tmp/circle-include |
| mkdir -p /tmp/circle-include/mio |
| ln -s /home/bradd/.openclaw/workspace/research/triage/audits/circle-2026-06-26/ONE/onert-micro/externals/gen/circle-generated/circle \ |
| /tmp/circle-include/mio/circle |
| |
| clang++ -std=c++17 -O1 -g -fsanitize=address,undefined -fno-omit-frame-pointer \ |
| -I/tmp/flatbuffers-23.5.26/include \ |
| -I/tmp/circle-include \ |
| -Iresearch/triage/audits/circle-2026-06-26/ONE/compiler/mio-circle/include \ |
| research/triage/audits/circle-2026-06-26/poc/circle_reader_external_oob_probe.cpp \ |
| research/triage/audits/circle-2026-06-26/ONE/compiler/mio-circle/src/Reader.cpp \ |
| research/triage/audits/circle-2026-06-26/ONE/compiler/mio-circle/src/Helper.cpp \ |
| -o /tmp/circle_reader_external_oob_probe |
| |
| ASAN_OPTIONS=abort_on_error=0:symbolize=1 \ |
| /tmp/circle_reader_external_oob_probe \ |
| research/triage/audits/circle-2026-06-26/poc/external_offset_oob.circle |
| ``` |
|
|
| Observed: |
|
|
| ```text |
| ERROR: AddressSanitizer: heap-buffer-overflow |
| READ of size 1 |
| circle_reader_external_oob_probe.cpp:36 |
| 0 bytes after 232-byte region |
| ``` |
|
|
| Stronger `circledump` core reproduction: |
|
|
| ```bash |
| cd /home/bradd/.openclaw/workspace |
| |
| # Build FlatBuffers 23.5.26 flatc/libflatbuffers, then regenerate the current Circle schema. |
| cmake -S /tmp/flatbuffers-23.5.26 -B /tmp/flatbuffers-23.5.26-build \ |
| -G Ninja -DFLATBUFFERS_BUILD_TESTS=OFF -DFLATBUFFERS_INSTALL=OFF -DCMAKE_BUILD_TYPE=Release |
| cmake --build /tmp/flatbuffers-23.5.26-build --target flatc flatbuffers -j2 |
| |
| rm -rf /tmp/circle-generated-current /tmp/circle-include-current |
| mkdir -p /tmp/circle-generated-current /tmp/circle-include-current/mio/circle /tmp/circle-generated-current/circle |
| /tmp/flatbuffers-23.5.26-build/flatc --cpp -o /tmp/circle-generated-current \ |
| research/triage/audits/circle-2026-06-26/ONE/runtime/libs/circle-schema/circle_schema.fbs |
| ln -sf /tmp/circle-generated-current/circle_schema_generated.h \ |
| /tmp/circle-include-current/mio/circle/schema_generated.h |
| ln -sf /tmp/circle-generated-current/circle_schema_generated.h \ |
| /tmp/circle-generated-current/circle/schema_generated.h |
| |
| clang++ -std=c++17 -O1 -g -fsanitize=address,undefined -fno-omit-frame-pointer \ |
| -I/tmp/flatbuffers-23.5.26/include \ |
| -I/tmp/circle-include-current \ |
| -Iresearch/triage/audits/circle-2026-06-26/ONE/compiler/mio-circle/include \ |
| -Iresearch/triage/audits/circle-2026-06-26/ONE/compiler/circledump/include \ |
| -Iresearch/triage/audits/circle-2026-06-26/ONE/compiler/circledump/src \ |
| research/triage/audits/circle-2026-06-26/poc/circledump_direct_driver.cpp \ |
| research/triage/audits/circle-2026-06-26/ONE/compiler/circledump/src/Dump.cpp \ |
| research/triage/audits/circle-2026-06-26/ONE/compiler/circledump/src/MetadataPrinter.cpp \ |
| research/triage/audits/circle-2026-06-26/ONE/compiler/circledump/src/OpPrinter.cpp \ |
| research/triage/audits/circle-2026-06-26/ONE/compiler/mio-circle/src/Reader.cpp \ |
| research/triage/audits/circle-2026-06-26/ONE/compiler/mio-circle/src/Helper.cpp \ |
| /tmp/flatbuffers-23.5.26-build/libflatbuffers.a \ |
| -o /tmp/circledump_direct_driver_current |
| |
| ASAN_OPTIONS=abort_on_error=0:symbolize=1 \ |
| /tmp/circledump_direct_driver_current \ |
| research/triage/audits/circle-2026-06-26/poc/external_offset_oob.circle |
| ``` |
|
|
| Observed: |
|
|
| ```text |
| ERROR: AddressSanitizer: heap-buffer-overflow |
| READ of size 1 |
| circledump::dump_buffer(...) Dump.cpp:48 |
| circledump::dump_model(...) Dump.cpp:404 |
| operator<<(...) Dump.cpp:479 |
| circledump_direct_driver.cpp:41 |
| 0 bytes after 232-byte region |
| ``` |
|
|
| ## Scanner Results |
|
|
| `mfv-scan` completed successfully: |
|
|
| ```bash |
| tools/model-format-workbench/mfv-scan \ |
| research/triage/audits/circle-2026-06-26/poc/external_offset_oob.circle \ |
| --out /tmp/mfv-scan-circle-external-oob \ |
| --timeout 180 |
| ``` |
|
|
| Relevant results: |
|
|
| - ModelAudit: no security issues detected. |
| - ModelScan: no issues; scanned zero model files because Circle is unsupported. |
| - PickleScan: not pickle; no dangerous globals. |
| - Magika/file: generic unknown/data detection. |
|
|
| This is useful report framing: generic model scanners miss the malformed Circle external-buffer span, while the format-aware reader accepts the file and returns an unsafe pointer/size pair. |
|
|
| ## Duplicate Sweep |
|
|
| Local: |
|
|
| - No prior local Circle submission found in `research/triage`. |
| - Existing local offset/size work is OpenVINO/GGUF/other formats, not Circle. |
|
|
| GitHub: |
|
|
| - `gh search issues '"external buffer" "offset" "circle"' --repo Samsung/ONE --limit 20`: no results. |
| - `gh search issues '"buffer_info" "offset"' --repo Samsung/ONE --limit 20`: no results. |
| - `gh search issues '"mio_circle" "buffer_info"' --repo Samsung/ONE --limit 20`: no results. |
| - `gh search issues '"Circle" "heap-buffer-overflow"' --repo Samsung/ONE --limit 20`: no results. |
|
|
| Web: |
|
|
| - Searches for exact `mio_circle::Reader` / `buffer_info` / `Circle external buffer offset size` did not surface a matching public report. |
| - Search noise included unrelated `pybind11 buffer_info` and Samsung non-ONE vulnerabilities. |
|
|
| Duplicate risk: low for exact bug class in Circle external buffers. |
|
|
| ## Next Steps |
|
|
| 1. Search ONE runtime paths for production consumers of external-buffer `offset`/`size`, especially tensor constant loaders. |
| 2. If only developer tooling is affected, submit carefully as malformed model file causing heap OOB read in shipped Circle inspection tooling. |
| 3. If a runtime loader consumes the same unchecked span, upgrade the report impact. |
|
|