diff --git a/server/2015Remote/2015RemoteDlg.cpp b/server/2015Remote/2015RemoteDlg.cpp index c85cc56..103b425 100644 --- a/server/2015Remote/2015RemoteDlg.cpp +++ b/server/2015Remote/2015RemoteDlg.cpp @@ -1689,8 +1689,12 @@ VOID CMy2015RemoteDlg::AddList(CString strIP, CString strAddr, CString strPCName std::string msg = startTime; msg += "|" + std::to_string(ContextObject->GetClientID()); auto signature = signMessage("", (BYTE*)msg.c_str(), msg.length()); - ASSERT(signature.size() <= sizeof(copy.Signature)); - memcpy(copy.Signature, signature.data(), signature.size()); + // 防御性边界保护:signMessage 正常返回 64 字节,但 ASSERT 在 Release 下是空操作, + // 若签名意外超长,无界 memcpy 会越界写 MasterSettings.Signature[64]。这里强制截断。 + size_t sigLen = signature.size(); + if (sigLen > sizeof(copy.Signature)) + sigLen = sizeof(copy.Signature); + memcpy(copy.Signature, signature.data(), sigLen); LeaveCriticalSection(&m_cs); Mprintf("主机[%s]上线: %s[%s][%s]\n", v[RES_CLIENT_PUBIP].empty() ? strIP : v[RES_CLIENT_PUBIP].c_str(), std::to_string(id).c_str(), loc, groupName.c_str()); @@ -6264,7 +6268,13 @@ VOID CMy2015RemoteDlg::MessageHandle(CONTEXT_OBJECT* ContextObject) case TOKEN_HEARTBEAT: case 137: // 心跳【L】 ContextObject->HeartbeatRecvMs.store(GetUnixMs(), std::memory_order_relaxed); - g_2015RemoteDlg->PostMessageA(WM_UPDATE_ACTIVEWND, 0, (LPARAM)ContextObject); + // 为延迟消息占一个引用计数:RemoveStaleContext 会等 IoRefCount==0 才回收对象, + // 否则这里经消息队列传出的裸指针在 UI 线程消费时可能已被替补抖动回收复用(use-after-free)。 + // UpdateUserEvent 处理完后配对 fetch_sub 释放。 + ContextObject->IoRefCount.fetch_add(1); + if (!g_2015RemoteDlg->PostMessageA(WM_UPDATE_ACTIVEWND, 0, (LPARAM)ContextObject)) { + ContextObject->IoRefCount.fetch_sub(1); // 投递失败,退回引用,避免泄漏 + } break; case TOKEN_SCREEN_PREVIEW_RSP: { // 屏幕预览响应:把整个包 + 来源 clientId 一并堆分配,转给主线程处理 @@ -6816,8 +6826,12 @@ bool CMy2015RemoteDlg::PromoteStandby(uint64_t clientID) std::string msg = startTime.GetString(); msg += "|" + std::to_string(clientID); auto signature = signMessage("", (BYTE*)msg.c_str(), msg.length()); - ASSERT(signature.size() <= sizeof(copy.Signature)); - memcpy(copy.Signature, signature.data(), signature.size()); + // 防御性边界保护:signMessage 正常返回 64 字节,但 ASSERT 在 Release 下是空操作, + // 若签名意外超长,无界 memcpy 会越界写 MasterSettings.Signature[64]。这里强制截断。 + size_t sigLen = signature.size(); + if (sigLen > sizeof(copy.Signature)) + sigLen = sizeof(copy.Signature); + memcpy(copy.Signature, signature.data(), sigLen); SendMasterSettings(standby, copy); return true; } @@ -6874,7 +6888,12 @@ LRESULT CMy2015RemoteDlg::OnUserOfflineMsg(WPARAM wParam, LPARAM lParam) LRESULT CMy2015RemoteDlg::UpdateUserEvent(WPARAM wParam, LPARAM lParam) { CONTEXT_OBJECT* ctx = (CONTEXT_OBJECT*)lParam; - UpdateActiveWindow(ctx); + if (ctx) { + UpdateActiveWindow(ctx); + // 释放 TOKEN_HEARTBEAT 投递延迟消息时占用的引用计数(与那里的 fetch_add 配对)。 + // 必须在 UpdateActiveWindow 完全返回后再释放,保证整个处理期间对象不被回收。 + ctx->IoRefCount.fetch_sub(1); + } return S_OK; } @@ -7211,7 +7230,14 @@ void CMy2015RemoteDlg::UpdateActiveWindow(CONTEXT_OBJECT* ctx) HeartbeatACK ack = { hb.Time, (char)authStatus, (char)isTrail }; if (authorized) { std::string authorization = isV2 ? LoadLicenseAuthorization(hb.SN) : BuildV1Authorization(hb.SN, true); - memcpy(ack.Authorization, authorization.c_str(), authorization.length()); + // 防御性边界保护:正常 authorization 约 150~200 字节;一旦异常超长,无界 memcpy + // 会越界写 HeartbeatACK.Authorization[200] 并踩栈金丝雀(0xC0000409)。 + // Release 下强制截断并保证 NUL 结尾。 + size_t authLen = authorization.length(); + if (authLen >= sizeof(ack.Authorization)) + authLen = sizeof(ack.Authorization) - 1; + memcpy(ack.Authorization, authorization.c_str(), authLen); + ack.Authorization[authLen] = '\0'; } // 在 send 前一刻填进处理耗时(毫秒)。GetUnixMs 底层是 chrono::system_clock, // 在 VS2019+ MSVC 上精度亚微秒(截断到 ms),两次作差误差 ≤ 1ms,能准确捕获