Bounds-check HostIP() in release mode (close an out-of-bounds read)#81
Merged
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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)insrc/blitzrc/bbruntime/bbsockets.cppindexedstd::vector<int> host_ipswith a BASIC-supplied index, guarded byif(debug)only — release skipped the check and dereferencedhost_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.cppwas the unswept remainder. Now validated in both modes, mirroring the file's sibling helpers (debugUDPStreametc.): cleanRuntimeErrorin debug,errorLog+return 0in 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.HostIP(1..CountHostIPs())unchanged.tests/SocketTest.bbpasses underblitzcc -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 emptyhost_ipsjust 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.test.batgreen; build 0 errors; corpus sweep unaffected.Trade-offs, deferred follow-ups
Release/Recast/Reference(legacy-compat Phase 2). UnlikeTest, these are prefix operators inparseUniExpr(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 (Releaseis GC-surface). Needs a focused feasibility pass.gxSoundSample::loadshort-filename crash — real, but not reachable in headless-t(audio uninitialized), so no CI regression test; manual-only.reference_mapsoundness; manual audible-pan check.🤖 Generated with Claude Code