Skip to content

Commit b014701

Browse files
committed
Hook into Winsock API to detect socket and event handle leaks
1 parent 80efc7e commit b014701

File tree

8 files changed

+208
-10
lines changed

8 files changed

+208
-10
lines changed

setup/version.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
#define VLDVERSION L"2.8.1"
3-
#define VERSION_NUMBER 2,8,1,0
4-
#define VERSION_STRING "2.8.1.0"
2+
#define VLDVERSION L"2.8.2"
3+
#define VERSION_NUMBER 2,8,2,0
4+
#define VERSION_STRING "2.8.2.0"
55
#define VERSION_COPYRIGHT "Copyright (C) 2005-2025"

setup/vld-setup.iss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
33

44
#define MyAppName "Visual Leak Detector"
5-
#define MyAppVersion "2.8.1"
5+
#define MyAppVersion "2.8.2"
66
#define MyAppPublisher "VLD Team"
77
#define MyAppURL "https://github.com/oneiric/vld"
88
#define MyAppRegKey "Software\Visual Leak Detector"

src/dllspatches.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,16 @@ patchentry_t VisualLeakDetector::m_ntdllPatch [] = {
769769
NULL, NULL, NULL
770770
};
771771

772+
patchentry_t VisualLeakDetector::m_winsockPatch [] = {
773+
"socket", NULL, _socket,
774+
"accept", NULL, _accept,
775+
"connect", NULL, _connect,
776+
"closesocket", NULL, _closesocket,
777+
"WSACreateEvent", NULL, _WSACreateEvent,
778+
"WSACloseEvent", NULL, _WSACloseEvent,
779+
NULL, NULL, NULL
780+
};
781+
772782
patchentry_t VisualLeakDetector::m_ole32Patch [] = {
773783
"CoGetMalloc", NULL, _CoGetMalloc,
774784
"CoTaskMemAlloc", NULL, _CoTaskMemAlloc,
@@ -842,6 +852,9 @@ moduleentry_t VisualLeakDetector::m_patchTable [] = {
842852
// NT APIs.
843853
"ntdll.dll", FALSE, 0x0, m_ntdllPatch,
844854

855+
// Winsock APIs.
856+
"ws2_32.dll", FALSE, 0x0, m_winsockPatch,
857+
845858
// COM heap APIs.
846859
"ole32.dll", FALSE, 0x0, m_ole32Patch
847860
};

src/stdafx.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <cassert>
44
#include <cerrno>
55
#include <cstdio>
6+
#include <winsock2.h>
67
#include <shlwapi.h>
78
#if _WIN32_WINNT < 0x0600 // Windows XP or earlier, no GetProcessIdOfThread()
89
#include <winternl.h>

src/vld.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1350,6 +1350,7 @@ VOID VisualLeakDetector::mapBlock (HANDLE heap, LPCVOID mem, SIZE_T size, bool d
13501350
blockinfo->reported = false;
13511351
blockinfo->debugCrtAlloc = debugcrtalloc;
13521352
blockinfo->ucrt = ucrt;
1353+
blockinfo->resource = IS_INTRESOURCE(heap);
13531354

13541355
if (SIZE_MAX - m_totalAlloc > size)
13551356
m_totalAlloc += size;
@@ -1672,6 +1673,9 @@ VOID VisualLeakDetector::reportConfig ()
16721673

16731674
bool VisualLeakDetector::isDebugCrtAlloc( LPCVOID block, blockinfo_t* info )
16741675
{
1676+
if (info->resource)
1677+
return false;
1678+
16751679
// Autodetection allocations from statically linked CRT
16761680
if (!info->debugCrtAlloc) {
16771681
crtdbgblockheader_t* crtheader = (crtdbgblockheader_t*)block;
@@ -1908,7 +1912,7 @@ SIZE_T VisualLeakDetector::reportLeaks (heapinfo_t* heapinfo, bool &firstLeak, S
19081912
info->callStack->dump(m_options & VLD_OPT_TRACE_INTERNAL_FRAMES);
19091913

19101914
// Dump the data in the user data section of the memory block.
1911-
if (m_maxDataDump != 0) {
1915+
if (m_maxDataDump != 0 && size != 0) {
19121916
Report(L" Data:\n");
19131917
if (m_options & VLD_OPT_UNICODE_REPORT) {
19141918
DumpMemoryW(address, (m_maxDataDump < size) ? m_maxDataDump : size);

src/vld.vcxproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@
8888
<DisableSpecificWarnings>4201;4229;4091;4302;4311;4312;4127</DisableSpecificWarnings>
8989
</ClCompile>
9090
<Link>
91-
<AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
91+
<AdditionalDependencies>shlwapi.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
9292
<SubSystem>Windows</SubSystem>
9393
</Link>
9494
<Manifest>
@@ -107,7 +107,7 @@
107107
<EnablePREfast>false</EnablePREfast>
108108
</ClCompile>
109109
<Link>
110-
<AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
110+
<AdditionalDependencies>shlwapi.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
111111
<SubSystem>Windows</SubSystem>
112112
</Link>
113113
<Manifest>
@@ -135,7 +135,7 @@
135135
<OptimizeReferences>true</OptimizeReferences>
136136
<EnableCOMDATFolding>true</EnableCOMDATFolding>
137137
<SubSystem>Windows</SubSystem>
138-
<AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
138+
<AdditionalDependencies>shlwapi.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
139139
</Link>
140140
<Manifest>
141141
<AdditionalManifestFiles>vld.dll.dependency.x86.manifest</AdditionalManifestFiles>
@@ -161,7 +161,7 @@
161161
<EnableCOMDATFolding>true</EnableCOMDATFolding>
162162
<Profile>true</Profile>
163163
<SubSystem>Windows</SubSystem>
164-
<AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
164+
<AdditionalDependencies>shlwapi.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
165165
</Link>
166166
<Manifest>
167167
<AdditionalManifestFiles>vld.dll.dependency.x64.manifest</AdditionalManifestFiles>

src/vld_hooks.cpp

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ extern HANDLE g_currentProcess;
4242
extern CriticalSection g_heapMapLock;
4343
extern DbgHelp g_DbgHelp;
4444

45+
// Heap handles are pointers, hence disjunct from numbers below 65536, which
46+
// hence can serve as pseudo heap handles for tracking non-memory resources.
47+
#define VLD_SOCKET_RESOURCE MAKEINTRESOURCE(1)
48+
#define VLD_WSAEVENT_RESOURCE MAKEINTRESOURCE(2)
49+
4550
////////////////////////////////////////////////////////////////////////////////
4651
//
4752
// Debug CRT and MFC IAT Replacement Functions
@@ -298,6 +303,171 @@ LPVOID VisualLeakDetector::_HeapReAlloc (HANDLE heap, DWORD flags, LPVOID mem, S
298303
return newmem;
299304
}
300305

306+
////////////////////////////////////////////////////////////////////////////////
307+
//
308+
// Winsock IAT Replacement Functions
309+
//
310+
////////////////////////////////////////////////////////////////////////////////
311+
312+
// _socket - Calls to socket are patched through to this function.
313+
// This function invokes the real socket and then calls VLD's allocation
314+
// tracking function with a pseudo heap handle of value VLD_SOCKET_RESOURCE.
315+
//
316+
// Return Value:
317+
//
318+
// Returns the return value from socket.
319+
//
320+
SOCKET VisualLeakDetector::_socket (int af, int type, int protocol)
321+
{
322+
PRINT_HOOKED_FUNCTION2();
323+
// Allocate the resource.
324+
SOCKET s = socket(af, type, protocol);
325+
326+
if ((s == INVALID_SOCKET) || !g_vld.enabled())
327+
return s;
328+
329+
if (!g_DbgHelp.IsLockedByCurrentThread()) // skip dbghelp.dll calls
330+
{
331+
CAPTURE_CONTEXT();
332+
CaptureContext cc(socket, context_);
333+
cc.Set(VLD_SOCKET_RESOURCE, reinterpret_cast<LPVOID>(s), NULL, 0);
334+
}
335+
336+
return s;
337+
}
338+
339+
// _accept - Calls to accept are patched through to this function.
340+
// This function invokes the real accept and then calls VLD's allocation
341+
// tracking function with a pseudo heap handle of value VLD_SOCKET_RESOURCE.
342+
//
343+
// Return Value:
344+
//
345+
// Returns the return value from accept.
346+
//
347+
SOCKET VisualLeakDetector::_accept (SOCKET s, struct sockaddr *addr, int *addrlen)
348+
{
349+
PRINT_HOOKED_FUNCTION2();
350+
// Allocate the resource.
351+
s = accept(s, addr, addrlen);
352+
353+
if ((s == INVALID_SOCKET) || !g_vld.enabled())
354+
return s;
355+
356+
if (!g_DbgHelp.IsLockedByCurrentThread()) // skip dbghelp.dll calls
357+
{
358+
CAPTURE_CONTEXT();
359+
CaptureContext cc(accept, context_);
360+
cc.Set(VLD_SOCKET_RESOURCE, reinterpret_cast<LPVOID>(s), NULL, 0);
361+
}
362+
363+
return s;
364+
}
365+
366+
// _connect - Calls to connect are patched through to this function.
367+
// This function invokes the real connect and then calls VLD's allocation
368+
// tracking function with a pseudo heap handle of value VLD_SOCKET_RESOURCE.
369+
//
370+
// Return Value:
371+
//
372+
// Returns the return value from connect.
373+
//
374+
SOCKET VisualLeakDetector::_connect (SOCKET s, const struct sockaddr *name, int namelen)
375+
{
376+
PRINT_HOOKED_FUNCTION2();
377+
// Allocate the resource.
378+
s = connect(s, name, namelen);
379+
380+
if ((s == INVALID_SOCKET) || !g_vld.enabled())
381+
return s;
382+
383+
if (!g_DbgHelp.IsLockedByCurrentThread()) // skip dbghelp.dll calls
384+
{
385+
CAPTURE_CONTEXT();
386+
CaptureContext cc(connect, context_);
387+
cc.Set(VLD_SOCKET_RESOURCE, reinterpret_cast<LPVOID>(s), NULL, 0);
388+
}
389+
390+
return s;
391+
}
392+
393+
// _closesocket - Calls to closesocket are patched through to this function.
394+
// This function calls VLD's free tracking function with a pseudo heap handle
395+
// of value VLD_SOCKET_RESOURCE and then invokes the real closesocket.
396+
//
397+
// Return Value:
398+
//
399+
// Returns the value returned by closesocket.
400+
//
401+
int VisualLeakDetector::_closesocket (SOCKET s)
402+
{
403+
PRINT_HOOKED_FUNCTION2();
404+
405+
if (!g_DbgHelp.IsLockedByCurrentThread()) // skip dbghelp.dll calls
406+
{
407+
// Record the current frame pointer.
408+
CAPTURE_CONTEXT();
409+
context_.func = reinterpret_cast<UINT_PTR>(closesocket);
410+
411+
// Unmap the resource from the specified pseudo heap.
412+
g_vld.unmapBlock(VLD_SOCKET_RESOURCE, reinterpret_cast<LPVOID>(s), context_);
413+
}
414+
415+
return closesocket(s);
416+
}
417+
418+
// _WSACreateEvent - Calls to WSACreateEvent are patched through to this
419+
// function. This function invokes the real WSACreateEvent and then calls
420+
// VLD's allocation tracking function with a pseudo heap handle of value
421+
// VLD_HANDLE_RESOURCE.
422+
//
423+
// Return Value:
424+
//
425+
// Returns the return value from WSACreateEvent.
426+
//
427+
WSAEVENT VisualLeakDetector::_WSACreateEvent ()
428+
{
429+
PRINT_HOOKED_FUNCTION2();
430+
// Allocate the resource.
431+
WSAEVENT hEvent = WSACreateEvent();
432+
433+
if ((hEvent == WSA_INVALID_EVENT) || !g_vld.enabled())
434+
return hEvent;
435+
436+
if (!g_DbgHelp.IsLockedByCurrentThread()) // skip dbghelp.dll calls
437+
{
438+
CAPTURE_CONTEXT();
439+
CaptureContext cc(WSACreateEvent, context_);
440+
cc.Set(VLD_WSAEVENT_RESOURCE, hEvent, NULL, 0);
441+
}
442+
443+
return hEvent;
444+
}
445+
446+
// _WSACloseEvent - Calls to WSACloseEvent are patched through to this function.
447+
// This function calls VLD's free tracking function with a pseudo heap handle
448+
// of value VLD_HANDLE_RESOURCE and then invokes the real WSACloseEvent.
449+
//
450+
// Return Value:
451+
//
452+
// Returns the value returned by closesocket.
453+
//
454+
BOOL VisualLeakDetector::_WSACloseEvent (WSAEVENT hEvent)
455+
{
456+
PRINT_HOOKED_FUNCTION2();
457+
458+
if (!g_DbgHelp.IsLockedByCurrentThread()) // skip dbghelp.dll calls
459+
{
460+
// Record the current frame pointer.
461+
CAPTURE_CONTEXT();
462+
context_.func = reinterpret_cast<UINT_PTR>(WSACloseEvent);
463+
464+
// Unmap the resource from the specified pseudo heap.
465+
g_vld.unmapBlock(VLD_WSAEVENT_RESOURCE, hEvent, context_);
466+
}
467+
468+
return WSACloseEvent(hEvent);
469+
}
470+
301471
////////////////////////////////////////////////////////////////////////////////
302472
//
303473
// COM IAT Replacement Functions

src/vldint.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ struct blockinfo_t {
111111
bool reported;
112112
bool debugCrtAlloc;
113113
bool ucrt;
114+
bool resource;
114115
};
115116

116117
// BlockMaps map memory blocks (via their addresses) to blockinfo_t structures.
@@ -353,6 +354,14 @@ class VisualLeakDetector : public IMalloc
353354
static BYTE __stdcall _RtlFreeHeap (HANDLE heap, DWORD flags, LPVOID mem);
354355
static LPVOID __stdcall _RtlReAllocateHeap (HANDLE heap, DWORD flags, LPVOID mem, SIZE_T size);
355356

357+
// Winsock IAT replacement functions
358+
static SOCKET __stdcall _socket (int af, int type, int protocol);
359+
static SOCKET __stdcall _accept (SOCKET s, struct sockaddr *addr, int *addrlen);
360+
static SOCKET __stdcall _connect (SOCKET s, const struct sockaddr *name, int namelen);
361+
static int __stdcall _closesocket (SOCKET s);
362+
static WSAEVENT __stdcall _WSACreateEvent ();
363+
static BOOL __stdcall _WSACloseEvent (WSAEVENT hEvent);
364+
356365
// COM IAT replacement functions
357366
static HRESULT __stdcall _CoGetMalloc (DWORD context, LPMALLOC *imalloc);
358367
static LPVOID __stdcall _CoTaskMemAlloc (SIZE_T size);
@@ -379,8 +388,9 @@ class VisualLeakDetector : public IMalloc
379388
static patchentry_t m_kernelbasePatch [];
380389
static patchentry_t m_kernel32Patch [];
381390
static patchentry_t m_ntdllPatch [];
391+
static patchentry_t m_winsockPatch [];
382392
static patchentry_t m_ole32Patch [];
383-
static moduleentry_t m_patchTable [58]; // Table of imports patched for attaching VLD to other modules.
393+
static moduleentry_t m_patchTable [59]; // Table of imports patched for attaching VLD to other modules.
384394
FILE *m_reportFile; // File where the memory leak report may be sent to.
385395
WCHAR m_reportFilePath [MAX_PATH]; // Full path and name of file to send memory leak report to.
386396
const char *m_selfTestFile; // Filename where the memory leak self-test block is leaked.

0 commit comments

Comments
 (0)