Downloads · 30 days
0
Talson/mfv-circle-onert-ignored-verifier
mfv-circle-onert-ignored-verifier is a machine learning model from Talson. Use it for the machine learning task on the model card, and read the license before you ship it in a product.
Target format (huntr): Circle ($1500 tier, "Model File Formats") Affected project: Samsung/ONE — On-device Neural Engine (onert runtime) Affected loaders: CircleLoader (.circle), TFLiteLoader (.tflite) — both inherit…
Downloads · 30 days
0
Access
Public
Updated Jun 12, 2026
Repo size
—
Likes
0
Public
Click a slice to open those files.
.md10.4 KB · 29%
From the Hugging Face model README
Target format (huntr): Circle ($1500 tier, "Model File Formats")
Affected project: Samsung/ONE — On-device Neural Engine (onert runtime)
Affected loaders: CircleLoader (.circle), TFLiteLoader (.tflite) — both inherit BaseLoader
Class: CWE-125 Out-of-bounds Read / CWE-252 Unchecked Return Value — heap-buffer-overflow READ and SEGV on malicious model file, reachable via the documented public C API.
Status: TOOL-VERIFIED under AddressSanitizer (2 distinct crash variants, clean reproduction from a fresh clone). 2026-06-12.
Samsung/ONE is Samsung's on-device neural engine, used on Samsung phones, Tizen devices, and edge AI deployments. The .circle format is ONE's native model format — every model compiled by ONE's toolchain ships as a .circle file. The canonical load path is:
// Public C API (runtime/onert/api/nnfw/src/APIImpl.cc:78)
nnfw_load_model_from_file(session, "model.circle");
// -> Session::loadModelFile -> loadCircleModel -> CircleLoader::loadFromFile -> loadModel()
Loading an untrusted .circle (or .tflite) model file causes memory corruption before inference even starts — the OOB reads occur during model parsing in loadModel(). A victim who downloads a model from a hub/colleague and loads it is memory-corrupted at parse time.
| # | Variant | Primitive | Trigger | Verified |
|---|---|---|---|---|
| 1 | Inflated tensor shape vector | heap-buffer-overflow READ (ASAN) | shape dims count 2 → 0x40000000 | ✅ ASAN |
| 2 | Inflated subgraphs vector | SEGV READ at unmapped address | subgraphs count 1 → 4096 | ✅ ASAN |
Both variants share one root cause: BaseLoader::loadModel() calls VerifyModelBuffer() but discards the return value, then proceeds to walk the model's flatbuffer data structures. Any corrupted vector length, offset, or table field causes an out-of-bounds read.
BaseLoader.h line 1702–1705 (Samsung/ONE @ de7f4736):
template <typename LoaderDomain> std::unique_ptr<ir::Model> BaseLoader<LoaderDomain>::loadModel()
{
LoaderDomain::VerifyModelBuffer(*_verifier.get()); // line 1704: RETURN VALUE DISCARDED
_domain_model = LoaderDomain::GetModel(_base); // line 1705: proceeds on UNVERIFIED buffer
// ... then walks metadata_list, signature_table, subgraphs, tensors, shapes
// on UNVERIFIED flatbuffer data → OOB reads on any corrupted field
VerifyModelBuffer() is a flatbuffers Verifier call that checks all offsets, vector lengths, and table fields are within bounds. It returns bool — true if the buffer is structurally valid, false if corrupted. The return value is silently discarded. The very next line calls GetModel() on the raw buffer and begins walking the data structures.
After GetModel(), loadModel() reads:
_domain_model->metadata() → metadata_list->size() → metadata_list->Get(i) (line 1713–1724)_domain_model->signature_defs() → signature_table->size() → signature_table->Get(i) (line 1728–1742)_domain_model->subgraphs() → subgraphs->size() → (*subgraphs)[i] (line 1747–1757)tensors(), inputs(), outputs(), operators() (in loadSubgraph)shape() → shape->size() → (*shape)[s] (in loadOperand)All of these are OOB-readable fields if the flatbuffer is corrupted. The verifier exists to catch this — but its result is thrown away.
The _verifier is correctly constructed with the buffer base and size in loadFromFile() (line 235) and loadFromBuffer() (line 250). The call to VerifyModelBuffer() at line 1704 is syntactically present — it looks like the developer intended to validate the buffer. But without checking the return value, it's a no-op. The likely origin is a copy-paste from flatbuffers documentation where the if(!Verify...) was accidentally dropped.
Arm NN's TfLiteParser (same domain — loading TFLite/flatbuffer models) checks the verifier correctly:
// armnn/src/armnnTfLiteParser/TfLiteParser.cpp:5524-5532
flatbuffers::Verifier verifier(binaryContent, len);
if (verifier.VerifyBuffer<tflite::Model>() == false)
{
throw ParseException("Buffer doesn't conform to the expected Tensorflow Lite "
"flatbuffers format. ...");
}
return tflite::UnPackModel(binaryContent); // only reached on verified buffer
A valid .circle model with tensor shape [1, 4] (2 elements). The shape vector's length field is corrupted from 2 to 0x40000000 (1G elements). The verifier rejects it (false), but loadModel() proceeds and reads 4GB past the 192-byte buffer.
==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x...0200 at pc 0x... thread T0
READ of size 4 at 0x...0200
#0 flatbuffers::IndirectHelper<int>::Read ONE/onert-micro/externals/flatbuffers/buffer.h:99
#1 flatbuffers::Vector<int, unsigned int>::Get ONE/onert-micro/externals/flatbuffers/vector.h:177
#2 flatbuffers::Vector<int, unsigned int>::operator[] vector.h:180
#3 load_like_onert harness.cpp:84
0x...0200 is located 0 bytes after 192-byte region [0x...0140,0x...0200)
(full log: findings/circle_evidence/asan_oob_read.txt)
The subgraphs vector count is corrupted from 1 to 4096. The verifier rejects it. loadModel() reads subgraphs->size() = 4096 and iterates, dereferencing (*subgraphs)[1] which produces a wild pointer into unmapped memory.
==ERROR: AddressSanitizer: SEGV on unknown address 0x7c47655e008d
The signal is caused by a READ memory access.
#0 flatbuffers::ReadScalar<unsigned short> ONE/onert-micro/externals/flatbuffers/base.h:427
#1 flatbuffers::Table::GetOptionalFieldOffset table.h:39
#2-3 flatbuffers::Table::GetPointer table.h:52,59
#4 circle::SubGraph::tensors() gen/circle_schema_generated.h:13097
#5 main harness2.cpp:67
(full log: findings/circle_evidence/asan_subgraphs_oob.txt)
de7f4736 (HEAD as of 2026-06-12): AFFECTED — BaseLoader.h:1704 discards the verifier return value. Both CircleLoader and TFLiteLoader inherit the vulnerable loadModel().git log search shows no prior fix attempt.TfLiteParser and Deserializer properly check the verifier return and throw on failure..circle file with arbitrary vector lengths/offsets; the loader reads past the buffer by up to the inflated count × element size. This yields:
make_unique<ir::Model> sizing and allocation, so OOB-read data influences control flow..circle or .tflite models through ONE's public API (nnfw_load_model_from_file, nnfw_load_model_from_modelfile, loadCircleModel, loadTFLiteModel). This includes Samsung's on-device AI framework on phones/Tizen.Verifier is the intended mitigation — it exists in the code but is defeated by the unchecked return.poc/circle/gen_and_run.sh clones Samsung/ONE, generates the Circle schema header, builds the harness with ASAN, generates a corrupted .circle file, and triggers the OOB read:
poc/circle/gen_and_run.sh /tmp/circle_mfv_poc
# -> heap-buffer-overflow READ (inflated tensor shape vector)
The C++ harness (poc/mfv_circle-armnn.cpp) replicates the exact code path from BaseLoader::loadModel():
.circle model via flatbuffers APIVerifyModelBuffer() (returns false — correctly rejects)GetModel() + walks subgraphs/tensors/shapes (exactly as onert does)Build flags: g++ -std=c++17 -fsanitize=address -g -O0 -I gen -I ONE/onert-micro/externals
Check the VerifyModelBuffer() return value and abort loading on failure:
// BaseLoader.h:1704 — FIX
if (!LoaderDomain::VerifyModelBuffer(*_verifier.get()))
{
throw std::runtime_error("Model buffer verification failed — rejecting malformed model");
}
_domain_model = LoaderDomain::GetModel(_base);
This is a one-line fix that enables the protection the developer clearly intended. The verifier call is already there — only the return check is missing.
VerifyModelBuffer() return) is distinct from all known Samsung ONE vulnerabilities.AV:L/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:H → 6.1 (local file, user loads malicious model; OOB read → crash + potential info leak; no integrity impact demonstrated).