I stumbled over this because one of my little tools accumulated working space over time, so I investigated:
include \masm32\MasmBasic\MasmBasic.inc
.code
MbwRec2 proc
push ecx
call MbBufferGet ; we need a slot in the circular buffer
mov ecx, [esp+8] ; source string
push 0 ; 2 args for MbBufferFix
push eax
MemState("leaked kBytes in: %i\n")
jecxz @F ; return a Null$
invoke MultiByteToWideChar, CP_UTF8, 0, ecx, -1, eax, MbBufSize/8
dec eax
shl eax, 1 ; returned chars, we need bytes
add [esp], eax
@@:
MemState("leaked kBytes out: %i\n")
call MbBufferFix
pop ecx
retn 4
MbwRec2 endp
Init
push 999 ; 1000 iterations
.Repeat
push Chr$("Hello World, how are you today?")
call MbwRec2
dec stack
.Until Sign?
EndOfCode
The
MemState macro is a simple way to check if memory got lost between two points in code; it prints only if there is a loss. For the 1,000 iterations above it spits out this:
leaked kBytes out: 4
leaked kBytes in: 20
leaked kBytes out: 4
leaked kBytes out: 4
leaked kBytes out: 4
leaked kBytes out: 4
leaked kBytes out: 4
leaked kBytes out: 4
leaked kBytes out: 4
leaked kBytes out: 4
leaked kBytes out: 4
leaked kBytes out: 4
leaked kBytes out: 4
leaked kBytes out: 4
leaked kBytes out: 4
leaked kBytes out: 4
leaked kBytes out: 4
leaked kBytes out: 4
That is 4*17=68 kBytes lost, a significant leak, especially since the converted string is plain Ascii and just 21 bytes long.
Tested on Win7-64, WinXP and Win10, source and exe attached.
P.S.: When testing with 10 Mio iterations with 1024 byte strings, it turns out that after a while the API stops allocating more, at about 500kBytes; weird logic
