Skip to content

Bounds-check HostIP() in release mode (close an out-of-bounds read)#81

Merged
CoreyRDean merged 2 commits into
developfrom
fix/hostip-bounds
May 30, 2026
Merged

Bounds-check HostIP() in release mode (close an out-of-bounds read)#81
CoreyRDean merged 2 commits into
developfrom
fix/hostip-bounds

Conversation

@CoreyRDean
Copy link
Copy Markdown
Collaborator

Non-technical summary

HostIP() looks up a cached host IP address by index. It only checked that the index was valid in debug builds — so in a normal release build, HostIP(0) or an out-of-range index read past the end of the array (or before it), an out-of-bounds memory read that can crash or leak nearby memory into a Blitz integer. This fixes it to validate in all builds, returning 0 (and logging) for a bad index. Advances INTENT #6 ("trust the runtime").

Technical summary

bbHostIP(index) in src/blitzrc/bbruntime/bbsockets.cpp indexed std::vector<int> host_ips with a BASIC-supplied index, guarded by if(debug) only — release skipped the check and dereferenced host_ips[index-1] (UB: HostIP(0)host_ips[-1], HostIP(big)→past the vector). This is the same debug-only-guard class already closed for banks (#73), ReadBytes/WriteBytes (#74/#75), and BBList; bbsockets.cpp was the unswept remainder. Now validated in both modes, mirroring the file's sibling helpers (debugUDPStream etc.): clean RuntimeError in debug, errorLog + return 0 in release. Valid-index path byte-identical. Windows runtime only (WSA-gated); no ABI change.

Acceptance criteria + results

  • HostIP(0), HostIP(-1), HostIP(n>CountHostIPs()) return 0 (no OOB) in release/test mode; raise a clean error in debug.
  • Valid HostIP(1..CountHostIPs()) unchanged.
  • New tests/SocketTest.bb passes under blitzcc -t — and it's genuinely CI-reachable (the host-IP path needs no audio/graphics device, unlike most gxruntime code; it's offline-safe since an empty host_ips just makes every index out-of-range). Pre-fix, HostIP(0) in test mode was an OOB read; reaching the final assert is the regression guard.
  • Full test.bat green; build 0 errors; corpus sweep unaffected.

Trade-offs, deferred follow-ups

  • Not pile-on: this is the first bounds/release-guard fix in 5 iterations (recent work was diagnostics, contextual-keyword, GC docs, audio) — it completes the established debug-only-guard audit on the last unswept module (sockets).
  • Deferred: contextual Release/Recast/Reference (legacy-compat Phase 2). Unlike Test, these are prefix operators in parseUniExpr (Release[.Type] <operand>), so making them contextual needs expression-position disambiguation (operand-follows = keyword vs operator/terminator-follows = identifier) — M-effort with real ambiguity risk (Release is GC-surface). Needs a focused feasibility pass.
  • Deferred: gxSoundSample::load short-filename crash — real, but not reachable in headless -t (audio uninitialized), so no CI regression test; manual-only.
  • Other Class B builtins; GC reference_map soundness; manual audible-pan check.

🤖 Generated with Claude Code

CoreyRDean and others added 2 commits May 30, 2026 03:26
bbHostIP(index) indexes a std::vector<int> with a BASIC-supplied index but only
validated it when the debug runtime was active. In release builds -- what shipped
games run -- the check was skipped and host_ips[index-1] dereferenced unchecked:
HostIP(0) read host_ips[-1], HostIP(big) read past the vector. An out-of-bounds
heap read (UB) that can crash or leak adjacent heap bytes into a BASIC integer.

This is the same debug-only-guard defect class already closed for banks, streams,
and lists; bbsockets.cpp was the unswept remainder. Validate in BOTH modes,
mirroring the file's sibling helpers and the bank/stream idiom: clean RuntimeError
in debug, errorLog + safe return 0 in release. Valid-index path unchanged.

tests/SocketTest.bb: pins the contract -- HostIP(0)/HostIP(-1)/HostIP(huge) return
0 instead of reading out of bounds. Offline/CI-safe: it doesn't depend on name
resolution (an empty host_ips just makes every index out-of-range), and the
host-IP path needs no audio/graphics device, so unlike most gxruntime code it is
reachable under blitzcc -t. Pre-fix, HostIP(0) was an OOB read; reaching the final
assert proves the fix.

Windows runtime only (sockets are WSA-gated); no ABI change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Root cause (debug-only guard -> release OOB read), the bank/stream guard idiom,
why it's CI-testable (no audio/graphics needed), and deferred follow-ups
(Phase-2 contextual keywords need parseUniExpr expression-position disambiguation;
gxsound short-filename crash is not CI-testable).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@CoreyRDean CoreyRDean requested a review from a team as a code owner May 30, 2026 08:27
@CoreyRDean CoreyRDean added the bug Something isn't working label May 30, 2026
@CoreyRDean CoreyRDean merged commit 2f55c0f into develop May 30, 2026
4 checks passed
@CoreyRDean CoreyRDean deleted the fix/hostip-bounds branch May 30, 2026 08:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant