Fix crash while unloading modules

The bug allowed unprivileged users to trigger use-after-free while
unloading modules

Note that the fix requires a slight API change for OnEmbeddedWebRequest:
now the callers (webadmin is the only known caller of it) call it via
DoEmbeddedWebRequest() instead. The modules which implement this hook
need no changes.

CVE-2026-82373

Found by Claude from Anthropic, reported by Ada Logics.
This commit is contained in:
Alexey Sokolov committed 2026-09-12 19:08:21 +01:00
1 parent d9b392afd9
commit 4b88c64c6e
7 files changed
+143 -111

No files matched your search

+10 -19
View File
@@ -21,8 +21,8 @@
#include <znc/IRCNetwork.h>
#include <znc/Query.h>
using std::set;
using std::map;
using std::set;
using std::vector;
#define CALLMOD(MOD, CLIENT, USER, NETWORK, FUNC) \
@@ -30,10 +30,9 @@ using std::vector;
CModule* pModule = nullptr; \
if (NETWORK && (pModule = (NETWORK)->GetModules().FindModule(MOD))) { \
try { \
CClient* pOldClient = pModule->GetClient(); \
pModule->SetClient(CLIENT); \
CModCallProtector Inside(*pModule); \
CTemporaryModClient TempClient(*pModule, CLIENT); \
pModule->FUNC; \
pModule->SetClient(pOldClient); \
} catch (const CModule::EModException& e) { \
if (e == CModule::UNLOAD) { \
(NETWORK)->GetModules().UnloadModule(MOD); \
@@ -41,13 +40,10 @@ using std::vector;
} \
} else if ((pModule = (USER)->GetModules().FindModule(MOD))) { \
try { \
CClient* pOldClient = pModule->GetClient(); \
CIRCNetwork* pOldNetwork = pModule->GetNetwork(); \
pModule->SetClient(CLIENT); \
pModule->SetNetwork(NETWORK); \
CModCallProtector Inside(*pModule); \
CTemporaryModClient TempClient(*pModule, CLIENT); \
CTemporaryModNetwork TempNetwork(*pModule, NETWORK); \
pModule->FUNC; \
pModule->SetClient(pOldClient); \
pModule->SetNetwork(pOldNetwork); \
} catch (const CModule::EModException& e) { \
if (e == CModule::UNLOAD) { \
(USER)->GetModules().UnloadModule(MOD); \
@@ -55,16 +51,11 @@ using std::vector;
} \
} else if ((pModule = CZNC::Get().GetModules().FindModule(MOD))) { \
try { \
CClient* pOldClient = pModule->GetClient(); \
CIRCNetwork* pOldNetwork = pModule->GetNetwork(); \
CUser* pOldUser = pModule->GetUser(); \
pModule->SetClient(CLIENT); \
pModule->SetNetwork(NETWORK); \
pModule->SetUser(USER); \
CModCallProtector Inside(*pModule); \
CTemporaryModClient TempClient(*pModule, CLIENT); \
CTemporaryModNetwork TempNetwork(*pModule, NETWORK); \
CTemporaryModUser TempUser(*pModule, USER); \
pModule->FUNC; \
pModule->SetClient(pOldClient); \
pModule->SetNetwork(pOldNetwork); \
pModule->SetUser(pOldUser); \
} catch (const CModule::EModException& e) { \
if (e == CModule::UNLOAD) { \
CZNC::Get().GetModules().UnloadModule(MOD); \