通过“太多”安全吗?外部函数的参数?

发布于 2024-09-18 14:06:24 字数 580 浏览 6 评论 0原文

这种情况只有在没有名称修改的情况下才会发生(我相信),所以下面的代码是C。 假设 Ac 中定义了一个函数 A

void A(int x, int y){
    //Do stuff
}

现在还有一个单独的文件 Bc:

extern "C"{
    void A(int x, int y, int z);
}

void B(){
    A(1, 2, 3);
}

A 最初被声明为只有 2 个参数,但在 Bc 中声明时它有一个额外的参数,并且在 B() 中用第三个参数调用它。 我知道有可能发生这种情况,例如在与 Fortran 子例程链接时,或者在动态链接时。

我想向函数传递额外的参数是不安全的,任何人都可以解释当调用函数并将参数传递给它时内存中发生了什么吗?因此,传递这个既不被使用也不被需要的“额外”参数是多么安全。

额外的参数是否有可能覆盖函数内使用的内存空间?或者,对 A 的函数调用是否在内存中为参数分配空间,然后告诉 A 参数内存块的开头在哪里,A 读出前两个参数并忽略最后一个,使其完全安全?

任何有关该功能的信息都会很有启发,谢谢。

This situation can only occur without name mangling (I believe), so the below code is C.
Say there is a function A defined in A.c as

void A(int x, int y){
    //Do stuff
}

Now there is also a separate file B.c:

extern "C"{
    void A(int x, int y, int z);
}

void B(){
    A(1, 2, 3);
}

A is initially declared to have only 2 arguments yet when declared in B.c it has an extra one, and it is called with that third in B().
I know it is possible to make this situation occur, for example when linking with fortran subroutines, OR when dynamically linking.

I imagine it is unsafe to pass an extra argument to a function, can anyone explain what is happening in memory when a function is called and arguments are passed to it? And, therefore, how safe it is to pass this "extra" argument that is neither used nor wanted.

Is it possible that the extra argument overwrites a space in memory that is used within the function? Or does the function call to A allocate space in memory for the arguments then tell A where the beginning of the argument memory block is, A reads out the first two arguments and ignores the last, making it completely safe?

Any information on the function would be greatly enlightening, thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

乜一 2024-09-25 14:06:24

链接是实现定义的,所以没有办法明确地说。

也就是说,C 的其他功能(特别是 vardic 参数)强制实现通常允许它的实现。

例如,我不知道如果您编写以下内容,任何实现都会失败:

 printf("%d", 1, 2);

但是,它只会打印“1”。

这里很多人都提出了cdeclpascal__stdcall调用约定。然而,这些都不是标准的一部分,而是某些实现的所有功能。这让我们回到我的第一句话。

Linkage is implementation-defined, so there is no way to say definitely.

That said, other features of C (notably vardic parameters) force an implementation that would usually allow it.

For example, I don't know of any implementation that would fail if you wrote:

 printf("%d", 1, 2);

It would, however, merely print "1".

Many people here are bringing up cdecl, pascal and __stdcall calling conventions. However, none of those are part of the Standard and are all features of certain implementions,. which bring us back to my first sentence.

节枝 2024-09-25 14:06:24

这取决于所使用的调用约定。使用cdecl,调用者按从右到左的顺序将参数压入堆栈,然后被调用者通过偏移堆栈指针来访问它们。在这种情况下,调用太多参数不会破坏任何东西。

但是,如果您有从左到右的调用约定,那么事情就会崩溃。

It depends on the calling convention used. With cdecl, the caller pushes arguments onto the stack in right-to-left order, and then the callee accesses them by offsetting the stack pointer. Calling too many arguments won't break anything in this case.

If, however, you have a calling convention that is left-to-right, then things would break.

二智少女 2024-09-25 14:06:24

使用 cdecl 调用约定,调用者负责清理堆栈,所以这是安全的。相比之下,pascal 调用约定使被调用者负责清理,因此这将是危险的。

With the cdecl calling convention, the caller is responsible for cleaning up the stack, so this would be safe. To contrast, the pascal calling convention makes the callee responsible for cleanup, and so this would be dangerous.

缺⑴份安定 2024-09-25 14:06:24

