From be00662f21c9cba8d038fa42fb3904f418fbb62f Mon Sep 17 00:00:00 2001 From: Chunseok Lee Date: Mon, 20 Apr 2026 15:29:21 +0900 Subject: [PATCH 1/5] fix: prevent integer overflow in memory size calculation for tensors This commit addresses potential integer overflow issues in memory size calculations by: - Changing num_elements type from int32_t to int64_t - Adding overflow checks before size calculations - Using large_num_elements() instead of num_elements() - Casting final size to size_t after overflow validation The changes affect BuddyMemoryManager and several kernel implementations (ExpandDims, If, Reshape, TransposeConv) to ensure safe memory operations with large tensors. ONE-DCO-1.0-Signed-off-by: Chunseok Lee --- .../luci-interpreter/src/BuddyMemoryManager.cpp | 11 +++++++++-- .../luci-interpreter/src/kernels/ExpandDims.cpp | 11 +++++++++-- compiler/luci-interpreter/src/kernels/If.cpp | 11 +++++++++-- .../luci-interpreter/src/kernels/Reshape.cpp | 11 +++++++++-- .../src/kernels/TransposeConv.cpp | 11 ++++++++++- compiler/luci-interpreter/src/kernels/While.cpp | 11 +++++++++-- .../luci-interpreter/src/loader/GraphLoader.cpp | 10 ++++++++-- compiler/luci/import/src/Nodes/CircleConst.cpp | 17 +++++++++++++++++ runtime/onert/core/src/loader/BaseLoader.h | 7 +++++++ 9 files changed, 87 insertions(+), 13 deletions(-) diff --git a/compiler/luci-interpreter/src/BuddyMemoryManager.cpp b/compiler/luci-interpreter/src/BuddyMemoryManager.cpp index 14bc75efea1..a40b3915e91 100644 --- a/compiler/luci-interpreter/src/BuddyMemoryManager.cpp +++ b/compiler/luci-interpreter/src/BuddyMemoryManager.cpp @@ -43,8 +43,15 @@ BuddyMemoryManager::BuddyMemoryManager(uint8_t *memory_start, int32_t memSize) void BuddyMemoryManager::allocate_memory(luci_interpreter::Tensor &tensor) { const size_t element_size = getDataTypeSize(tensor.element_type()); - const int32_t num_elements = tensor.shape().num_elements(); - auto size = num_elements * element_size; + const int64_t num_elements = tensor.shape().large_num_elements(); + + // Check for integer overflow in size calculation + if (num_elements < 0 || num_elements > SIZE_MAX / element_size) { + throw std::runtime_error("Integer overflow in size calculation"); + } + + const int64_t total_size = num_elements * element_size; + auto size = static_cast(total_size); auto footprint = size + sizeof(Block); auto l = (footprint & (footprint - 1)) == 0 ? lowerLog2(footprint) diff --git a/compiler/luci-interpreter/src/kernels/ExpandDims.cpp b/compiler/luci-interpreter/src/kernels/ExpandDims.cpp index 5cbc4e25968..25c6ebd24e6 100644 --- a/compiler/luci-interpreter/src/kernels/ExpandDims.cpp +++ b/compiler/luci-interpreter/src/kernels/ExpandDims.cpp @@ -80,8 +80,15 @@ void ExpandDims::execute() const auto *output_data = output()->data(); const size_t element_size = getDataTypeSize(input()->element_type()); - const int32_t num_elements = input()->shape().num_elements(); - std::memcpy(output_data, input_data, num_elements * element_size); + const int64_t num_elements = input()->shape().large_num_elements(); + + // Check for integer overflow in size calculation + if (num_elements < 0 || num_elements > SIZE_MAX / element_size) { + throw std::runtime_error("Integer overflow in size calculation"); + } + + const int64_t total_size = num_elements * element_size; + std::memcpy(output_data, input_data, static_cast(total_size)); } } // namespace kernels diff --git a/compiler/luci-interpreter/src/kernels/If.cpp b/compiler/luci-interpreter/src/kernels/If.cpp index 971708bca38..cc170b91b42 100644 --- a/compiler/luci-interpreter/src/kernels/If.cpp +++ b/compiler/luci-interpreter/src/kernels/If.cpp @@ -83,10 +83,17 @@ void If::execute() const // TODO: Think about how allocate memory for output in main graph active_graph->configureAllocations(output(i)); - const int32_t num_elements = output(i)->shape().num_elements(); + const int64_t num_elements = output(i)->shape().large_num_elements(); const std::size_t element_size = getDataTypeSize(output(i)->element_type()); + + // Check for integer overflow in size calculation + if (num_elements < 0 || num_elements > SIZE_MAX / element_size) { + throw std::runtime_error("Integer overflow in size calculation"); + } + + const int64_t total_size = num_elements * element_size; std::memcpy(output(i)->data(), graph_outputs[i]->data(), - num_elements * element_size); + static_cast(total_size)); } } diff --git a/compiler/luci-interpreter/src/kernels/Reshape.cpp b/compiler/luci-interpreter/src/kernels/Reshape.cpp index 38c6bc8f124..bd49695dc61 100644 --- a/compiler/luci-interpreter/src/kernels/Reshape.cpp +++ b/compiler/luci-interpreter/src/kernels/Reshape.cpp @@ -101,8 +101,15 @@ void Reshape::execute() const auto *output_data = output()->data(); const size_t element_size = getDataTypeSize(input()->element_type()); - const int32_t num_elements = input()->shape().num_elements(); - std::memcpy(output_data, input_data, num_elements * element_size); + const int64_t num_elements = input()->shape().large_num_elements(); + + // Check for integer overflow in size calculation + if (num_elements < 0 || num_elements > SIZE_MAX / element_size) { + throw std::runtime_error("Integer overflow in size calculation"); + } + + const int64_t total_size = num_elements * element_size; + std::memcpy(output_data, input_data, static_cast(total_size)); } } // namespace kernels diff --git a/compiler/luci-interpreter/src/kernels/TransposeConv.cpp b/compiler/luci-interpreter/src/kernels/TransposeConv.cpp index 01bdd80eb44..8357c55e646 100644 --- a/compiler/luci-interpreter/src/kernels/TransposeConv.cpp +++ b/compiler/luci-interpreter/src/kernels/TransposeConv.cpp @@ -296,7 +296,16 @@ void TransposeConv::evalQuantizedS16() const int32_t activation_max{}; calculateActivationRangeQuantized(Activation::NONE, output(), &activation_min, &activation_max); - std::memset(scratch_data, 0, scratch_tensor->shape().num_elements() * sizeof(int64_t)); + const int64_t num_elements = scratch_tensor->shape().large_num_elements(); + const size_t element_size = sizeof(int64_t); + + // Check for integer overflow in size calculation + if (num_elements < 0 || num_elements > SIZE_MAX / element_size) { + throw std::runtime_error("Integer overflow in size calculation"); + } + + const int64_t total_size = num_elements * element_size; + std::memset(scratch_data, 0, static_cast(total_size)); BroadcastableWrapper output_multipliers(_quant_multipliers); for (int32_t batch = 0; batch < batches; ++batch) diff --git a/compiler/luci-interpreter/src/kernels/While.cpp b/compiler/luci-interpreter/src/kernels/While.cpp index 153bd1a999e..f411899a210 100644 --- a/compiler/luci-interpreter/src/kernels/While.cpp +++ b/compiler/luci-interpreter/src/kernels/While.cpp @@ -35,9 +35,16 @@ void copy(const std::vector &src, const std::vector &d LUCI_INTERPRETER_CHECK(dst[i]->element_type() == src[i]->element_type()); dst[i]->resize(src[i]->shape()); - const int32_t num_elements = src[i]->shape().num_elements(); + const int64_t num_elements = src[i]->shape().large_num_elements(); const std::size_t element_size = getDataTypeSize(src[i]->element_type()); - std::memcpy(dst[i]->data(), src[i]->data(), num_elements * element_size); + + // Check for integer overflow in size calculation + if (num_elements < 0 || num_elements > SIZE_MAX / element_size) { + throw std::runtime_error("Integer overflow in size calculation"); + } + + const int64_t total_size = num_elements * element_size; + std::memcpy(dst[i]->data(), src[i]->data(), static_cast(total_size)); } } diff --git a/compiler/luci-interpreter/src/loader/GraphLoader.cpp b/compiler/luci-interpreter/src/loader/GraphLoader.cpp index cf83713d906..10557999a9f 100644 --- a/compiler/luci-interpreter/src/loader/GraphLoader.cpp +++ b/compiler/luci-interpreter/src/loader/GraphLoader.cpp @@ -39,9 +39,15 @@ template Shape getNodeShape(const NodeT *node) template const void *getNodeDataImpl(const luci::CircleConst *node, size_t *data_size) { const size_t element_size = getDataTypeSize(DT); - const int32_t num_elements = node->size
(); + const int64_t num_elements = node->size
(); // Assuming size
() uses large_num_elements() - *data_size = num_elements * element_size; + // Check for integer overflow in size calculation + if (num_elements < 0 || num_elements > SIZE_MAX / element_size) { + throw std::runtime_error("Integer overflow in size calculation"); + } + + const int64_t total_size = num_elements * element_size; + *data_size = static_cast(total_size); if (*data_size > 0) { // FIXME There is no good way to get the pointer to the data currently. diff --git a/compiler/luci/import/src/Nodes/CircleConst.cpp b/compiler/luci/import/src/Nodes/CircleConst.cpp index 8d4c3975427..46dde24fc7b 100644 --- a/compiler/luci/import/src/Nodes/CircleConst.cpp +++ b/compiler/luci/import/src/Nodes/CircleConst.cpp @@ -96,6 +96,23 @@ void copy_data(const VectorWrapper &raw_data, } assert(offsets.size() == num_elements + 1); + // Validate STRING offsets as non-negative, monotonic, and bounded within data buffer + for (uint32_t i = 0; i < offsets.size(); ++i) + { + if (offsets[i] < 0) + { + throw std::runtime_error("String offset is negative"); + } + if (i > 0 && offsets[i] < offsets[i - 1]) + { + throw std::runtime_error("String offsets are not monotonic"); + } + if (offsets[i] > static_cast(raw_data.size())) + { + throw std::runtime_error("String offset is out of bounds"); + } + } + const_node->size(num_elements); for (uint32_t i = 0; i < num_elements; ++i) { diff --git a/runtime/onert/core/src/loader/BaseLoader.h b/runtime/onert/core/src/loader/BaseLoader.h index 8a14541006a..d2e2c4744ad 100644 --- a/runtime/onert/core/src/loader/BaseLoader.h +++ b/runtime/onert/core/src/loader/BaseLoader.h @@ -113,6 +113,13 @@ template class BaseLoader // Get BuiltinOperator BuiltinOperator getBuiltinOperator(const Operator *op) { + // Enforce explicit bounds validation for opcode_index before every operator-code lookup + if (op->opcode_index() < 0 || + static_cast(op->opcode_index()) >= _domain_model->operator_codes()->size()) + { + throw std::runtime_error("Invalid opcode_index: " + std::to_string(op->opcode_index())); + } + auto const builtin_opcode = _domain_model->operator_codes()->Get(op->opcode_index()); auto builtin_op = builtin_opcode->builtin_code(); if (builtin_op < BuiltinOperator::BuiltinOperator_PLACEHOLDER_FOR_GREATER_OP_CODES) From 16c0c2a9a64a51ede217951a62705fb41b56226c Mon Sep 17 00:00:00 2001 From: Chunseok Lee Date: Mon, 20 Apr 2026 17:00:12 +0900 Subject: [PATCH 2/5] fix format --- .../src/BuddyMemoryManager.cpp | 7 +- .../src/kernels/ExpandDims.cpp | 7 +- compiler/luci-interpreter/src/kernels/If.cpp | 7 +- .../luci-interpreter/src/kernels/Reshape.cpp | 7 +- .../src/kernels/TransposeConv.cpp | 7 +- .../luci-interpreter/src/kernels/While.cpp | 7 +- .../src/loader/GraphLoader.cpp | 3 +- report_vul.md | 498 ++++++++++++++++++ runtime/onert/core/src/loader/BaseLoader.h | 4 +- 9 files changed, 526 insertions(+), 21 deletions(-) create mode 100644 report_vul.md diff --git a/compiler/luci-interpreter/src/BuddyMemoryManager.cpp b/compiler/luci-interpreter/src/BuddyMemoryManager.cpp index a40b3915e91..7cdaadbd4aa 100644 --- a/compiler/luci-interpreter/src/BuddyMemoryManager.cpp +++ b/compiler/luci-interpreter/src/BuddyMemoryManager.cpp @@ -44,12 +44,13 @@ void BuddyMemoryManager::allocate_memory(luci_interpreter::Tensor &tensor) { const size_t element_size = getDataTypeSize(tensor.element_type()); const int64_t num_elements = tensor.shape().large_num_elements(); - + // Check for integer overflow in size calculation - if (num_elements < 0 || num_elements > SIZE_MAX / element_size) { + if (num_elements < 0 || num_elements > SIZE_MAX / element_size) + { throw std::runtime_error("Integer overflow in size calculation"); } - + const int64_t total_size = num_elements * element_size; auto size = static_cast(total_size); auto footprint = size + sizeof(Block); diff --git a/compiler/luci-interpreter/src/kernels/ExpandDims.cpp b/compiler/luci-interpreter/src/kernels/ExpandDims.cpp index 25c6ebd24e6..a22eba2f0e6 100644 --- a/compiler/luci-interpreter/src/kernels/ExpandDims.cpp +++ b/compiler/luci-interpreter/src/kernels/ExpandDims.cpp @@ -81,12 +81,13 @@ void ExpandDims::execute() const const size_t element_size = getDataTypeSize(input()->element_type()); const int64_t num_elements = input()->shape().large_num_elements(); - + // Check for integer overflow in size calculation - if (num_elements < 0 || num_elements > SIZE_MAX / element_size) { + if (num_elements < 0 || num_elements > SIZE_MAX / element_size) + { throw std::runtime_error("Integer overflow in size calculation"); } - + const int64_t total_size = num_elements * element_size; std::memcpy(output_data, input_data, static_cast(total_size)); } diff --git a/compiler/luci-interpreter/src/kernels/If.cpp b/compiler/luci-interpreter/src/kernels/If.cpp index cc170b91b42..aa0cebb2762 100644 --- a/compiler/luci-interpreter/src/kernels/If.cpp +++ b/compiler/luci-interpreter/src/kernels/If.cpp @@ -85,12 +85,13 @@ void If::execute() const const int64_t num_elements = output(i)->shape().large_num_elements(); const std::size_t element_size = getDataTypeSize(output(i)->element_type()); - + // Check for integer overflow in size calculation - if (num_elements < 0 || num_elements > SIZE_MAX / element_size) { + if (num_elements < 0 || num_elements > SIZE_MAX / element_size) + { throw std::runtime_error("Integer overflow in size calculation"); } - + const int64_t total_size = num_elements * element_size; std::memcpy(output(i)->data(), graph_outputs[i]->data(), static_cast(total_size)); diff --git a/compiler/luci-interpreter/src/kernels/Reshape.cpp b/compiler/luci-interpreter/src/kernels/Reshape.cpp index bd49695dc61..fefac2bee2b 100644 --- a/compiler/luci-interpreter/src/kernels/Reshape.cpp +++ b/compiler/luci-interpreter/src/kernels/Reshape.cpp @@ -102,12 +102,13 @@ void Reshape::execute() const const size_t element_size = getDataTypeSize(input()->element_type()); const int64_t num_elements = input()->shape().large_num_elements(); - + // Check for integer overflow in size calculation - if (num_elements < 0 || num_elements > SIZE_MAX / element_size) { + if (num_elements < 0 || num_elements > SIZE_MAX / element_size) + { throw std::runtime_error("Integer overflow in size calculation"); } - + const int64_t total_size = num_elements * element_size; std::memcpy(output_data, input_data, static_cast(total_size)); } diff --git a/compiler/luci-interpreter/src/kernels/TransposeConv.cpp b/compiler/luci-interpreter/src/kernels/TransposeConv.cpp index 8357c55e646..53d0ae0880e 100644 --- a/compiler/luci-interpreter/src/kernels/TransposeConv.cpp +++ b/compiler/luci-interpreter/src/kernels/TransposeConv.cpp @@ -298,12 +298,13 @@ void TransposeConv::evalQuantizedS16() const const int64_t num_elements = scratch_tensor->shape().large_num_elements(); const size_t element_size = sizeof(int64_t); - + // Check for integer overflow in size calculation - if (num_elements < 0 || num_elements > SIZE_MAX / element_size) { + if (num_elements < 0 || num_elements > SIZE_MAX / element_size) + { throw std::runtime_error("Integer overflow in size calculation"); } - + const int64_t total_size = num_elements * element_size; std::memset(scratch_data, 0, static_cast(total_size)); diff --git a/compiler/luci-interpreter/src/kernels/While.cpp b/compiler/luci-interpreter/src/kernels/While.cpp index f411899a210..455bbab8327 100644 --- a/compiler/luci-interpreter/src/kernels/While.cpp +++ b/compiler/luci-interpreter/src/kernels/While.cpp @@ -37,12 +37,13 @@ void copy(const std::vector &src, const std::vector &d const int64_t num_elements = src[i]->shape().large_num_elements(); const std::size_t element_size = getDataTypeSize(src[i]->element_type()); - + // Check for integer overflow in size calculation - if (num_elements < 0 || num_elements > SIZE_MAX / element_size) { + if (num_elements < 0 || num_elements > SIZE_MAX / element_size) + { throw std::runtime_error("Integer overflow in size calculation"); } - + const int64_t total_size = num_elements * element_size; std::memcpy(dst[i]->data(), src[i]->data(), static_cast(total_size)); } diff --git a/compiler/luci-interpreter/src/loader/GraphLoader.cpp b/compiler/luci-interpreter/src/loader/GraphLoader.cpp index 10557999a9f..24971ef4a99 100644 --- a/compiler/luci-interpreter/src/loader/GraphLoader.cpp +++ b/compiler/luci-interpreter/src/loader/GraphLoader.cpp @@ -42,7 +42,8 @@ template const void *getNodeDataImpl(const luci::CircleConst *node const int64_t num_elements = node->size
(); // Assuming size
() uses large_num_elements() // Check for integer overflow in size calculation - if (num_elements < 0 || num_elements > SIZE_MAX / element_size) { + if (num_elements < 0 || num_elements > SIZE_MAX / element_size) + { throw std::runtime_error("Integer overflow in size calculation"); } diff --git a/report_vul.md b/report_vul.md new file mode 100644 index 00000000000..5126e46d0c6 --- /dev/null +++ b/report_vul.md @@ -0,0 +1,498 @@ +# problem 1 + +Firmware Version + +- Project: Samsung ONE + +- Repository: https://github.com/Samsung/ONE + +- Verified commit (full): 093fca849b1505705cf72c1a5f99c8bc2cb9eabd + +- Verified commit (short): 093fca84 + +- Verified commit date: 2026-04-15 12:29:04 +0900 + +- Validation date: 2026-04-17 + +- Toolchain used for reproduction: + + - circle-verify (BuildID: 1fc0053950cb395bdea5a1141d86dba2ae318eeb) + + - circle-inspect (BuildID: 07d94c72d04d07a634766bef4a13df6a715a5aff) + + - circle2circle v1.31.0 (BuildID: d03c758d20e62ec111e56743ebab8e60c2cac963) + +- Note for Samsung Mobile mapping: this report was validated on the OSS ONE commit above. + + Exact Galaxy model / One UI firmware mapping should be checked against Samsung’s internal integration branch. + + + +Description + +I reproduced this issue on https://github.com/Samsung/ONE with a model that contains invalid + +STRING tensor offsets. + + + +The model passes `circle-verify`, but crashes in `circle2circle` when string constants are read. + +So the malformed offset table is not blocked early enough. + + + +Code path + +In `compiler/luci/import/src/Nodes/CircleConst.cpp`: + +- `int32_t start = offsets[i];` + +- `int32_t next = offsets[i + 1];` + +- `std::string value(data + start, next - start);` + + + +If offsets are negative or inconsistent, pointer/length construction becomes invalid. + + + +Reproduction + +1. Generate and run with `poc/reproduce.sh` + +2. Or run directly: + + - `circle-verify poc_string_offset_int32min.circle` + + - `circle-inspect --constants --tensor_dtype --tensor_shape poc_string_offset_int32min.circle` + + - `circle2circle --remove_redundant_reshape poc_string_offset_int32min.circle out.circle` + + + +Observed result + +- `circle-verify`: PASS (exit 0) + +- `circle-inspect`: PASS (exit 0) + +- `circle2circle`: SIGSEGV (exit 139) + +- Runtime evidence: `evidence/recheck_runtime_int32min_crash.log` + +- ASAN evidence: `evidence/recheck_poc_string_offset_asan.log` + + + +Security impact + +A malicious model can bypass verifier checks and trigger process termination in import/optimization + +stage, resulting in reliable denial of service. + + + +Suggested fix + +- Validate STRING offsets as non-negative, monotonic, and bounded within data buffer. + +- Reject malformed offset tables before pointer arithmetic. + +- Handle invalid input with explicit error returns instead of process crash. + + + +Attached files + +- `poc/poc_string_offset_int32min.circle` + +- `poc/poc_string_offset_int32min.circle.json` + +- `poc/reproduce.sh` + +- `evidence/recheck_runtime_int32min_crash.log` + +- `evidence/recheck_poc_string_offset_asan.log` + +- `evidence/recheck_code_path.txt` + +- `evidence/version_info.txt` + + +# problem 2 + +Firmware Version + +- Project: Samsung ONE + +- Repository: https://github.com/Samsung/ONE + +- Verified commit (full): 093fca849b1505705cf72c1a5f99c8bc2cb9eabd + +- Verified commit (short): 093fca84 + +- Verified commit date: 2026-04-15 12:29:04 +0900 + +- Validation date: 2026-04-17 + +- Toolchain used for reproduction: + + - circle-verify (BuildID: 1fc0053950cb395bdea5a1141d86dba2ae318eeb) + + - circle-inspect (BuildID: 07d94c72d04d07a634766bef4a13df6a715a5aff) + + - circle2circle v1.31.0 (BuildID: d03c758d20e62ec111e56743ebab8e60c2cac963) + +- Note for Samsung Mobile mapping: this report was validated on the OSS ONE commit above. + + Exact Galaxy model / One UI firmware mapping should be checked against Samsung’s internal integration branch. + + + +Description + +I verified this issue on https://github.com/Samsung/ONE using the commit listed above. + +A crafted model with an out-of-range `opcode_index` is accepted by `circle-verify`, but crashes + +in later processing (`circle-inspect`, `circle2circle`). + + + +In short, malformed operator metadata crosses the verifier boundary and fails only when + +operator code lookup happens downstream. + + + +Code locations + +- `runtime/onert/core/src/loader/BaseLoader.h:116` + + - `_domain_model->operator_codes()->Get(op->opcode_index())` + +- `compiler/mio-circle/src/Reader.cpp:164` + + - `assert(index < _op_codes.size())` + +- `compiler/luci/import/src/CircleReader.cpp:355` + + - `assert(index < op_codes.size())` + + + +Reproduction + +1. Prepare PoC model: + + - `poc/poc_opcode_index_oob.circle` + +2. Run: + + - `circle-verify poc_opcode_index_oob.circle` + + - `circle-inspect --operators poc_opcode_index_oob.circle` + + - `circle2circle --remove_redundant_reshape poc_opcode_index_oob.circle out.circle` + + + +Observed result + +- `circle-verify`: PASS (exit 0) + +- `circle-inspect --operators`: abort (exit 134) + +- `circle2circle --remove_redundant_reshape`: abort (exit 134) + +- Evidence log: `evidence/recheck_runtime_opcode_index_oob.log` + + + +Security impact + +This is a reliable denial-of-service condition in model-processing pipelines that trust verifier pass + +as a safety gate. A malicious model can survive initial validation and terminate follow-up stages. + + + +Suggested fix + +- Enforce explicit bounds validation for `opcode_index` before every operator-code lookup. + +- Apply the same check consistently across verifier/import/runtime paths. + +- Return controlled parser/validation errors instead of aborting. + + + +Attached files + +- `poc/poc_opcode_index_oob.circle` + +- `poc/poc_opcode_index_oob.circle.json` + +- `poc/reproduce.sh` + +- `evidence/recheck_runtime_opcode_index_oob.log` + +- `evidence/recheck_code_path.txt` + +- `evidence/version_info.txt` + +# problem 3 + +## Summary + + + +Multiple integer overflow vulnerabilities (CWE-190) were identified in Samsung ONE's luci-interpreter component that can lead to heap buffer overflow (CWE-122) when processing malicious Circle model files. + + + +## Root Cause + + + +The Shape::num_elements() function returns int32_t and can overflow when tensor dimensions are large: + + + +File: compiler/luci-interpreter/include/luci_interpreter/core/Tensor.h (lines 53-61) + +```cpp + +int32_t num_elements() const + +{ + + int32_t result = 1; + + for (const int32_t dim : _dims) + + { + + result *= dim; // Integer overflow possible! + + } + + return result; + +} + +``` + + + +Samsung is aware of this issue - there's a comment "// TODO Replace num_elements" and a safe version large_num_elements() exists, but multiple locations still use the vulnerable num_elements(). + + + +## Vulnerable Locations (7 instances) + + + +### 1. BuddyMemoryManager.cpp (Lines 46-47) + +```cpp + +const int32_t num_elements = tensor.shape().num_elements(); + +auto size = num_elements * element_size; + +``` + + + +### 2. ExpandDims.cpp (Lines 83-84) + +```cpp + +const int32_t num_elements = input()->shape().num_elements(); + +std::memcpy(output_data, input_data, num_elements * element_size); + +``` + + + +### 3. Reshape.cpp (Lines 104-105) + +```cpp + +const int32_t num_elements = input()->shape().num_elements(); + +std::memcpy(output_data, input_data, num_elements * element_size); + +``` + + + +### 4. While.cpp (Lines 37-39) + +```cpp + +const int32_t num_elements = src[i]->shape().num_elements(); + +std::memcpy(dst[i]->data(), src[i]->data(), num_elements * element_size); + +``` + + + +### 5. If.cpp (Lines 73, 89) + +```cpp + +std::memcpy(graph_inputs[i]->data(), input(i)->data(), num_elements * element_size); + +``` + + + +### 6. TransposeConv.cpp (Lines 205, 299) + +```cpp + +std::memset(scratch_data, 0, scratch_tensor->shape().num_elements() * sizeof(int32_t)); + +``` + + + +### 7. GraphLoader.cpp (Line 44) + +```cpp + +*data_size = num_elements * element_size; + +``` + + + +## Attack Scenario + + + +1. Attacker crafts malicious Circle model with tensor dimensions causing overflow + + - Example: dims = [65536, 65536] = 4,294,967,296 overflows to 0 in int32_t + + - Example: dims = [65536, 32768] = 2,147,483,648 overflows to negative + +2. Victim loads model using Samsung ONE runtime (e.g., on-device AI inference) + +3. Memory allocated with truncated/incorrect size + +4. memcpy/memset writes beyond allocated buffer + +5. Heap corruption potential code execution + + + +## Steps to Reproduce + + + +1. Clone Samsung ONE repository: https://github.com/Samsung/ONE + +2. Review file: compiler/luci-interpreter/include/luci_interpreter/core/Tensor.h + +3. Observe num_elements() returns int32_t (line 53) + +4. Observe large_num_elements() returns int64_t (line 64) with comment "// TODO Replace num_elements" + +5. Search for usages: grep -rn "\.num_elements()" compiler/luci-interpreter/src/ + +6. Confirm multiple locations use vulnerable num_elements() instead of large_num_elements() + + + +## Impact + + + +- Heap Buffer Overflow via memcpy/memset with truncated size + +- Potential Remote Code Execution when processing untrusted Circle model files + +- Affects Samsung devices using ONE for on-device AI inference + + + +## CVSS Score + + + +8.1 (High) - AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H + + + +## Recommended Fix + + + +Replace all instances of num_elements() with large_num_elements() and add overflow checks: + +```cpp + +// Before (vulnerable): + +const int32_t num_elements = input()->shape().num_elements(); + +std::memcpy(output_data, input_data, num_elements * element_size); + + + +// After (fixed): + +const int64_t num_elements = input()->shape().large_num_elements(); + +const int64_t total_size = num_elements * static_cast(element_size); + +if (total_size > SIZE_MAX || num_elements < 0) { + + throw std::runtime_error("Integer overflow in size calculation"); + +} + +std::memcpy(output_data, input_data, static_cast(total_size)); + +``` + + + +## Affected Files + + + +1. compiler/luci-interpreter/src/BuddyMemoryManager.cpp + +2. compiler/luci-interpreter/src/kernels/ExpandDims.cpp + +3. compiler/luci-interpreter/src/kernels/Reshape.cpp + +4. compiler/luci-interpreter/src/kernels/While.cpp + +5. compiler/luci-interpreter/src/kernels/If.cpp + +6. compiler/luci-interpreter/src/kernels/TransposeConv.cpp + +7. compiler/luci-interpreter/src/loader/GraphLoader.cpp + + + + + +## References + + + +- CWE-190: Integer Overflow or Wraparound + +- CWE-122: Heap-based Buffer Overflow + +- Similar: CVE-2022-29216 (TensorFlow integer overflow) \ No newline at end of file diff --git a/runtime/onert/core/src/loader/BaseLoader.h b/runtime/onert/core/src/loader/BaseLoader.h index d2e2c4744ad..2dc31cbccce 100644 --- a/runtime/onert/core/src/loader/BaseLoader.h +++ b/runtime/onert/core/src/loader/BaseLoader.h @@ -114,12 +114,12 @@ template class BaseLoader BuiltinOperator getBuiltinOperator(const Operator *op) { // Enforce explicit bounds validation for opcode_index before every operator-code lookup - if (op->opcode_index() < 0 || + if (op->opcode_index() < 0 || static_cast(op->opcode_index()) >= _domain_model->operator_codes()->size()) { throw std::runtime_error("Invalid opcode_index: " + std::to_string(op->opcode_index())); } - + auto const builtin_opcode = _domain_model->operator_codes()->Get(op->opcode_index()); auto builtin_op = builtin_opcode->builtin_code(); if (builtin_op < BuiltinOperator::BuiltinOperator_PLACEHOLDER_FOR_GREATER_OP_CODES) From 7dd8a67e8ca116f2bffad557822cc2e30aa094f8 Mon Sep 17 00:00:00 2001 From: Chunseok Lee Date: Mon, 20 Apr 2026 17:11:18 +0900 Subject: [PATCH 3/5] delete dummy file --- report_vul.md | 498 -------------------------------------------------- 1 file changed, 498 deletions(-) delete mode 100644 report_vul.md diff --git a/report_vul.md b/report_vul.md deleted file mode 100644 index 5126e46d0c6..00000000000 --- a/report_vul.md +++ /dev/null @@ -1,498 +0,0 @@ -# problem 1 - -Firmware Version - -- Project: Samsung ONE - -- Repository: https://github.com/Samsung/ONE - -- Verified commit (full): 093fca849b1505705cf72c1a5f99c8bc2cb9eabd - -- Verified commit (short): 093fca84 - -- Verified commit date: 2026-04-15 12:29:04 +0900 - -- Validation date: 2026-04-17 - -- Toolchain used for reproduction: - - - circle-verify (BuildID: 1fc0053950cb395bdea5a1141d86dba2ae318eeb) - - - circle-inspect (BuildID: 07d94c72d04d07a634766bef4a13df6a715a5aff) - - - circle2circle v1.31.0 (BuildID: d03c758d20e62ec111e56743ebab8e60c2cac963) - -- Note for Samsung Mobile mapping: this report was validated on the OSS ONE commit above. - - Exact Galaxy model / One UI firmware mapping should be checked against Samsung’s internal integration branch. - - - -Description - -I reproduced this issue on https://github.com/Samsung/ONE with a model that contains invalid - -STRING tensor offsets. - - - -The model passes `circle-verify`, but crashes in `circle2circle` when string constants are read. - -So the malformed offset table is not blocked early enough. - - - -Code path - -In `compiler/luci/import/src/Nodes/CircleConst.cpp`: - -- `int32_t start = offsets[i];` - -- `int32_t next = offsets[i + 1];` - -- `std::string value(data + start, next - start);` - - - -If offsets are negative or inconsistent, pointer/length construction becomes invalid. - - - -Reproduction - -1. Generate and run with `poc/reproduce.sh` - -2. Or run directly: - - - `circle-verify poc_string_offset_int32min.circle` - - - `circle-inspect --constants --tensor_dtype --tensor_shape poc_string_offset_int32min.circle` - - - `circle2circle --remove_redundant_reshape poc_string_offset_int32min.circle out.circle` - - - -Observed result - -- `circle-verify`: PASS (exit 0) - -- `circle-inspect`: PASS (exit 0) - -- `circle2circle`: SIGSEGV (exit 139) - -- Runtime evidence: `evidence/recheck_runtime_int32min_crash.log` - -- ASAN evidence: `evidence/recheck_poc_string_offset_asan.log` - - - -Security impact - -A malicious model can bypass verifier checks and trigger process termination in import/optimization - -stage, resulting in reliable denial of service. - - - -Suggested fix - -- Validate STRING offsets as non-negative, monotonic, and bounded within data buffer. - -- Reject malformed offset tables before pointer arithmetic. - -- Handle invalid input with explicit error returns instead of process crash. - - - -Attached files - -- `poc/poc_string_offset_int32min.circle` - -- `poc/poc_string_offset_int32min.circle.json` - -- `poc/reproduce.sh` - -- `evidence/recheck_runtime_int32min_crash.log` - -- `evidence/recheck_poc_string_offset_asan.log` - -- `evidence/recheck_code_path.txt` - -- `evidence/version_info.txt` - - -# problem 2 - -Firmware Version - -- Project: Samsung ONE - -- Repository: https://github.com/Samsung/ONE - -- Verified commit (full): 093fca849b1505705cf72c1a5f99c8bc2cb9eabd - -- Verified commit (short): 093fca84 - -- Verified commit date: 2026-04-15 12:29:04 +0900 - -- Validation date: 2026-04-17 - -- Toolchain used for reproduction: - - - circle-verify (BuildID: 1fc0053950cb395bdea5a1141d86dba2ae318eeb) - - - circle-inspect (BuildID: 07d94c72d04d07a634766bef4a13df6a715a5aff) - - - circle2circle v1.31.0 (BuildID: d03c758d20e62ec111e56743ebab8e60c2cac963) - -- Note for Samsung Mobile mapping: this report was validated on the OSS ONE commit above. - - Exact Galaxy model / One UI firmware mapping should be checked against Samsung’s internal integration branch. - - - -Description - -I verified this issue on https://github.com/Samsung/ONE using the commit listed above. - -A crafted model with an out-of-range `opcode_index` is accepted by `circle-verify`, but crashes - -in later processing (`circle-inspect`, `circle2circle`). - - - -In short, malformed operator metadata crosses the verifier boundary and fails only when - -operator code lookup happens downstream. - - - -Code locations - -- `runtime/onert/core/src/loader/BaseLoader.h:116` - - - `_domain_model->operator_codes()->Get(op->opcode_index())` - -- `compiler/mio-circle/src/Reader.cpp:164` - - - `assert(index < _op_codes.size())` - -- `compiler/luci/import/src/CircleReader.cpp:355` - - - `assert(index < op_codes.size())` - - - -Reproduction - -1. Prepare PoC model: - - - `poc/poc_opcode_index_oob.circle` - -2. Run: - - - `circle-verify poc_opcode_index_oob.circle` - - - `circle-inspect --operators poc_opcode_index_oob.circle` - - - `circle2circle --remove_redundant_reshape poc_opcode_index_oob.circle out.circle` - - - -Observed result - -- `circle-verify`: PASS (exit 0) - -- `circle-inspect --operators`: abort (exit 134) - -- `circle2circle --remove_redundant_reshape`: abort (exit 134) - -- Evidence log: `evidence/recheck_runtime_opcode_index_oob.log` - - - -Security impact - -This is a reliable denial-of-service condition in model-processing pipelines that trust verifier pass - -as a safety gate. A malicious model can survive initial validation and terminate follow-up stages. - - - -Suggested fix - -- Enforce explicit bounds validation for `opcode_index` before every operator-code lookup. - -- Apply the same check consistently across verifier/import/runtime paths. - -- Return controlled parser/validation errors instead of aborting. - - - -Attached files - -- `poc/poc_opcode_index_oob.circle` - -- `poc/poc_opcode_index_oob.circle.json` - -- `poc/reproduce.sh` - -- `evidence/recheck_runtime_opcode_index_oob.log` - -- `evidence/recheck_code_path.txt` - -- `evidence/version_info.txt` - -# problem 3 - -## Summary - - - -Multiple integer overflow vulnerabilities (CWE-190) were identified in Samsung ONE's luci-interpreter component that can lead to heap buffer overflow (CWE-122) when processing malicious Circle model files. - - - -## Root Cause - - - -The Shape::num_elements() function returns int32_t and can overflow when tensor dimensions are large: - - - -File: compiler/luci-interpreter/include/luci_interpreter/core/Tensor.h (lines 53-61) - -```cpp - -int32_t num_elements() const - -{ - - int32_t result = 1; - - for (const int32_t dim : _dims) - - { - - result *= dim; // Integer overflow possible! - - } - - return result; - -} - -``` - - - -Samsung is aware of this issue - there's a comment "// TODO Replace num_elements" and a safe version large_num_elements() exists, but multiple locations still use the vulnerable num_elements(). - - - -## Vulnerable Locations (7 instances) - - - -### 1. BuddyMemoryManager.cpp (Lines 46-47) - -```cpp - -const int32_t num_elements = tensor.shape().num_elements(); - -auto size = num_elements * element_size; - -``` - - - -### 2. ExpandDims.cpp (Lines 83-84) - -```cpp - -const int32_t num_elements = input()->shape().num_elements(); - -std::memcpy(output_data, input_data, num_elements * element_size); - -``` - - - -### 3. Reshape.cpp (Lines 104-105) - -```cpp - -const int32_t num_elements = input()->shape().num_elements(); - -std::memcpy(output_data, input_data, num_elements * element_size); - -``` - - - -### 4. While.cpp (Lines 37-39) - -```cpp - -const int32_t num_elements = src[i]->shape().num_elements(); - -std::memcpy(dst[i]->data(), src[i]->data(), num_elements * element_size); - -``` - - - -### 5. If.cpp (Lines 73, 89) - -```cpp - -std::memcpy(graph_inputs[i]->data(), input(i)->data(), num_elements * element_size); - -``` - - - -### 6. TransposeConv.cpp (Lines 205, 299) - -```cpp - -std::memset(scratch_data, 0, scratch_tensor->shape().num_elements() * sizeof(int32_t)); - -``` - - - -### 7. GraphLoader.cpp (Line 44) - -```cpp - -*data_size = num_elements * element_size; - -``` - - - -## Attack Scenario - - - -1. Attacker crafts malicious Circle model with tensor dimensions causing overflow - - - Example: dims = [65536, 65536] = 4,294,967,296 overflows to 0 in int32_t - - - Example: dims = [65536, 32768] = 2,147,483,648 overflows to negative - -2. Victim loads model using Samsung ONE runtime (e.g., on-device AI inference) - -3. Memory allocated with truncated/incorrect size - -4. memcpy/memset writes beyond allocated buffer - -5. Heap corruption potential code execution - - - -## Steps to Reproduce - - - -1. Clone Samsung ONE repository: https://github.com/Samsung/ONE - -2. Review file: compiler/luci-interpreter/include/luci_interpreter/core/Tensor.h - -3. Observe num_elements() returns int32_t (line 53) - -4. Observe large_num_elements() returns int64_t (line 64) with comment "// TODO Replace num_elements" - -5. Search for usages: grep -rn "\.num_elements()" compiler/luci-interpreter/src/ - -6. Confirm multiple locations use vulnerable num_elements() instead of large_num_elements() - - - -## Impact - - - -- Heap Buffer Overflow via memcpy/memset with truncated size - -- Potential Remote Code Execution when processing untrusted Circle model files - -- Affects Samsung devices using ONE for on-device AI inference - - - -## CVSS Score - - - -8.1 (High) - AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H - - - -## Recommended Fix - - - -Replace all instances of num_elements() with large_num_elements() and add overflow checks: - -```cpp - -// Before (vulnerable): - -const int32_t num_elements = input()->shape().num_elements(); - -std::memcpy(output_data, input_data, num_elements * element_size); - - - -// After (fixed): - -const int64_t num_elements = input()->shape().large_num_elements(); - -const int64_t total_size = num_elements * static_cast(element_size); - -if (total_size > SIZE_MAX || num_elements < 0) { - - throw std::runtime_error("Integer overflow in size calculation"); - -} - -std::memcpy(output_data, input_data, static_cast(total_size)); - -``` - - - -## Affected Files - - - -1. compiler/luci-interpreter/src/BuddyMemoryManager.cpp - -2. compiler/luci-interpreter/src/kernels/ExpandDims.cpp - -3. compiler/luci-interpreter/src/kernels/Reshape.cpp - -4. compiler/luci-interpreter/src/kernels/While.cpp - -5. compiler/luci-interpreter/src/kernels/If.cpp - -6. compiler/luci-interpreter/src/kernels/TransposeConv.cpp - -7. compiler/luci-interpreter/src/loader/GraphLoader.cpp - - - - - -## References - - - -- CWE-190: Integer Overflow or Wraparound - -- CWE-122: Heap-based Buffer Overflow - -- Similar: CVE-2022-29216 (TensorFlow integer overflow) \ No newline at end of file From 62501151613a7821293a3288589e6f031fc28c66 Mon Sep 17 00:00:00 2001 From: Chunseok Lee Date: Mon, 20 Apr 2026 17:54:01 +0900 Subject: [PATCH 4/5] fix runtime error --- compiler/luci-interpreter/src/kernels/ExpandDims.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/luci-interpreter/src/kernels/ExpandDims.cpp b/compiler/luci-interpreter/src/kernels/ExpandDims.cpp index a22eba2f0e6..3330f1915f8 100644 --- a/compiler/luci-interpreter/src/kernels/ExpandDims.cpp +++ b/compiler/luci-interpreter/src/kernels/ExpandDims.cpp @@ -83,7 +83,7 @@ void ExpandDims::execute() const const int64_t num_elements = input()->shape().large_num_elements(); // Check for integer overflow in size calculation - if (num_elements < 0 || num_elements > SIZE_MAX / element_size) + if (num_elements < 0 || static_cast(num_elements) > SIZE_MAX / element_size) { throw std::runtime_error("Integer overflow in size calculation"); } From b9f7a5d66ec24d865b6f44f8f64697a3f3cafc8d Mon Sep 17 00:00:00 2001 From: Chunseok Lee Date: Tue, 21 Apr 2026 09:05:26 +0900 Subject: [PATCH 5/5] fix --- compiler/luci-interpreter/src/BuddyMemoryManager.cpp | 2 +- compiler/luci-interpreter/src/kernels/If.cpp | 2 +- compiler/luci-interpreter/src/kernels/Reshape.cpp | 2 +- compiler/luci-interpreter/src/kernels/TransposeConv.cpp | 2 +- compiler/luci-interpreter/src/kernels/While.cpp | 2 +- compiler/luci-interpreter/src/loader/GraphLoader.cpp | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/luci-interpreter/src/BuddyMemoryManager.cpp b/compiler/luci-interpreter/src/BuddyMemoryManager.cpp index 7cdaadbd4aa..507b7ac1e42 100644 --- a/compiler/luci-interpreter/src/BuddyMemoryManager.cpp +++ b/compiler/luci-interpreter/src/BuddyMemoryManager.cpp @@ -46,7 +46,7 @@ void BuddyMemoryManager::allocate_memory(luci_interpreter::Tensor &tensor) const int64_t num_elements = tensor.shape().large_num_elements(); // Check for integer overflow in size calculation - if (num_elements < 0 || num_elements > SIZE_MAX / element_size) + if (num_elements < 0 || static_cast(num_elements) > SIZE_MAX / element_size) { throw std::runtime_error("Integer overflow in size calculation"); } diff --git a/compiler/luci-interpreter/src/kernels/If.cpp b/compiler/luci-interpreter/src/kernels/If.cpp index aa0cebb2762..3d2e297c7d8 100644 --- a/compiler/luci-interpreter/src/kernels/If.cpp +++ b/compiler/luci-interpreter/src/kernels/If.cpp @@ -87,7 +87,7 @@ void If::execute() const const std::size_t element_size = getDataTypeSize(output(i)->element_type()); // Check for integer overflow in size calculation - if (num_elements < 0 || num_elements > SIZE_MAX / element_size) + if (num_elements < 0 || static_cast(num_elements) > SIZE_MAX / element_size) { throw std::runtime_error("Integer overflow in size calculation"); } diff --git a/compiler/luci-interpreter/src/kernels/Reshape.cpp b/compiler/luci-interpreter/src/kernels/Reshape.cpp index fefac2bee2b..f14a4f7f4d0 100644 --- a/compiler/luci-interpreter/src/kernels/Reshape.cpp +++ b/compiler/luci-interpreter/src/kernels/Reshape.cpp @@ -104,7 +104,7 @@ void Reshape::execute() const const int64_t num_elements = input()->shape().large_num_elements(); // Check for integer overflow in size calculation - if (num_elements < 0 || num_elements > SIZE_MAX / element_size) + if (num_elements < 0 || static_cast(num_elements) > SIZE_MAX / element_size) { throw std::runtime_error("Integer overflow in size calculation"); } diff --git a/compiler/luci-interpreter/src/kernels/TransposeConv.cpp b/compiler/luci-interpreter/src/kernels/TransposeConv.cpp index 53d0ae0880e..04664680d4b 100644 --- a/compiler/luci-interpreter/src/kernels/TransposeConv.cpp +++ b/compiler/luci-interpreter/src/kernels/TransposeConv.cpp @@ -300,7 +300,7 @@ void TransposeConv::evalQuantizedS16() const const size_t element_size = sizeof(int64_t); // Check for integer overflow in size calculation - if (num_elements < 0 || num_elements > SIZE_MAX / element_size) + if (num_elements < 0 || static_cast(num_elements) > SIZE_MAX / element_size) { throw std::runtime_error("Integer overflow in size calculation"); } diff --git a/compiler/luci-interpreter/src/kernels/While.cpp b/compiler/luci-interpreter/src/kernels/While.cpp index 455bbab8327..3259f741f47 100644 --- a/compiler/luci-interpreter/src/kernels/While.cpp +++ b/compiler/luci-interpreter/src/kernels/While.cpp @@ -39,7 +39,7 @@ void copy(const std::vector &src, const std::vector &d const std::size_t element_size = getDataTypeSize(src[i]->element_type()); // Check for integer overflow in size calculation - if (num_elements < 0 || num_elements > SIZE_MAX / element_size) + if (num_elements < 0 || static_cast(num_elements) > SIZE_MAX / element_size) { throw std::runtime_error("Integer overflow in size calculation"); } diff --git a/compiler/luci-interpreter/src/loader/GraphLoader.cpp b/compiler/luci-interpreter/src/loader/GraphLoader.cpp index 24971ef4a99..abb260d0f58 100644 --- a/compiler/luci-interpreter/src/loader/GraphLoader.cpp +++ b/compiler/luci-interpreter/src/loader/GraphLoader.cpp @@ -42,7 +42,7 @@ template const void *getNodeDataImpl(const luci::CircleConst *node const int64_t num_elements = node->size
(); // Assuming size
() uses large_num_elements() // Check for integer overflow in size calculation - if (num_elements < 0 || num_elements > SIZE_MAX / element_size) + if (num_elements < 0 || static_cast(num_elements) > SIZE_MAX / element_size) { throw std::runtime_error("Integer overflow in size calculation"); }