# Selector integer conversion, storage bounds and dispatcher entry routes Static pass, 2026-10-11. Reproduce with `python3 scripts/analyze_selector_bounds.py`; check with `python3 scripts/test_selector_bounds.py`. [Generated evidence](selector-bounds.json) retains verified QVM/native provenance, 18 integer instruction traces, composed digit/parser/slot-bound fixtures, name-copy call bounds, match insertion boundaries, dispatcher arguments and ten Linux ELF64 function graphs. This extends [authentication queries](AUTH_QUERIES.md). No original process or complete QVM runs; all CALLs remain recorded with zero placeholder results. The bounded evaluator can now stop explicitly at a helper's LEAVE instruction and capture its operand stack without implementing a real VM frame return. ## BND-001: decimal parser and wrap behavior Entry **37159** reads signed bytes through LOAD1/SEX8. It skips leading bytes whose signed value is at most **32**, returning zero if it encounters NUL. Consequently bytes 128–255 also satisfy its leading-skip comparison: the standalone parser fixture `ff 31 32` returns 12. This is separate from the selector's digit classifier, which sends such a string to name handling rather than numeric parsing. After leading skipping, the parser consumes one optional `+` or `-`, initializes an accumulator to zero, and consumes digits until the first nondigit. Each digit performs **accumulator * 10 + digit** through MULI/ADD, and the result is multiplied by the sign. There is no visible range/overflow check, decimal-point handling or trailing-input validation. | Input | Result in the bounded 32-bit evaluator | | --- | --- | | Empty / whitespace / `junk` | 0 | | `01`, `+12`, `-12junk`, `12.5` | 1, 12, -12, 12 respectively | | `2147483647` / `2147483648` | 2147483647 / -2147483648 | | `4294967295` / `4294967296` / `4294967299` | -1 / 0 / 3 | | `-2147483648` / `-2147483649` | -2147483648 / 2147483647 | The tests also compare actual instruction-loop results against independently computed modulo-2^32 arithmetic at multiple boundaries. This establishes the bounded evaluator's interpretation of the supplied instructions, not original interpreter/JIT runtime conformance. Original execution modes and cross-platform arithmetic remain to be checked. Composed fixtures feed digit-only strings through the selector's actual classifier, this parser and its actual signed slot-bounds slice with a supplied count of four. `4294967296` and `4294967299` then pass as slots zero and three; `4294967295` and `2147483648` fail as negative slots. These are separate slices whose results are explicitly connected, not execution of the whole selector or a native command. The Rust selector contract must decide this compatibility behavior based on an original-runtime fixture rather than assuming a checked integer parse behaves identically. ## BND-002: name copy and match-list capacity Selector instructions **253692–253703** call copy helper 69338 with local destination **24**, source `record + 504` and capacity **64**. The [previous copy-helper recovery](DEFERRED_AUTH.md) identifies game trap 102 / native strncpy. The helper's positive-capacity slice supplies native copy count **63** and explicitly writes NUL at destination offset **63**. Source-null and capacity-below-one branches invoke the existing error helper; they are not treated as returning safely here. The report composes a **modeled** first-63-byte copy with the actual cleaning instructions. Native libc is not invoked and padding/overlap/error behavior is not newly verified. Under that copy contract, 63 `A` bytes, 64 `A` bytes and 63 `A` bytes followed by `X` yield the same cleaned name. Color escapes also consume space before cleanup: a 64-byte source beginning with `^1` has only its first 63 bytes available for cleaning. Complete selection of those colliding names still depends on exact-match precedence and the selector text. The report does not claim a whole-selector collision test has run. The name selector stores partial-match pointers at **local 96 + match_count * 4** and increments match_count. Local 352 is the next recovered scalar temporary, so local offsets **96 through 348** form a **256-byte / 64-pointer** region. Bounded insertion fixtures show: | Prior match count | Written local offset | New count | | --- | --- | --- | | 0 | 96 | 1 | | 63 | 348 | 64 | | 64 | 352 | 65 | There is no explicit match-count bound in this insertion slice. The last fixture deliberately violates the intended count/layout invariant; it shows the 65th insertion crossing into local 352, not a demonstrated original-runtime overflow or a complete account of its effects. The surrounding name loop contributes at most one partial entry per visited slot and exits immediately on an exact match. A client count at most 64 therefore fits this region, subject to normal loop/state invariants. The loop count at **34992524** is initialized at instruction **4220** from the integer mirror **34089604**, identified as `sv_maxclients` (main registry default `12`, raw flags 37). The same initialization region clears **405504 = 64 * 6336** bytes at VM address 101536 and sets the record-base global to that address. The QVM count-copy slice contains no clamp. All **109 literal references** to the count address are indexed; the collector does not claim alias/bulk-write coverage. Both Linux ELF64 `SV_Startup` graphs show the normal native startup correction path: integer values at most zero request 1, values above **64** request 64, then the cvar integer is reloaded for allocation. `SV_BoundMaxClients` similarly accepts a lower-bound argument and uses upper comparison 64. Native Cvar_Set timing/merge semantics, alternate startup/restart paths, dynamic max-client changes and maliciously inconsistent VM/native state are not verified here. Thus native startup evidence supports the normal 64-client invariant but does not prove that every selector invocation has a safe count. ## BND-003: native/QVM/chat entry chain Initialized QVM entry table **3536** maps command 0 to instruction **21**, command 6 to **105**, and command 9 to **125**. Retained entry windows establish these routes: - **Initialization:** case 0 forwards its three arguments to entry 3877. The native `SV_InitGameProgs` tail calls VM command zero after VM creation and time retrieval. This connects the count-copy initialization above and the previously recovered call to internal auth-mode setup at instruction 5042 to the native game initialization route; restart/re-entry scheduling is still open. - **Client commands:** case 6 forwards its first argument to entry **204035**. The native `SV_ExecuteClientCommand` tail calls VM command six, deriving a native client slot from its client pointer. Entry 204035 uses that slot to address **34091368 + slot * 880**, checks object offset 520 for a nonzero record pointer and retrieves command argv 0. Complete native admission/flood/command filtering is retained in its graph but not fully specified by this pass. - **Console commands:** case 9 calls **262012** and returns its result. Native `SV_GameCommand` returns zero unless native server state equals **2**; on that path it invokes VM command nine. This establishes a server-state gate and the console-dispatch entry, not native rcon authentication or shell/console access policy. Entry 204035 compares command names against **say**, **say_team** and **me**, passing its object to chat handler **187911** with raw second arguments **0**, **1** and **4**, respectively, and a zero third argument. Three actual argument slices are retained. Correction (2026-10-11): with ENTER frame 3340, incoming argument locals 3348/3352/3356 are the first/second/third arguments. Local **3352** holds channel mode, whereas the early branch reads **3356**, the third argument. The original pass mistakenly mapped that branch to the second argument. All three observed say/say_team/me callers supply third argument **zero**, so all three take the path toward slash/auth checks. A synthetic nonzero third argument calls helper 180858 and jumps to the common send path at 189262; this is a third-argument bypass, not a channel restriction. Corrected fixtures keep channel modes 0/1/4 separate from this argument. Full channel formatting/recipient behavior remains open. Direct scanning finds exactly the five adjacent call sites linking these three dispatcher targets: two in QVM entry 0 and three in entry 204035. Computed calls and native-to-VM transfers are separate evidence and are not counted as those adjacent QVM calls. ## Completion and remaining work RE-257–259 cover parser/slot composition, selector storage/count-source boundaries and cross-layer dispatcher routes. RE-256 remains open for complete parser/copy/match/runtime conformance and mode/trust behavior. RE-260 tracks interpreter/JIT arithmetic comparison, complete maxclients startup/restart/mutation invariants, native client-command filtering and nonzero chat-mode behavior. The rewrite still needs safe ownership/limits and evidence-backed compatibility contracts before implementation.