mınxomaτ mınxomaτ - 1 year ago 45
C++ Question

Passing a reference to a string works only if copied into a temporary variable first

I'm currently writing code for a size-coding entry. I say this because it causes some restrictions as to what I can do. So if you read the code and ask yourself e.g. "why use wsprintf, not just sprintf", it has a reason. No CRT can be linked. Only WinAPI libs are allowed. The objective is to generate the least amount of assembly instructions going into the linker. With that said, onto the problem.

Prerequisites



This macro:

#define GLExt(a) a(wglGetProcAddress(#a))


is used as a shorthand to call OpenGL extension functions previously declared. This problem only concerns one function, which has this signature:

extern "C" {
typedef void(__stdcall*glShaderSource)
(unsigned shader, int count, char** string, const int *length);
}


The Problem



I load a string constant as a template and use wsprintf to fill in the values. In this small example of the issue it's simply the elapsed time in ms. The following code works fine. I've inserted the generated assembly above the respective LOCs:

const auto starttime = GetTickCount();

do {
char *pflog;

//~ call DWORD PTR __imp__GetTickCount@0
//~ sub eax, DWORD PTR _starttime$1$[esp+24]
//~ push eax
//~ push OFFSET <text from data>
//~ push DWORD PTR _pflog$1[esp+32]
//~ call DWORD PTR __imp__wsprintfA

wsprintf(pflog, "void main(void){int foo=%d;}", GetTickCount() - starttime);

//~ mov eax, DWORD PTR _pflog$1[esp+36]
//~ add esp, 12 ; 0000000cH
//~ mov DWORD PTR _blorg$2[esp+24], eax

char *blorg = pflog;

//~ lea eax, DWORD PTR _blorg$2[esp+24]
//~ push 0
//~ push eax
//~ push 1
//~ push ebp
//~ push OFFSET <glShaderSource>
//~ call esi
//~ call eax

GLExt(glShaderSource)(s, 1, &blorg, 0);
}


However, if I try to use
pflog
directly, the code compiles without complaint but crashes on the first call to
glShaderSource
. Here's is the annotated version of that:

const auto starttime = GetTickCount();

do {
char *pflog;

//~ call DWORD PTR __imp__GetTickCount@0
//~ sub eax, DWORD PTR _starttime$1$[esp+24]
//~ push eax
//~ push OFFSET <text from data>
//~ push DWORD PTR _pflog$1[esp+32]
//~ call DWORD PTR __imp__wsprintfA
//~ add esp, 12

wsprintf(pflog, "void main(void){int foo=%d;}", GetTickCount() - starttime);

//~ lea eax, DWORD PTR _pflog$1[esp+24]
//~ push 0
//~ push eax
//~ push 1
//~ push ebp
//~ push OFFSET <glShaderSource>
//~ call esi
//~ call eax

GLExt(glShaderSource)(s, 1, &pflog, 0);
} while (1);


I'm a bit stumped. As far as I can tell the variables are accessed in the same way at the same offsets in both versions (apart from the copy). Why is one version unexpectedly crashing, or rather: Why does the temporary variable make a difference?

Answer Source
char *pflog;

wsprintf(pflog, "void main(void){int foo=%d;}", GetTickCount() - starttime);

Whatever happens after this is doomed to fail. Unfortunately, it did not fail right in the second line. You are writing to random memory you do not own. You cannot just write a string to an uninitialized character pointer. You need to allocate the memory behind it, by either using new (preferred in C++) or any of the alloc functions.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download