views:

338

answers:

8

I came across the following weird chunk of code.Imagine you have the following typedef:

typedef int (*MyFunctionPointer)(int param_1, int param_2);

And then , in a function , we are trying to run a function from a DLL in the following way:

LPCWSTR DllFileName;    //Path to the dll stored here
LPCSTR _FunctionName;   // (mangled) name of the function I want to test

MyFunctionPointer functionPointer;

HINSTANCE hInstLibrary = LoadLibrary( DllFileName );
FARPROC functionAddress = GetProcAddress( hInstLibrary, _FunctionName );

functionPointer = (MyFunctionPointer) functionAddress;

//The values are arbitrary
int a = 5;
int b = 10;
int result = 0;

result = functionPointer( a, b );  //Possible error?

The problem is, that there isn't any way of knowing if the functon whose address we got with LoadLibrary takes two integer arguments.The dll name is provided by the user at runtime, then the names of the exported functions are listed and the user selects the one to test ( again, at runtime :S:S ). So, by doing the function call in the last line, aren't we opening the door to possible stack corruption? I know that this compiles, but what sort of run-time error is going to occur in the case that we are passing wrong arguments to the function we are pointing to?

+4  A: 

I'm afraid there is no way to know - the programmer is required to know the prototype beforehand when getting the function pointer and using it.

If you don't know the prototype beforehand then I guess you need to implement some sort of protocol with the DLL where you can enumerate any function names and their parameters by calling known functions in the DLL. Of course, the DLL needs to be written to comply with this protocol.

Cthutu
That's not my point: I know I shouldn't call the function like that, but exactly what sort of error it it going to cause? Is it something that can be handled, or am I just going to mess up my call stack and crash the application.
Emil D
You cannot handle it. You will might not mess up the stack if the function is a C function but the parameters will be wrong, which of course may cause crashes.
Cthutu
A: 

Generally if you are calling LoadLibrary and GetProcByAddrees you have documentation that tells you the prototype. Even more commonly like with all of the windows.dll you are provided a header file. While this will cause an error if wrong its usually very easy to observe and not the kind of error that will sneak into production.

rerun
I have reasons to believe that the guy who wrote this didn't have ANY information about the function being called, apart from its name.So, in the case we are indeedpassing the wrong number of arguments, what is going to happen?
Emil D
Any thing can happen. What if the function takes three args or a string and an int? Calling a function with the wrong number/type of arguments is undefined.
lilburne
+1  A: 

I do not think so, it is a good question, the only provision is that you MUST know what the parameters are for the function pointer to work, if you don't and blindly stuff the parameters and call it, it will crash or jump off into the woods never to be seen again... It is up to the programmer to convey the message on what the function expects and the type of parameters, luckily you could disassemble it and find out from looking at the stack pointer and expected address by way of the 'stack pointer' (sp) to find out the type of parameters.

Using PE Explorer for instance, you can find out what functions are used and examine the disassembly dump...

Hope this helps, Best regards, Tom.

tommieb75
A: 

Most C/C++ compilers have the caller set up the stack before the call, and readjust the stack pointer afterwards. If the called function does not use pointer or reference arguments, there will be no memory corruption, although the results will be worthless. And as rerun says, pointer/reference mistakes almost always show up with a modicum of testing.

Daniel Newby
Hmm, I thought that with stdcall it was the responsibility of the callee to readjust the stack after the function call.Therefore, if we pass the wrong number of args, wouldn't that throw off the SP?
Emil D
No. I normal C/C++ compilers, the caller is responsible. That is how variadic functions like printf can work with a reasonable degree of safety.
Daniel Newby
Yes, but then again , the default calling convention for most C/C++ compilers is _cdecl, not _stdcall, as far as I know
Emil D
+1  A: 

It will either crash in the DLL code (since it got passed corrupt data), or: I think Visual C++ adds code in debug builds to detect this type of problem. It will say something like: "The value of ESP was not saved across a function call", and will point to code near the call. It helps but isn't totally robust - I don't think it'll stop you passing in the wrong but same-sized argument (eg. int instead of a char* parameter on x86). As other answers say, you just have to know, really.

AshleysBrain
That would be pretty useful.Is there any way to have the problem detected and not get the error screen? Because if that is the case, I'd be able to patch up the problem somewhat.I am not really worried about passing wrong parameters of the same size, as long as I avoid stack corruption outside the function.
Emil D
I don't know about detecting it yourself; my best guess would be to figure out what the debug build does, and add your own inline assembly to do the same check in a release build. Sounds tough though.
AshleysBrain
+3  A: 

If it's a __stdcall function and they've left the name mangling intact (both big ifs, but certainly possible nonetheless) the name will have @nn at the end, where nn is a number. That number is the number of bytes the function expects as arguments, and will clear off the stack before it returns.

So, if it's a major concern, you can look at the raw name of the function and check that the amount of data you're putting onto the stack matches the amount of data it's going to clear off the stack.

Note that this is still only a protection against Murphy, not Machiavelli. When you're creating a DLL, you can use an export file to change the names of functions. This is frequently used to strip off the name mangling -- but I'm pretty sure it would also let you rename a function from xxx@12 to xxx@16 (or whatever) to mislead the reader about the parameters it expects.

Edit: (primarily in reply to msalters's comment): it's true that you can't apply __stdcall to something like a member function, but you can certainly use it on things like global functions, whether they're written in C or C++.

For things like member functions, the exported name of the function will be mangled. In that case, you can use UndecorateSymbolName to get its full signature. Using that is somewhat nontrivial, but not outrageously complex either.

Jerry Coffin
The question is tagged C++, not C. But you describe the name mangling of C functions.
MSalters
+4  A: 

There are three errors I can think of if the expected and used number or type of parameters and calling convention differ:

  • if the calling convention is different, wrong parameter values will be read
  • if the function actually expects more parameters than given, random values will be used as parameters (I'll let you imagine the consequences if pointers are involved)
  • in any case, the return address will be complete garbage, so random code with random data will be run as soon as the function returns.

In two words: Undefined behavior

RaphaelSP
+1  A: 

There is no general answer. The Standard mandates that certain exceptions be thrown in certain circumstances, but aside from that describes how a conforming program will be executed, and sometimes says that certain violations must result in a diagnostic. (There may be something more specific here or there, but I certainly don't remember one.)

What the code is doing there isn't according to the Standard, and since there is a cast the compiler is entitled to go ahead and do whatever stupid thing the programmer wants without complaint. This would therefore be an implementation issue.

You could check your implementation documentation, but it's probably not there either. You could experiment, or study how function calls are done on your implementation.

Unfortunately, the answer is very likely to be that it'll screw something up without being immediately obvious.

David Thornley