Skip to content

fix(game): close DataPack ownership leaks - #1571

Merged
rumblefrog merged 1 commit into
mainfrom
fix/1570-datapack-lifecycle
Sep 18, 2026
Merged

rumblefrog merged 1 commit into
mainfrom
fix/1570-datapack-lifecycle

Conversation

@rumblefrog

Copy link
Copy Markdown
Member

Summary

  • centralize recursive cleanup for ban DataPack values and their nested reason packs
  • clear pending ban state on replacement, cancellation, disconnect, and map end
  • unpack and release callback data before falling back to temporary bans
  • validate stored userids before acting on asynchronous client slots
  • release ban-IP, add-ban, and report callback packs on every return path
  • stop the unsilence flow after transferring pack ownership to TempUnBlock

Testing

  • compiled every top-level sbpp_*.sp plugin with SourcePawn 1.12.0.7253
  • ran an adversarial review of pack ownership, callback layouts, menu cancellation, and client-slot reuse; addressed all findings and completed a final clean review

Closes #1570

@rumblefrog
rumblefrog requested a review from a team September 18, 2026 21:19
@rumblefrog
rumblefrog merged commit 26414b5 into main Sep 18, 2026
1 check passed
@rumblefrog
rumblefrog deleted the fix/1570-datapack-lifecycle branch September 18, 2026 21:21
Rushaway added a commit to srcdslab/sourcebans-pp that referenced this pull request Sep 22, 2026
Companion to the DataPack/Timer/KeyValues handle-leak sweep already on
this branch: upstream sbpp#1571 ("close DataPack ownership
leaks") also fixed a second, distinct bug class in the same functions
that this branch's earlier commits didn't cover - stale client-index
reuse across an async DB round trip.

Every ban command (sm_ban, sm_banip, sm_addban, sm_unban) and the
target-list/time/reason menu flow packs the issuing admin's (and, for
the menu flow, the target's) raw client index into a DataPack or a
per-slot global array, then reads it back after an async SQL query or
after the admin steps through several menu screens. SourceMod reuses a
freed client slot immediately, so if the original admin or target
disconnects in that window and a different player connects into the
same slot, the stale index now points at an unrelated player. The
existing IsClientInGame()/IsClientConnected() guards can't tell "still
the same player" from "someone else now" - only a userid survives a
disconnect/reconnect as a stable identity.

sbpp_comms.sp already carries this pattern (Query_UnBlockSelect reads
adminUserID/targetUserID and resolves via GetClientOfUserId()); this
port brings sbpp_main.sp's ban flow in line with it:

- New g_BanTargetUserId[] parallels g_BanTarget[]/g_BanTime[] so the
  target-list -> time -> reason menu chain (ReasonSelected,
  HackingSelected, ChatHook's own-reason path) re-validates the
  target's userid in PrepareBan() before banning, instead of trusting
  a live-looking but possibly-reused client index.
- CommandBanIp/CommandUnban/CommandAddBan now pack the admin's userid
  instead of the raw index; SelectBanIpCallback, InsertBanIpCallback,
  SelectUnbanCallback, InsertUnbanCallback, SelectAddbanCallback and
  InsertAddbanCallback all resolve it back via GetClientOfUserId()
  before using it for ShowActivity2()/LogAction()/PrintToChat() - the
  existing "admin && IsClientInGame(admin)" guards stay correct as-is
  since GetClientOfUserId() returns 0 for a slot that moved on.
- VerifyInsert (both the success path and the primary-DB-failure path
  that falls back to the SQLite queue) and UTIL_InsertTempBan now
  re-resolve admin from the adminUserId field the pack already
  carried but discarded, and UTIL_InsertTempBan additionally
  re-resolves the target from targetUserId before deciding whether to
  kick them.
- Native_SBBanPlayer's PrepareBan() call passes the target's current
  userid, which is a no-op validation there since the call is
  synchronous - kept for signature consistency and because the native
  is public API third-party plugins call.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Rushaway added a commit to srcdslab/sourcebans-pp that referenced this pull request Sep 22, 2026
…bpp#1571/sbpp#1572

Two bugs surfaced while comparing this fork's Synergy/queue-related
plugin fixes against upstream sbpp#1572 ("use native
auth ids for engine bans") — upstream's diff touches the same
functions and happens to carry the fix for the first one as a side
effect.

1. ProcessQueueCallback re-INSERTs a matured queue row into the main
   `%!s_bans` table, but was building and executing that query against
   `db` — the local SQLite queue connection this callback fires on
   (bound via SQLiteDB.Query(ProcessQueueCallback, ...) in
   ProcessQueue()) — instead of `DB`, the main panel database the
   `%!s_bans` table actually lives on. SQLite has no such table, so the
   INSERT would fail every time; AddedFromSQLiteCallback's
   `results == null` branch just re-arms the temporary in-game hold and
   leaves the row in the queue, so the ban would keep getting
   re-enforced locally but NEVER actually land in the permanent
   database once it came back up after an outage — silently defeating
   the entire point of the offline queue. Switched both the
   `.Format()` call (MySQL vs SQLite escaping rules differ) and the
   final `.Query()` to `DB`. Paired with a `DB == INVALID_HANDLE`
   guard at the top of the callback (matching upstream) so this
   doesn't trade "silently fails forever" for "throws a native error"
   on a cycle where the main DB is still down.

2. AddedFromSQLiteCallback's success path calls
   `RemoveBan(auth, BANFLAG_AUTHID)` with the raw Steam2 identity to
   clear the temporary in-game hold once the real ban lands in the
   main database. But the temp hold was created by SBPP_BanIdentity(),
   which converts to SteamID3 whenever it can (some engines, e.g.
   Synergy, reject the STEAM_ format for the "banid" console command,
   so SBPP_BanIdentity works around it) — a Steam2-only RemoveBan()
   call silently misses a SteamID3-keyed entry, leaving a stale
   temporary ban sitting in SourceMod's cache until it expires on its
   own. Try the SteamID3 form too; removing an identity with no
   matching ban is a harmless no-op.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Memory leaks in sbpp_main.sp — DataPack handles not properly freed in ban/unban/callback paths

1 participant