PoC for Huntr Report โ€” Missing Resource Limits and Eager Operator Deserialization Cause High Memory Amplification in model-explorer-circle

Target: model-explorer-circle 0.1.4 (Circle adapter for Google Model Explorer)

CWE: CWE-400 (Uncontrolled Resource Consumption)

Summary

A structurally parseable Circle model containing 200,000 operators causes disproportionate memory and CPU consumption when processed by CircleAdapter, because the adapter eagerly deserializes the entire model into a heavyweight Python object tree (ModelT.InitFromObj()) instead of using FlatBuffers' native lazy table access, and then builds a second, parallel in-memory representation (GraphNode objects) on top of it โ€” with no operator-count or file-size limit enforced anywhere before these allocations occur.

Threat model

An attacker can distribute a syntactically valid .circle model through a model repository, issue attachment, shared project, or other untrusted source. Processing the file through the Circle adapter causes disproportionate transient memory consumption before the user receives the rendered graph. The server binds to localhost by default (model_explorer.consts.DEFAULT_HOST), so this is a local-processing threat, not a remote network attack unless the operator explicitly overrides --host.

Environment

model-explorer-circle==0.1.4
ai-edge-model-explorer==0.1.32
flatbuffers==25.12.19
Python 3.13.3
Windows 11 Home 10.0.26200 (Build 26200)

Files in this repository

File Purpose
poc_200000_operators.circle The PoC model file used for all measurements below (5.47 MB, 200,000 operators).
generate_poc.py Regenerates the PoC file for any operator count N.
measure_eager_vs_lazy.py Reproduces the A/B comparison (eager InitFromObj() vs. lazy FlatBuffers table access) and the live object-count check.
measure_concurrency.py Reproduces the concurrent-request memory measurement against a running server.
results.csv Raw numbers from every measurement below.

Impact

A 5.47 MB, structurally parseable Circle model containing 200,000 operators caused approximately 375 MB additional peak RSS and 6.37 seconds of processing.

A direct A/B measurement showed that eager unpacking through ModelT.InitFromObj() added 142.8 MB RSS, while reading the same tested operator fields lazily through FlatBuffers' native table API added 5.36 MB. This represents approximately 26.6x additional RSS associated with eager object materialization during this stage.

The adapter then retains both representations simultaneously: 200,000 unpacked OperatorT objects and 200,002 generated GraphNode objects were observed coexisting through live object counts. No file-size, operator-count, or equivalent resource limit is enforced before these allocations occur.

Concurrency testing against one server process showed compounding peak memory consumption:

  • 1 simultaneous request: +271.6 MB RSS
  • 2 simultaneous requests: +494.3 MB RSS
  • 3 simultaneous requests: +799.9 MB RSS

The server remains responsive to unrelated requests (verified by polling /check_health during processing), but concurrent conversions accumulate memory pressure inside the same process. Memory returns to approximately baseline after completion (verified across 3 sequential requests: 90.6 / 91.6 / 92.9 MB, close to the 86-96 MB baseline), so this is transient uncontrolled resource consumption, not a memory leak.

Reproduction

1. Setup

pip install ai-edge-model-explorer==0.1.32 model-explorer-circle==0.1.4 flatbuffers psutil requests

2. Regenerate the PoC (optional -- the file is already included)

python generate_poc.py 200000 poc_200000_operators.circle

3. Eager-vs-lazy A/B measurement + live object counts

python measure_eager_vs_lazy.py poc_200000_operators.circle

Expected output (from our run):

file size:                 5468.9 KB
operators touched (lazy):  200000  (sanity: inputs=0 outputs=200000)
lazy scan time:            1.111 s
lazy RSS delta:            5.35 MB

eager load_model time:     4.076 s
eager build_graph time:    2.404 s
eager RSS after load:      142.4 MB  (InitFromObj stage)
eager RSS after build:     376.0 MB  (+build_graph on top)
live OperatorT instances:  200000
live TensorT instances:    1
live GraphNode instances:  200002
adapter.model still referenced (both representations coexist): True

==> eager/lazy overhead ratio for this stage: 26.6x

4. Concurrency measurement

model-explorer --extensions model_explorer_circle --port 8093 --no_open_in_browser &
# find the server PID (Linux/macOS: pgrep -f model-explorer; Windows: see task manager or
# `Get-CimInstance Win32_Process | Where CommandLine -like '*8093*'` in PowerShell)

python measure_concurrency.py poc_200000_operators.circle <server_pid> --port 8093 --max-concurrency 3

Safety note: measure_concurrency.py aborts automatically if available system memory drops below 1 GB, and defaults to a maximum of 3 concurrent requests. All measurements in this report were run locally on a single developer machine and intentionally capped at 3 concurrent requests for safety -- we did not test higher concurrency or attempt to induce an actual out-of-memory condition. Numbers above 3 concurrent requests are not claimed or extrapolated.

Suggested fixes

  • Check OperatorsLength() (and tensor/subgraph counts) before calling InitFromObj().
  • Add a configurable maximum operator/tensor count.
  • Add an upload size limit.
  • Reject oversized models with a clear validation error instead of processing them fully.
  • Avoid full eager deserialization when only a bounded set of fields is needed.
  • Add a regression test with a high-operator-count file.

Relation to sibling reports

This is an independent report from the two TypeError-based reports on the same target (missing subgraphs / missing operators validation). The root cause, affected code path, CWE (CWE-400 vs. CWE-20/CWE-754), impact class (resource consumption vs. unhandled exception), and fix are all different, so this is not considered a duplicate of either.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. ๐Ÿ™‹ Ask for provider support