MST
星途 面试题库

面试题:C语言中基于栈的缓冲区溢出原理及防范

详细阐述C语言中基于栈的缓冲区溢出的原理,包括函数调用过程中栈的结构变化。并说明在安全编程规范下,怎样有效防范基于栈的缓冲区溢出漏洞。
15.8万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

基于栈的缓冲区溢出原理

  1. 函数调用过程中栈的结构变化
    • 函数调用开始
      • 当一个函数被调用时,调用者会把返回地址压入栈中。返回地址是函数调用结束后程序应该继续执行的下一条指令的地址。
      • 然后,调用者会为被调用函数的局部变量在栈上分配空间。例如,如果被调用函数定义了一个数组 char buffer[10],这10个字节的空间会在栈上被预留。
    • 函数执行过程
      • 在函数执行期间,对局部变量的访问和操作都是基于栈指针进行的。例如,向 buffer 数组写入数据时,通过栈指针偏移来确定 buffer 在栈中的位置。
    • 函数调用结束
      • 函数执行完毕后,栈上为局部变量分配的空间被释放,通过恢复栈指针到函数调用前的位置来实现。然后,从栈中弹出返回地址,程序跳转到该返回地址继续执行。
  2. 缓冲区溢出发生机制
    • 当向一个基于栈的缓冲区(如局部数组)写入数据时,如果写入的数据长度超过了该缓冲区的实际大小,就会发生基于栈的缓冲区溢出。例如,char buffer[10]; strcpy(buffer, "123456789012345");,这里 strcpy 试图将长度为15的字符串复制到只有10个字节大小的 buffer 中。
    • 溢出的数据会覆盖栈上紧邻该缓冲区的其他数据,可能是其他局部变量、函数的返回地址等。如果返回地址被覆盖,当函数返回时,程序会跳转到错误的地址,可能导致程序崩溃、执行恶意代码等严重后果。

安全编程规范下防范措施

  1. 边界检查
    • 对输入数据长度进行检查:在将数据写入缓冲区之前,确保数据长度不超过缓冲区的大小。例如,使用 strncpy 替代 strcpystrncpy(buffer, source, sizeof(buffer));strncpy 最多只会复制 sizeof(buffer) - 1 个字符到 buffer 中,防止溢出。
    • 在循环操作缓冲区时检查边界:如果通过循环向缓冲区写入数据,要确保循环变量不会导致访问越界。例如:
for (int i = 0; i < sizeof(buffer); i++) {
    buffer[i] = data[i];
}
  1. 使用安全函数
    • 除了 strncpy 替代 strcpy 外,还有 snprintf 替代 sprintfsprintf 可能会因为格式化字符串产生的数据过长而导致缓冲区溢出,而 snprintf 会确保输出不超过指定的缓冲区大小。例如:snprintf(buffer, sizeof(buffer), "%s", source);
  2. 编译器保护机制
    • 启用栈保护选项:现代编译器(如GCC)提供了栈保护选项,如 -fstack - protector 系列选项。这些选项会在函数的栈帧中插入额外的保护数据(通常称为金丝雀值),当函数返回时,检查该值是否被修改。如果被修改,说明发生了缓冲区溢出,程序会终止执行。
  3. 代码审查
    • 在开发过程中,定期进行代码审查,仔细检查对缓冲区的操作,特别是涉及字符串处理、数组访问等操作,确保没有潜在的缓冲区溢出风险。例如,审查是否有未检查边界的数组访问操作:int arr[10]; int index = getIndex(); arr[index] = value;,这里如果 getIndex 返回的值大于9,就会发生溢出,通过代码审查可以发现并修正此类问题。