Skip to content

fix: bound strtod() scan to the input length - #36

Open
SweetSophia wants to merge 2 commits into
Live2D:developfrom
SweetSophia:fix/strtod-bounded-scan
Open

SweetSophia wants to merge 2 commits into
Live2D:developfrom
SweetSophia:fix/strtod-bounded-scan

Conversation

@SweetSophia

Copy link
Copy Markdown

Bug

strtod() in src/live2dcubismframework.ts scanned with an unbounded loop:

for (let i = 1; ; i++) {

Termination depends on Number(s.substring(0, i)) becoming NaN. Once i exceeds s.length, substring clamps to the full string. A complete valid number never produces NaN, so the loop never exits. Number("") === 0, so empty / whitespace-only input hangs the same way.

Caller: CubismJson.parseValue in src/utils/cubismjson.ts. Typical JSON with a trailing } often survives; a number at EOF, a numeric document, or trailing whitespace after a number does not.

A second hang remained after bounding the scan: parseValue derived outEndPos with buffer.indexOf(afterString[0]). When strtod consumes through end-of-input the remainder is '', and indexOf('') is always 0, so parse position rewound.

Change

  1. Bound the scan: for (let i = 1; i <= s.length; i++).
  2. Compute outEndPos from consumed length instead of indexOf:
const numericSlice = buffer.slice(i);
f = strtod(numericSlice, afterString);
const remainder = afterString[0] ?? '';
outEndPos[0] = i + numericSlice.length - remainder.length;

Parse / endPtr behavior is otherwise unchanged.

Repro (Node)

Unbounded strtod('123', ['']) never returns. Same for "1.5", "1e10", "-3.2", "1E-4", "", " 5 ". Inputs with a terminator ("123}", "123,", "123abc") already returned.

After the bound-only change, EOF numbers still rewound via indexOf(''). Consumed-length outEndPos advances to the end of the slice.

Checks

  • eslint on the touched files: clean
  • tsc --noEmit: pre-existing Live2DCubismCore errors on develop (Core is not in this repo). No new errors from this change.

Ikati tracking: https://github.com/SweetSophia/ikati/issues/27
Fork review: SweetSophia#1

The scan loop had no upper bound. Once i exceeds s.length, substring
clamps to the full string, so a complete number never becomes NaN and
the loop never exits. Empty and whitespace-only inputs hang the same
way because Number('') is 0.

Keep the existing parse behavior; only terminate the prefix walk.
strtod() remainder is empty when a number reaches end-of-input.
buffer.indexOf('') always returns 0, so parseValue rewound to the
start of the buffer and could loop. Derive outEndPos from the
slice length minus remainder length instead.
Copilot AI lite review requested due to automatic review settings September 5, 2026 17:11

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The changes directly address confirmed infinite-loop and parse-position rewind bugs with minimal, localized risk and consistent behavior for existing callers.

Pull request overview

Fixes a JSON parsing hang by ensuring numeric scanning and parse position advancement are correctly bounded at end-of-input. This improves reliability of CubismJson.parseValue when parsing numbers at EOF or with trailing whitespace.

Changes:

  • Bound strtod()’s incremental scan loop to s.length to prevent non-terminating scans on valid EOF numbers or empty input.
  • Updated CubismJson.parseValue to compute outEndPos from the consumed numeric length (instead of indexOf), preventing parse-position rewind when the remainder is ''.
File summaries
File Description
src/live2dcubismframework.ts Prevents infinite loops in strtod() by bounding the scan to the input length.
src/utils/cubismjson.ts Fixes numeric outEndPos calculation to advance correctly through end-of-input cases.
Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 0
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants