<< PreviousNext >>

Answer to Dynamic function calls

Since we are copying data to the top of the stack while still keeping the old esp, we are actually exceeding our stack space for a while. This would be fine as long as no other code depended on the esp to be correct (we know what we're doing, we can handle it). However, since we call memcpy to do the copying for us, memcpy will use the space "above" esp, the same space memcpy will use for its stack frame. Bad.

Why is the limit 3?

To understand why the limit is 3 elements, we should take a look at the generated machine code. The line memcpy(to, params, pSize) will be compiled to something like this:

push pSize;
push params;
push to;
call memcpy;
add esp, 12;

So when calling memcpy, we will consume 3`sizeof(void)` bytes on the stack for parameters. What happens next is that call actually does push eip (the instruction pointer) before jumping to the function. So the first 3 elements on the stack are occupied by parameters to memcpy, and the fourth is occupied by the return address of memcpy.

When calling a function with one parameter, memcpy will only overwrite the pSize parameter. This is possibly bad since we will write into too much memory, but it works for some reason, possibly memcpy keeps its own copy deeper down the stack. Two and three parameters and it will overwrite the 'src' and 'dest' pointers. This is not a problem since it will keep temporary pointers somewhere deeper down the stack, which are not overwritten.

The fourth parameter will however cause the return address to be overwritten, which will cause a crash when memcpy returns (if nothing else breaks before). And that is where I saw the bug.

Solution

One possible solution is to simply implement memcpy inline by doing for example:

for (nat i = 0; i < count; i++)
  to[i] = params[i];

This does not depend on any extra memory above esp (well, it depends on how the compiler treats new scopes, but most compilers allocate memory for all scopes at once).

Another idea would be to adjust esp in the first asm-block, so that the memory used by the call to memcpy does not collide with the memory we want to use for our call. The reason I did not do that is because I have no idea about how the compiler refers to local variables. It might choose to do so by using the esp pointer, and in that case things would be really bad. I have not tried this solution though.

Problem

Comments

New comment

You can use GitHub flavored markdown here. Parsed by Parsedown, which does not support all of GitHub's features. For example, specifying the language of code listings is not supported.

Name: