Views
No views yet
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..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:1// Public C API (runtime/onert/api/nnfw/src/APIImpl.cc:78)
2nnfw_load_model_from_file(session, "model.circle");
3// -> Session::loadModelFile -> loadCircleModel -> CircleLoader::loadFromFile -> loadModel().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 |
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):1template <typename LoaderDomain> std::unique_ptr<ir::Model> BaseLoader<LoaderDomain>::loadModel()
2{
3 LoaderDomain::VerifyModelBuffer(*_verifier.get()); // line 1704: RETURN VALUE DISCARDED
4 _domain_model = LoaderDomain::GetModel(_base); // line 1705: proceeds on UNVERIFIED buffer
5 // ... then walks metadata_list, signature_table, subgraphs, tensors, shapes
6 // on UNVERIFIED flatbuffer data → OOB reads on any corrupted fieldVerifyModelBuffer() 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.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)_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.1// armnn/src/armnnTfLiteParser/TfLiteParser.cpp:5524-5532
2flatbuffers::Verifier verifier(binaryContent, len);
3if (verifier.VerifyBuffer<tflite::Model>() == false)
4{
5 throw ParseException("Buffer doesn't conform to the expected Tensorflow Lite "
6 "flatbuffers format. ...");
7}
8return tflite::UnPackModel(binaryContent); // only reached on verified buffer.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)findings/circle_evidence/asan_oob_read.txt)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:67findings/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:1poc/circle/gen_and_run.sh /tmp/circle_mfv_poc
2# -> heap-buffer-overflow READ (inflated tensor shape vector)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)g++ -std=c++17 -fsanitize=address -g -O0 -I gen -I ONE/onert-micro/externalsVerifyModelBuffer() return value and abort loading on failure:1// BaseLoader.h:1704 — FIX
2if (!LoaderDomain::VerifyModelBuffer(*_verifier.get()))
3{
4 throw std::runtime_error("Model buffer verification failed — rejecting malformed model");
5}
6_domain_model = LoaderDomain::GetModel(_base);VerifyModelBuffer() return) is distinct from all known Samsung ONE vulnerabilities.