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/world.cpp | 45 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) (limited to 'src/world.cpp') diff --git a/src/world.cpp b/src/world.cpp index ea186b4..a0576ab 100644 --- a/src/world.cpp +++ b/src/world.cpp @@ -633,6 +633,10 @@ bool load_png_rgba(const char *path, std::vector &pixels, } std::vector g_sky_pixels; +// Pristine copy of the decoded PNG. HDR compensation is applied to +// g_sky_pixels in place, so without this the linearised/darkened bytes bled +// into the next map when it used the LDR sky shader. +std::vector g_sky_pixels_src; int g_sky_px_w = 0, g_sky_px_h = 0; // ITextureRegenerator has two virtuals: RegenerateTextureBits then Release. @@ -717,6 +721,28 @@ bool is_sky_material(const char *name, const char *group) { strncmp(name, "skybox/", 7) == 0; } +// ITexture reference counting (material system header order, matching the +// slots already used here: SetTextureRegenerator = 12, Download = 13): +// 10 = IncrementReferenceCount, 11 = DecrementReferenceCount +// The engine owns the original textures; holding a reference keeps them valid +// between "capture" and "restore" instead of leaving a dangling pointer. +static void texture_addref(void *tex) { + if (!tex) + return; + using Fn = void (*)(void *); + vfunc(tex, 10)(tex); +} + +void release_sky_originals() { + for (auto &r : g_sky_orig) { + if (!r.texture) + continue; + using Fn = void (*)(void *); + vfunc(r.texture, 11)(r.texture); + } + g_sky_orig.clear(); +} + void record_orig(const char *material, const char *var, const char *value, void *texture) { if (!material || !var) @@ -725,6 +751,7 @@ void record_orig(const char *material, const char *var, const char *value, if (r.material == material && r.var == var) return; } + texture_addref(texture); g_sky_orig.push_back( {material, var, value ? value : "", texture}); } @@ -750,6 +777,10 @@ int apply_sky_texture() { // where the materials are definitely loaded, and re-upload if needed. bool hdr = sky_uses_hdr_shader(); if (hdr && !g_sky_compensated && !g_sky_pixels.empty()) { + // Always start from the untouched decode so repeated HDR passes cannot + // compound the gamma conversion. + if (!g_sky_pixels_src.empty()) + g_sky_pixels = g_sky_pixels_src; for (size_t i = 0; i + 3 < g_sky_pixels.size(); i += 4) { for (int c = 0; c < 3; c++) { float v = (float)g_sky_pixels[i + (size_t)c] / 255.0f; @@ -822,7 +853,7 @@ void restore_sky_texture() { } void *ms = material_system(); if (!ms) { - g_sky_orig.clear(); + release_sky_originals(); g_sky_applied = false; return; } @@ -833,7 +864,7 @@ void restore_sky_texture() { auto count = vfunc(ms, MATSYS_GetNumMaterials); int total = count(ms); if (total <= 0 || total > 200000) { - g_sky_orig.clear(); + release_sky_originals(); g_sky_applied = false; return; } @@ -866,7 +897,7 @@ void restore_sky_texture() { vfunc(var, 5)(var, r.value.c_str()); } } - g_sky_orig.clear(); + release_sky_originals(); g_sky_applied = false; } @@ -925,6 +956,7 @@ void load_sky_texture() { return; } g_sky_pixels.swap(pixels); + g_sky_pixels_src = g_sky_pixels; g_sky_px_w = w; g_sky_px_h = h; @@ -960,6 +992,13 @@ void skybox_invalidate() { void skybox_reload_image() { restore_sky_texture(); + // Detach the regenerator before dropping the handle: the old texture may + // outlive it and must not call back into a regenerator whose pixels were + // replaced. + if (g_sky_tex) { + using SetRegenFn = void (*)(void *, void *); + vfunc(g_sky_tex, 12)(g_sky_tex, nullptr); + } g_sky_tex = nullptr; g_sky_tex_tried = false; g_sky_reload = true; -- cgit v1.2.3