From 33f5ed9ef58f26c6b3b52cf605dd4311d658a60d Mon Sep 17 00:00:00 2001 From: salmonik Date: Fri, 6 Nov 2026 16:09:55 +0100 Subject: Fix audit findings: crash safety, correctness, rendering, performance Crash/UB: - gate every hook behind an init-stage flag until config/surface/input are ready - publish module maps as immutable atomic snapshots instead of reallocating shared vectors under unlocked readers - validate the %ebp walk in the RandomSeed/RandomFloat hooks against the stack - bounds-check every RecvTable/RecvProp/string dereference with module_range() - preserve the Itanium vtable header when cloning (client, CInput, model render, studio render) - reject negative usercmd sequence numbers; guard config_load against a null section target; cap the bone parent walk; check mprotect; drop PROT_EXEC on the GOT patch; absorb server.so .bss tail Correctness: - filter dormant entities across ESP, aimbot, triggerbot, melee, items and projectiles (slot self-tested against the local player) - reset the server-health latch per map and only latch on valid model indices - bone cache sized for the 128-target scan; set valid=false on every failure; head_position keeps searching past an invalid bone - vischeck steps past glass by probing until clear, and ignores entities owned by the target (held weapons, bone followers) - aimbot: maxs no longer disables it, AABB-aware FOV prefilter, lock hysteresis, lead point re-checked for FOV and visibility - spread: seed hook arms on the no-spread toggle alone; engine toggle only controls the RandomFloat zeroing - resolve m_hOwnerEntity/m_hOwner; NPC lead velocity from the server entity - vector_angles pitch range, point_ground normal test, underwater jump+duck no longer skips combat, semi-auto press/release edge for synthetic fire - item/projectile markers and blast rings refresh origin per frame while discovery stays at 10 Hz; trajectory uses the real m_DmgRadius and honours the blast-radius toggle Rendering/GUI: - one overlay draw per engine frame (FRAME_RENDER_START boundary), fixing alpha stacking and the speed-graph flicker - clipped lines/outlines and ellipsised text instead of drawing past the content pane or shifting over neighbours - dropdown dismissal consumes the click; hover ignores hidden controls - chams: reset AlphaModulate, reference shared materials once, restore prop tint colour; fonts created once per handle - projection uses Source's 4:3 aspect scaling and roll; screen size refreshed each frame; FOV circle gated on the aim toggles, not the bind Performance: - TTL caches for NPC relation, friendly/neutral and damage class - model names memoised per model index - entity pointer -> index cache for the triggerbot (was a 4096-slot walk/tick) Build: - real Makefile with -MMD dependency tracking, debug/release objects and an always-relink rule; build.sh kept as a thin wrapper --- src/cinput.cpp | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) (limited to 'src/cinput.cpp') diff --git a/src/cinput.cpp b/src/cinput.cpp index 08686a1..45e2b04 100644 --- a/src/cinput.cpp +++ b/src/cinput.cpp @@ -53,23 +53,25 @@ static constexpr size_t VERIFIED_STRIDE = 0x58; static constexpr size_t VERIFIED_CRC_OFF = 0x54; CUserCmd *get_usercmd(int seq) { - if (!g_input) + // Negative sequence numbers would make `seq % 90` negative and the size_t + // cast a huge index; reject before forming the pointer. + if (!g_input || seq < 0) return nullptr; uintptr_t cmds = *(uintptr_t *)(g_input + CMDOFF_M_PCOMMANDS); if (!cmds) return nullptr; - auto *cmd = (CUserCmd *)(cmds + (size_t)(seq % 90) * 0x54); + auto *cmd = (CUserCmd *)(cmds + (size_t)((unsigned)seq % 90u) * 0x54); if (cmd->command_number != seq) return nullptr; return cmd; } void fix_verified_crc(int seq, uint32_t crc) { - if (!g_input) + if (!g_input || seq < 0) return; uintptr_t verified = *(uintptr_t *)(g_input + CMDOFF_M_PVERIFIED); if (verified) - *(uint32_t *)(verified + (size_t)(seq % 90) * VERIFIED_STRIDE + + *(uint32_t *)(verified + (size_t)((unsigned)seq % 90u) * VERIFIED_STRIDE + VERIFIED_CRC_OFF) = crc; } @@ -92,6 +94,11 @@ static InputExtraMouseFn oInputExtraMouseSample = nullptr; // commands. While the GUI is open it must not touch the view; movement still // comes through CreateMove. static void hkdInputExtraMouseSample(void *self, float frametime, bool active) { + if (!init_live()) { + if (oInputExtraMouseSample) + oInputExtraMouseSample(self, frametime, active); + return; + } if (gui_is_open()) return; if (oInputExtraMouseSample) @@ -109,6 +116,11 @@ void input_clear_mouse_states() { static void hkdInputCreateMove(void *self, int seq, float frametime, bool active) { + if (!init_live()) { + if (oInputCreateMove) + oInputCreateMove(self, seq, frametime, active); + return; + } // Mouse deltas are accumulated between ticks. With the custom GUI open we // must discard them before CInput::CreateMove applies mouse look, otherwise // the view moves for one frame and snaps back when do_move freezes it. @@ -151,8 +163,11 @@ static bool install_input_hook() { if (!vt) return false; constexpr size_t N = 64; - void **nvt = new void *[N]; - memcpy(nvt, vt, N * sizeof(void *)); + void **nvt = vtable_clone((void *)g_input, N); + if (!nvt) { + logf("CInput vtable clone failed"); + return false; + } oInputCreateMove = (InputCreateMoveFn)vt[3]; nvt[3] = (void *)&hkdInputCreateMove; if (N > 4) { @@ -173,7 +188,7 @@ static bool install_input_hook() { bool find_input_global() { void **vt = VTABLE(g_iface.client); uintptr_t cm = (uintptr_t)vt[21]; // CreateMove slot - if (!g_client.contains(cm)) { + if (!mod_client().contains(cm)) { logf("CreateMove %p outside client module", (void *)cm); return false; } @@ -189,7 +204,7 @@ bool find_input_global() { break; } } - if (!anchor || !g_client.contains(anchor)) { + if (!anchor || !mod_client().contains(anchor)) { logf("PIC anchor not found in CreateMove"); return false; } @@ -214,16 +229,16 @@ bool find_input_global() { uint32_t disp; memcpy(&disp, b + i + 2, 4); uintptr_t var = anchor + disp; - if (!g_client.contains(var)) + if (!mod_client().contains(var)) continue; uintptr_t input = *(uintptr_t *)var; - if (!input || !g_client.contains(input)) + if (!input || !mod_client().contains(input)) continue; uintptr_t vtbl = *(uintptr_t *)input; - if (!vtbl || !g_client.contains(vtbl)) + if (!vtbl || !mod_client().contains(vtbl)) continue; uintptr_t f3 = *(uintptr_t *)(vtbl + 3 * sizeof(void *)); - if (!g_client.contains(f3)) + if (!mod_client().contains(f3)) continue; logd("CInput* = %#lx (ptr var %#lx, vfunc[3] %#lx)", (unsigned long)input, (unsigned long)var, (unsigned long)f3); -- cgit v1.2.3