1

I'm using OutputDebugStringW in my application. Since WinDBG does not automatically append a newline, I had to add the \n in my output buffer:

#include <iostream>
#include <windows.h>

int main()
{
    wchar_t buff[100];
    ZeroMemory(buff, sizeof(buff));
    _snwprintf_s(buff, sizeof(buff) / sizeof(wchar_t) - 1, L"[test] %d", GetCurrentThreadId());
    buff[wcslen(buff)] = L'\n';
    OutputDebugStringW(buff);

    char buff2[100];
    ZeroMemory(buff2, sizeof(buff2));
    snprintf(buff2, sizeof(buff2), "[test2] %d", GetCurrentThreadId());
    buff2[strlen(buff2)] = '\n';
    OutputDebugStringA(buff2);
}

It works but DbgView prints unexpected things:

  1. For OutputDebugStringA, the newline works fine
  2. For OutputDebugStringW, the newline is not recognized somehow

enter image description here

If I don't append the newline both of them will work just fine. What is the problem here?

daisy
  • 22,498
  • 29
  • 129
  • 265
  • @KenWhite the buffer size is 100 * sizeof(wchar_t) and it's nulled before using it. And I'm only using the first 10 bytes, so there's no such thing as overwriting the NULL terminator. – daisy Jul 10 '21 at 02:34
  • @KenWhite The actual function is much more complicated. The new line has to be added at last, can't use it in the format string, updated question – daisy Jul 10 '21 at 02:59
  • My original thought was overwriting the terminator as well since this certainly looks exactly like that. I wouldn't be shocked to find that in a debug build the entire buffer is filled with a known value like 0xFD so even though it was zeroed before the call it isn't 0 after it. It's easy enough to test, either add a 0 after the `\n` or dump out the bytes of the buffer. – Retired Ninja Jul 10 '21 at 03:33
  • 2
    Turns out it's 0xfefe after the `_snwprintf_s`. Can't find it documented with a quick search, but there's this: https://stackoverflow.com/questions/61135165/snwprintf-s-heap-corruption-error-in-free-call 0xfe is a known value the debug heap uses to fill slack memory: https://stackoverflow.com/a/370362/920069 – Retired Ninja Jul 10 '21 at 03:37
  • @RetiredNinja Interesting... The `snprintf` family is not listed under [`_CrtSetDebugFillThreshold`](https://learn.microsoft.com/is-is/cpp/c-runtime-library/reference/crtsetdebugfillthreshold?view=msvc-160) but that must be it. – dxiv Jul 10 '21 at 04:12
  • @dxiv Nice! I knew I'd seen that list before but couldn't remember where. `sprintf_s` also fills the slack space so it must just be missing from the list. Every other string function seems to be on it so not really unexpected. – Retired Ninja Jul 10 '21 at 04:20
  • @RetiredNinja Inedeed, that's the problem! haven't thought about that before. – daisy Jul 12 '21 at 00:27

1 Answers1

1

As pointed out by Retired Ninja, _snwprintf_s fill out the buffer with another value if the program is build in DEBUG mode. In this case the wchar_t is filled with 0xFEFE:

enter image description here

If I compile the program in Release mode, the string will be NULL terminated.

daisy
  • 22,498
  • 29
  • 129
  • 265
  • You should *never* rely on the prior contents of the buffer, put the null terminator there explicitly. – Mark Ransom Jul 12 '21 at 00:57
  • "*in Release mode, the string will be NULL terminated*" — Except if `wcslen(buff) == 99`, to nitpick. – dxiv Jul 12 '21 at 20:00