My mind was wandering today on the topic of function pointers, and I came up with the following scenario in my head:
我的思绪今天在关于函数指针的主题上徘徊,我在脑海中想出了以下场景:
__stdcall int function (int)
{
return 0;
}
int main()
{
(*(int(*)(char*,char*))function)("thought", "experiment");
return 0;
}
AFAIK this code would corrupt the stack, so what types of issues could I be looking at if I ran this code?
AFAIK这段代码会破坏堆栈,所以如果我运行这段代码,我会看到哪些类型的问题?
I'd do this investigating myself however I'm away from my dev machine for a week.
我这样做是在调查自己,但是我离开我的开发机器一周了。
EDIT: Hold on a second, I've been thinking a bit more. As has been observed in the comments, the intent of this code was to have a parameter left on the stack when all is said and done (caller puts two params on the stack, callee -- expecting only one param -- pops only one off). However, since my cast doesn't make mention of the calling convention, am I casting away stdcall, at least from the view of the caller? int function(int) will still pop a param off the stack, but does the caller revert to thinking the function is __cdecl (the default) because of the cast? (i.e. three total params popped?)
编辑:等一下,我一直在想更多。正如在评论中所观察到的,这段代码的意图是在完成所有操作后在堆栈上留下一个参数(调用者在堆栈上放置两个参数,被调用者 - 期望只有一个参数 - 只弹出一个参数)。但是,由于我的演员没有提到调用约定,我是否抛弃了stdcall,至少从调用者的角度来看? int function(int)仍然会从堆栈中弹出一个param,但调用者是否会因为强制转换而认为该函数是__cdecl(默认值)? (即三个总参数弹出?)
EDIT2: The answer to that second question, as confirmed by Rob, is yes. I would have to restate __stdcall if I wanted to leave a param on the stack:
EDIT2:正如Rob所证实的那样,第二个问题的答案是肯定的。如果我想在堆栈上留下一个参数,我将不得不重述__stdcall:
(*(__stdcall int(*)(char*,char*))function)("thought", "experiment");
3 个解决方案
#1
You are calling the function as if it is _cdecl which means the caller pushes the arguments and cleans up the stack.
您正在调用该函数,就好像它是_cdecl一样,这意味着调用者会推送参数并清理堆栈。
The receiving function is _stdcall which implies the callee cleans up the stack. The callee is expecting a single argument so will pop 4 bytes off the stack.
接收函数是_stdcall,这意味着被调用者清理堆栈。被调用者期待一个参数,因此将从堆栈中弹出4个字节。
When the function returns the caller will then pop off two pointers (having previously pushed on two pointers), so your stack is being corrupted by 4 bytes.
当函数返回时,调用者将弹出两个指针(之前已经推送了两个指针),因此您的堆栈被4个字节损坏。
Both calling conventions use the same return mechanism, and have the same register rules (eax, ecx and edx are not preserved). See wikipedia for more details.
两个调用约定都使用相同的返回机制,并且具有相同的寄存器规则(不保留eax,ecx和edx)。有关详细信息,请参阅*。
Depending on the stack frame layout and alignment this mismatch could cause a number of effects. If you are lucky then you get away with it. If not you might mess up the return address of your main function, causing the program to crash when it branches to who-knows-where. If the compiler has injected some kind of stack guard to catch corruption then it will likely detect this and abort the program.
根据堆栈框架布局和对齐,这种不匹配可能会导致许多影响。如果你很幸运,那么你就可以逃脱它。如果不是,您可能会搞乱主函数的返回地址,导致程序在分支到who-know-where时崩溃。如果编译器注入了某种堆栈保护来捕获损坏,那么它可能会检测到并中止程序。
#2
No, it will definitely not cause a blue screen. No user-mode process is able to do that. Even if such bug were in kernel-mode code, the BSOD would occur only after accessing invalid memory or passing wrong arguments to a function.
不,它肯定不会导致蓝屏。没有用户模式进程能够做到这一点。即使这样的bug存在于内核模式代码中,BSOD也只能在访问无效内存或向函数传递错误参数后才会发生。
You are simply corrupting private memory of your process, and the corruption may (or may not) later result in an invalid operation (eg. dereferencing a pointer pointing to invalid memory). When this happens, the OS terminates your process, but no sooner.
您只是破坏了进程的私有内存,并且损坏可能(或可能不会)导致无效操作(例如,取消引用指向无效内存的指针)。发生这种情况时,操作系统会终止您的进程,但不会很快。
#3
I think you would have 'undefined behavior' in this case.
我认为在这种情况下你会有'未定义的行为'。
From the C standard: (I would assume it's the same in C++)
从C标准:(我认为它在C ++中是相同的)
768 If a converted pointer is used to call a function whose type is not compatible with the pointed-to type, the behavior is undefined.
768如果转换的指针用于调用类型与指向类型不兼容的函数,则行为未定义。
Edit: On most operating system, this type of error would not cause problems in your whole operating system. But it would cause undefined problems in your program. It would be very hard for a user mode program to be able to cause a blue-screen.
编辑:在大多数操作系统上,此类错误不会导致整个操作系统出现问题。但它会导致程序中出现未定义的问题。用户模式程序很难能够产生蓝屏。
#1
You are calling the function as if it is _cdecl which means the caller pushes the arguments and cleans up the stack.
您正在调用该函数,就好像它是_cdecl一样,这意味着调用者会推送参数并清理堆栈。
The receiving function is _stdcall which implies the callee cleans up the stack. The callee is expecting a single argument so will pop 4 bytes off the stack.
接收函数是_stdcall,这意味着被调用者清理堆栈。被调用者期待一个参数,因此将从堆栈中弹出4个字节。
When the function returns the caller will then pop off two pointers (having previously pushed on two pointers), so your stack is being corrupted by 4 bytes.
当函数返回时,调用者将弹出两个指针(之前已经推送了两个指针),因此您的堆栈被4个字节损坏。
Both calling conventions use the same return mechanism, and have the same register rules (eax, ecx and edx are not preserved). See wikipedia for more details.
两个调用约定都使用相同的返回机制,并且具有相同的寄存器规则(不保留eax,ecx和edx)。有关详细信息,请参阅*。
Depending on the stack frame layout and alignment this mismatch could cause a number of effects. If you are lucky then you get away with it. If not you might mess up the return address of your main function, causing the program to crash when it branches to who-knows-where. If the compiler has injected some kind of stack guard to catch corruption then it will likely detect this and abort the program.
根据堆栈框架布局和对齐,这种不匹配可能会导致许多影响。如果你很幸运,那么你就可以逃脱它。如果不是,您可能会搞乱主函数的返回地址,导致程序在分支到who-know-where时崩溃。如果编译器注入了某种堆栈保护来捕获损坏,那么它可能会检测到并中止程序。
#2
No, it will definitely not cause a blue screen. No user-mode process is able to do that. Even if such bug were in kernel-mode code, the BSOD would occur only after accessing invalid memory or passing wrong arguments to a function.
不,它肯定不会导致蓝屏。没有用户模式进程能够做到这一点。即使这样的bug存在于内核模式代码中,BSOD也只能在访问无效内存或向函数传递错误参数后才会发生。
You are simply corrupting private memory of your process, and the corruption may (or may not) later result in an invalid operation (eg. dereferencing a pointer pointing to invalid memory). When this happens, the OS terminates your process, but no sooner.
您只是破坏了进程的私有内存,并且损坏可能(或可能不会)导致无效操作(例如,取消引用指向无效内存的指针)。发生这种情况时,操作系统会终止您的进程,但不会很快。
#3
I think you would have 'undefined behavior' in this case.
我认为在这种情况下你会有'未定义的行为'。
From the C standard: (I would assume it's the same in C++)
从C标准:(我认为它在C ++中是相同的)
768 If a converted pointer is used to call a function whose type is not compatible with the pointed-to type, the behavior is undefined.
768如果转换的指针用于调用类型与指向类型不兼容的函数,则行为未定义。
Edit: On most operating system, this type of error would not cause problems in your whole operating system. But it would cause undefined problems in your program. It would be very hard for a user mode program to be able to cause a blue-screen.
编辑:在大多数操作系统上,此类错误不会导致整个操作系统出现问题。但它会导致程序中出现未定义的问题。用户模式程序很难能够产生蓝屏。