在 C 中,这是违反约束的,因此会导致未定义的行为。

“如果表示被调用函数的表达式具有包含原型的类型,则参数的数量应与参数的数量一致。” (C99,第 6.5.2.2 条)

也就是说,实际上它主要取决于底层的调用约定。

In C this is a violation of constraints, therefore it results in undefined behaviour.

"If the expression that denotes the called function has a type that includes a prototype, the number of arguments shall agree with the number of parameters." (C99, §6.5.2.2)

That said, in practice it will depend mostly on the underlying calling conventions.

峩卟喜欢 2024-09-25 14:06:24

至少在C和C++中它不会造成任何伤害。参数从右向左推送,被调用者负责堆栈清理。

但是,除非您使用可变参数或强制转换函数类型,否则编译器不会让您执行此操作。例如:

#include <stdio.h>

static void foo (int a, int b, int c, int d, int e, int f, int g)
{
    printf ("A:%d B:%d C:%d D:%d E:%d F:%d G:%d \n",
            a, b, c, d, e, f, g);
}

int main ()
{
    typedef void (*bad_foo) (int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int);
    foo (1, 2, 3, 4, 5, 6, 7);
    bad_foo f = (bad_foo) (&foo);
    f (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17);
}

如果您查看汇编代码,所有参数都会被推入寄存器,但多余的参数会被忽略。

At least in C and C++ it won't do any harm. Arguments are pushed right to left and a callee is responsible for the stack cleanup.

However, the compiler won't let you do this unless you are using variadic parameters or cast a function type. For example:

#include <stdio.h>

static void foo (int a, int b, int c, int d, int e, int f, int g)
{
    printf ("A:%d B:%d C:%d D:%d E:%d F:%d G:%d \n",
            a, b, c, d, e, f, g);
}

int main ()
{
    typedef void (*bad_foo) (int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int);
    foo (1, 2, 3, 4, 5, 6, 7);
    bad_foo f = (bad_foo) (&foo);
    f (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17);
}

If you look at assembly code, all parameters are pushed into register but extra onces are just getting ignored.

帅气尐潴 2024-09-25 14:06:24

此类代码违反了单一定义规则(好吧,C 无论如何都相当于它......)它是否有效完全取决于平台。

特别是在 x86 上,如果函数被声明为 __cdecl,那么它将起作用,因为调用者会清理堆栈,但如果它是 __stdcall(大多数 Win32 函数都是如此),被调用者清理堆栈,在这种情况下会清理错误(因为它有太多参数)。因此,这取决于所使用的外部函数的调用约定。

我不明白你为什么想要这样做。

Such code violates the One Definition Rule (well, C's equivalent of it anyway...) Whether it works or not is entirely platform specific.

Specifically on x86, if the function was declared __cdecl, then it would work, because the caller cleans the stack, but if it was __stdcall (as most Win32 functions are), the callee cleans the stack, and would clean it wrong in that case (because it got too many parameters). Therefore it would depend on that external function's calling convention used.

I can't understand why you'd ever want to do this though.

╰ゝ天使的微笑 2024-09-25 14:06:24

如果我做对了,这可能会导致你的程序从内存中执行随机代码。当调用函数时,一些值,包括返回地址(函数完成时程序将跳回的位置)被压入堆栈。之后,函数参数(x,y,z)被压入堆栈,程序跳转到函数的入口点。然后,该函数将从堆栈中弹出参数 (x, y),执行某些操作,然后从堆栈中弹出返回地址(在本例中为 z,这是错误的)并跳回该地址。

这是堆栈详细信息的一个很好的描述: http://www.tenouk.com/Bufferoverflowc/Bufferoverflow2a .html

If I get it right, this might lead to your program to execude random code from memory. When a function is called, a few values, including the return address (where the program will jump back to when the function is finished) are pushed to the stack. After that, the function arguments (x, y, z) are pushed to the stack and the program jumps to the entry point of the function. The function will then pop the arguments (x, y) from the stack, do something, then pop the return address from the stack (z in this case, which is wrong) and jump back to it.

Here's a nice description of the stack details: http://www.tenouk.com/Bufferoverflowc/Bufferoverflow2a.html

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文