urt-rs / docs /research /GAME_CVARS.md
nmarshall's picture
Recover authentication selectors command checks and mode paths
9ac939d
|
Raw History Blame Contribute Delete
8.24 kB

Game cvar registry and authentication announcement callers

Static pass, 2026-10-11. Reproduce with python3 scripts/analyze_game_cvars.py; check with python3 scripts/test_game_cvars.py. Generated evidence includes every row of the recovered main qagame registry, 126 decoded-instruction registration argument traces, registration/update function partitions, three direct announcement callers, 11 verbosity references and both Linux ELF64 Cvar_Register/Cvar_Update graphs. Inputs are source/disassembly-hash verified. No original game process or complete QVM executes.

CVAR-001: 126-row main registry

Qagame registration function 2677 initializes its table pointer to VM address 4, increments it by 28 bytes per row, and compares the row index against the initialized word at 3532, whose value is 126. Thus 4 + 126 * 28 = 3532: the table ends exactly at the count word. Update function 2950 uses the same start, stride and count address.

Each row contains seven 32-bit words. The registration argument slice loads mirror pointer at offset 0, name pointer at 4, default pointer at 8 and flags at 12, forwarding them to trap 3 (encoded CALL target -4). Offset 16 is initialized modification-count cache and is assigned from mirror offset 4 when a mirror exists. Offsets 20 and 24 are retained as raw words pending complete behavioral analysis; their presence in control flow does not by itself establish final domain names.

The report records all 126 unique names, defaults, raw flags, mirror addresses, original string addresses and source table/file offsets. Empty defaults are valid and preserved. Each row’s actual registration instruction slice is evaluated with initialized table memory and checked against that row’s decoded fields. It records the call without invoking registration or applying cvar policy.

This is the main table, not every engine or game cvar. Additional direct registration sites exist elsewhere, including registration calls after this table loop and other subsystems. Those and dynamically generated names need separate coverage. Flags remain numeric here; do not infer archive/serverinfo/readonly meanings solely from an upstream enum without verifying this native fork. Defaults are supplied registration arguments, not effective settings after configs, command-line values or native registration merges.

Authentication-related rows include:

Name Supplied default Raw flags
auth 0 68
auth_status init 68
auth_enable 1 1
auth_cheaters 1 1
auth_tags 1 1
auth_notoriety 0 1
auth_groups Empty 1
auth_owners Empty 1
auth_verbosity 1 1
auth_cmds_friend bigtext say 1
auth_cmds_member bigtext say map restart 1
auth_cmds_referee bigtext say map restart swapteams shuffleteams slap reload 1

Command-list strings do not establish how commands are authorized, parsed or overridden. That policy remains research work.

CVAR-002: native mirror and auth_verbosity identity

auth_verbosity is registry row 122 at VM address 3420. Its mirror pointer is 35002960. Both native Cvar_Update graphs copy a native cvar’s numeric words into mirror offsets 8 and 12, and its string into offset 16 with a 256-byte bound. The native integer word is stored at mirror offset 12. Therefore the announcer's global 35002972 = 35002960 + 12 is the integer view of auth_verbosity.

Native Cvar_Update reads the mirror handle at offset zero and checks it against the native index count. It compares modification counts and skips unchanged values; a missing native string also skips the update. On the update path it assigns mirror modification count at offset 4 before checking the string length, copies the string at offset 16, then copies numeric words to offsets 8 and 12. The visible mirror view spans 272 bytes; this does not establish every surrounding allocation field.

strlen + 1 must be unsigned at most 256, so at most 255 non-NUL bytes fit on the normal path. An invalid handle or longer string calls Com_Error(1, ...). The earlier error boundary establishes frame abort on the ordinary Linux mode-1 path; post-call instructions in a syntactic graph are not proof that these failures return. Registration graphs are retained for subsequent merge/default/flag policy recovery, rather than claimed completely specified here.

The announcer’s fallback behavior now has a cvar identity: auth_verbosity=1 selects broadcast print, =2 selects broadcast chat, and other values select no fallback output. These are exact comparisons in the helper. This does not imply that effective runtime verbosity is always its default.

The previously traced positive gate at 35017616 is a different address. It is not the integer mirror of auth_enable (35003256 + 12), auth (35003848 + 12), or auth_verbosity. Its initialization and role remain to be traced; do not rename it from its apparent effect alone.

CVAR-003: three direct announcement callers

A complete adjacent CONST-165352/CALL scan finds these three qagame call sites:

Function entry / CALL instruction Arguments and bounded interpretation
1264 / 1379 Selected record, mode 2, caller’s third-argument object. Its usage/error strings identify an auth-whois query helper; successful tail forwards those values unchanged.
166169 / 166744 Response-selected record, mode 0, zero third argument. This local branch calls only when auth_verbosity is signed positive.
174399 / 175025 Begin-selected record, mode 1, zero third argument, after the one-shot/pending gates already documented.

Query helper 1264 starts with the positive-35017616 gate. An empty selector chooses usage output; a failed selection chooses unknown-client output. For a zero caller object these paths use diagnostic helper 5123; for a nonzero caller object they calculate its recipient through object offset 520 and client stride 6336, then send chat commands. The success path invokes the detailed mode-two announcer. Exact selector grammar and identity lookup in helper 253561 remain open. Usage strings alone do not prove the command dispatch/registration route or caller permissions.

The authentication response slice tests auth_verbosity with <= 0, so a positive value other than one or two still invokes mode-zero helper 165352; its fallback then produces no output. Five synthetic verbosity inputs -1/0/1/2/3 verify this local distinction. They do not execute preceding response parsing or validate a packet’s trust/identity.

The 11 literal integer-view references occur in entries 171, 165352, 166169 and 171668. Several are outside the announcer and affect initialization/response/private message paths. All are retained as instruction windows rather than prematurely interpreted as a complete global usage specification. The registry pointer supplies an indirect update route even though no literal CONST-35002960 occurs in qagame code.

Completion and next work

Follow-up: CHG-001–003 recover the visible word-20/24 behavior, modification-cache gates, four numeric setting branches and 28 direct registration sites (23 exact constant-argument traces). Raw field values remain preserved; complete native policy and all row-word mutations are still open.

RE-245–247 cover the main registry extraction, native mirror/verbosity connection and bounded direct caller argument/gate evidence. RE-230/244 remain open for full caller/object/global-update/reset and rendered-output behavior. RE-248 adds the remaining cvar registrations, native flag/merge/update policy, table word-20/24 semantics, positive-gate initialization and auth-whois selector/dispatch/permission recovery. The next implementation contract should use recovered names and fields rather than inferred global labels.

Further follow-up: QUERY-001–003 connects auth-whois to both direct dispatch routes, recovers its selector helpers and role-check policy, and assigns the positive gate a bounded internal authentication-mode role. Full selector/permission/configuration lifecycle remains open.