The allocation path
At the report-time revision, Atlas parsed the rolling-window operand as an integer, carried it through StatefulExpr.RollingCount and OnlineRollingCount, and passed it to RollingBuffer. RollingBuffer then called ArrayHelper.fill, which eagerly allocated a Double array of that length.
The missing boundary
The graph max-datapoints setting constrained graph density; it did not cap the independent state allocated for a rolling window. A bounded route-level probe kept max-datapoints at 10 while a 4,096-point rolling-count request still reached the render path successfully.
The allocation math
TraceMint's analysis quantified the operand. A window of two billion elements requests a Double array of that length—about sixteen gigabytes—well before the value nears the integer ceiling, so a single crafted rolling-count expression can drive the heap to OutOfMemoryError. A window near three hundred million (roughly two and a half gigabytes) already exhausts a typical Atlas heap. The buffer is allocated per matched time series, so a broad matcher multiplies the request by the number of series it selects, and a duration-derived period (trend and related operators) can integer-overflow to a negative size and fault instead.
The impact boundary
This was an availability-only finding. A caller able to reach the graph endpoint could request memory disproportionate to the request size and potentially destabilize the service. The research did not identify data exposure, authorization bypass or code execution; practical risk depended on who could reach that endpoint and on process memory limits.
The later upstream hardening
A later public Atlas commit added a maximum window size at the RollingBuffer allocation chokepoint, applied it across rolling, delay and trend operators, and added rejection tests. The public commit establishes the code change; this note does not claim a causal link between the submission and that change.
The lesson
Cap user-controlled resource dimensions where allocation occurs. Adjacent limits on output density, time range or response size do not necessarily constrain stateful working memory.
Follow the allocation path.
- 01
Graph endpoint at the report-time revision
The graph route converts the request into graph configuration and dispatches rendering.
- 02
Rolling-window expression parsing
The rolling-count word accepted an integer window and constructed the stateful expression.
- 03
Stateful expression handoff
The expression created OnlineRollingCount with the supplied integer window.
- 04
Rolling buffer construction
OnlineRollingCount delegated its size to RollingBuffer.
- 05
Allocation point
RollingBuffer initialized a Double array through ArrayHelper.fill.
- 06
Array allocation
ArrayHelper.fill allocated the requested Double-array length before filling it.
- 07
Later upstream memory bound
Commit 93a2d476 introduced a shared maximum at RollingBuffer, covering rolling, delay and trend operators and rejecting out-of-range sizes.
Separate the records
This note uses a reporter-supplied program reference for the submission date and assessment boundary. It links only to public, immutable Atlas source and the later upstream hardening commit.
No private HackerOne report is presented as a public source. The upstream commit confirms the affected allocation pattern and the later code change. It does not establish that this submission caused or exclusively identified that change.