stack smashing detect以及解决之道

news2024/12/26 21:16:51

0. 简介

相较于其他报错,stack smashing detect这个报错是最令人头疼的段错误种类。“Stack smashing detect” 是指在程序运行过程中检测到栈溢出的情况。栈溢出是一种常见的安全漏洞,发生在程序尝试往栈空间写入超过其边界范围的数据时。

1. 常见分类

通常,导致 “Stack smashing detect” 错误的原因可能包括:

  1. 缓冲区溢出:当向一个缓冲区写入超过其分配大小的数据时,会覆盖到相邻的内存地址,导致栈被破坏。
  2. 函数调用错误:函数调用时参数传递错误或者返回值处理不当,可能引起栈结构被破坏。
  3. 格式化字符串漏洞:使用不当的格式化字符串函数(如printf)可能造成栈溢出。
  4. 栈溢出:如果递归调用层数过多,可能导致栈空间耗尽而触发 stack smashing detect。
  5. 内存泄漏:未正确释放之前分配的内存。
  6. 函数指针错误:调用或引用一个无效的函数指针。

下面我们来看一下每个可能原因的解决方法


2. 缓冲区溢出(使用GDB)

在C++中,数组的索引从0开始,因此需要确保不要超出数组的边界进行访问。如果程序中存在数组越界问题,可以按照以下步骤进行排查和解决:

  1. 检查数组的大小和边界。
  2. 确保数组的访问索引不会超出边界。
  3. 使用调试器(如gdb)来跟踪代码的执行,定位到越界访问的代码行。
  4. 修复代码中的越界访问错误,并确保访问索引正确计算。

3. 函数调用错误(使用GDB)

这种还是比较好排查的,也是实用GDB获取对应的函数,需要看一下传入的函数调用的参数个数、类型和顺序与函数定义一致。然后检查函数返回值是否被正确处理,避免因为忽略错误码而导致的问题。


4. 格式化字符串漏洞(使用GDB)

  1. 避免直接使用外部输入作为格式化字符串:不要将不受信任的字符串直接用作printf或其他格式化输出函数的格式串。
  2. 使用格式化字符串函数的安全版本:尽量使用安全的字符串函数,如strncpy代替strcpy,snprintf代替sprintf等,这些函数允许指定最大可写入字符数。

5. 栈溢出(AddressSanitzer&Valgrind&gperftools)

这种是最难排查的问题,栈溢出是指当程序中使用的栈空间超过其分配的限制时发生的情况。这通常是由于递归调用或过多的局部变量导致的。

为了解决栈溢出问题,可以考虑以下解决方法:

  1. 使用循环替代递归调用。
  2. 优化算法,减少需要使用的栈空间。
  3. 使用堆内存替代栈内存。
  4. 增加可用的栈空间(通过调整编译器选项),但需要注意堆栈溢出的风险。

5.1 分析递归深度

  1. 限制递归深度:为递归函数设置一个最大深度限制,当达到这个限制时停止递归。这可以帮助确定是否是递归深度导致的栈溢出。
  2. 递归到循环:如果可能,尝试将递归逻辑重写为循环逻辑。循环不会增加栈空间消耗,因此可以有效减少栈溢出的风险。

5.2 检查局部变量大小(这种一般程序在没有任何明显逻辑错误的情况下崩溃,特别是在程序退出或返回到上一级函数调用时)

  1. 减少局部变量:尤其是大型的数组或结构体,它们会占用大量的栈空间。考虑将它们改为动态分配的堆内存。

  2. 使用动态内存分配:对于需要大量内存的变量,使用new(C++)在堆上分配内存,而非在栈上。记得使用delete(C++)来释放分配的内存,以避免内存泄漏。

    5.2.1 如何修改

    作者这一次遇到的就是大量的struct被反复创建调用导致的栈资源消耗掉导致的,这里作者使用ulimit -s 查看linux默认栈空间的大小。然后通过命令 ulimit -s 设置大小值临时改变栈空间大小发现即解决了。然后进一步打log发现是struct传入

    我们这里使用指向指针的指针(双重指针)。使用LocalizationEstimate** locIns意味着locIns是一个指向另一个指针的指针,这个指针指向一个LocalizationEstimate实例。你需要在堆上动态分配LocalizationEstimate实例,并正确管理这些指针,包括分配和释放内存。当然这取决于你使用的操作

    单指针(T* ptr)
    用途:当你想在函数中操作指向对象的指针,或者分配/释放指向对象的内存时,会使用单指针。
    传参问题:如果你将单指针作为参数传递给函数,并在该函数内对指针进行重新分配(例如,使用new或delete),这个改变不会反映到调用者那里。这是因为指针本身是按值传递的,函数内的操作仅影响局部副本。
    双重指针(T** ptr)
    用途:当你需要在函数中改变指针本身的指向,或者你想在函数内部分配或释放内存,并将新的内存地址反映到调用者那里时,你会使用双重指针。
    传参动机:双重指针允许你在函数内部改变指针指向的地址,并通过函数参数将这个改变传递回调用者。这在动态内存管理和数据结构(如链表、树等)的修改中非常有用。

5.3 使用工具进行分析

  1. 编译器警告:启用编译器的所有警告(例如,使用-Wall -Wextra标志),可能会有关于栈空间使用的警告。
  2. 静态分析工具:使用静态分析工具检查代码,这些工具可以帮助发现潜在的问题,包括可能导致栈溢出的代码模式。
  3. 动态分析工具:使用如Valgrind、AddressSanitizer等动态分析工具运行程序,它们可以帮助检测栈溢出、内存泄漏和其他内存问题。

5.4 优化递归算法

  1. 尾递归优化:如果编译器支持尾递归优化,尝试将递归函数重写为尾递归形式。尾递归优化可以减少栈空间的使用。
  2. 分而治之:对于某些问题,考虑使用分而治之等策略,将问题分解为可以并行解决的小问题,这样可以减少单一递归调用链的深度。

5.5调整编译器栈大小设置

增加栈大小:在某些情况下,可以通过调整编译器设置来增加程序的栈大小。例如,在GCC中,可以使用链接器选项-Wl,–stack,来设定栈的大小。


6. 内存泄漏(AddressSanitzer&Valgrind&gperftools)

如果程序中存在内存泄漏问题,即未正确释放之前分配的内存,可能会导致栈溢出,并可能触发“stack smashing detected”错误。

为了解决内存泄漏问题,可以考虑以下解决方法:

  1. 使用动态内存分配的对象(如new/delete or malloc/free)时,确保正确释放内存。
  2. 使用智能指针(如std::shared_ptr或std::unique_ptr)来管理内存,以确保资源的正确释放。
  3. 使用编译器提供的内存分析工具来检测和解决内存泄漏问题。

7. 函数指针错误(使用GDB)

如果程序中存在函数指针错误,即调用或引用一个无效的函数指针,可能会触发“stack smashing detected”错误。

为了解决函数指针错误问题,可以考虑以下解决方法:

  1. 确保函数指针的初始化和使用是正确的。
  2. 使用nullptr来初始化和检查函数指针,以避免使用无效的指针。
  3. 使用调试器来跟踪并定位函数指针错误的位置。
  4. 确保函数指针的类型匹配。

8. 堆栈的划分

在C++中,变量可以在栈(stack)上分配,也可以在堆(heap)上分配,这取决于你如何声明和使用它们。下面是一些基本的规则和示例,帮助区分哪些声明是在栈上,哪些声明是在堆上。

8.1 在栈上的声明

  • 局部变量:在函数内部声明的变量(包括函数的参数)默认在栈上创建。这些变量的生命周期限定在其声明的块(如函数体)中。
    void function() {
        int localVariable = 10; // 栈上
        std::string localString = "Hello"; // 栈上
    }
    
  • 局部静态变量:虽然局部静态变量的生命周期贯穿整个程序执行期,但它们的存储位置通常不是堆,而是程序的静态存储区(不是栈)。
    void function() {
        static int localStaticVariable = 10; // 非堆,静态存储区
    }
    

8.2 在堆上的声明

  • 动态分配的对象:使用new操作符动态创建的对象在堆上分配。这些对象的生命周期不受其创建位置(如函数体)的限制,需要显式地使用delete操作符来释放。
    int* heapVariable = new int(10); // 堆上
    std::string* heapString = new std::string("Hello"); // 堆上
    
    delete heapVariable; // 释放堆内存
    delete heapString; // 释放堆内存
    
  • 动态分配的数组:使用new[]操作符动态创建的数组也在堆上。
    int* heapArray = new int[10]; // 堆上
    
    delete[] heapArray; // 释放堆内存
    

8.3 特别说明

  • 全局变量和静态变量:全局变量和静态变量(包括静态成员变量)不是在堆上分配的,它们存储在程序的静态存储区域,这个区域在程序启动时分配,在程序结束时释放。
  • std::vector 和类似的STL容器(比如 std::mapstd::string 等)在C++中表现出了有趣的双重特性:
    1. 容器的元数据在栈上:当你声明一个 std::vector 作为局部变量时,这个容器对象(包括指向其数据的指针、大小、容量等元数据)是存储在栈上的。这意味着,容器对象的生命周期与它被声明的作用域绑定。
      void function() {
          std::vector<int> myVector; // myVector对象本身在栈上
      } // myVector在此处离开作用域,被自动销毁
      
    2. 容器的数据在堆上:然而,std::vector 所管理的实际数据(即你放入容器的元素)是存储在堆上的。当你向 vector 中添加元素时,vector 负责在堆上分配足够的空间来存储这些元素,并在需要时(如扩容时)自动管理这些内存。当 vector 被销毁时(例如,当它离开作用域时),它也负责释放存储其元素的堆内存。

8.4 小结

  • 在栈上分配的变量包括函数内的局部变量,它们的生命周期由其所在的作用域决定。
  • 在堆上分配的对象和数组是通过new(或new[])操作符创建的,它们的生命周期由程序员通过delete(或delete[])操作符显式管理。
  • 全局变量和静态变量存储在静态存储区,既不在堆上也不在栈上。

9. 左值和右值

一句话,右值可以赋值给左值,不可以直接赋值给左值引用,但可以赋值给常量左值引用。而左值不能赋值给右值,只能是右值赋值给右值。一般来说&以及数字是右值。

具体可以参考下面的文章:https://gutsgwh1997.github.io/2020/02/13/C-%E4%B8%AD%E7%9A%84%E5%B7%A6%E5%80%BC%E5%92%8C%E5%8F%B3%E5%80%BC/


在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

…详情请参照古月居

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2115014.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

改写二进制文件

以下是一些常见的方法和工具&#xff1a; 1. 使用十六进制编辑器 十六进制编辑器 是最直接的工具之一&#xff0c;用于查看和编辑二进制文件中的数据。它允许你以十六进制格式查看和修改文件内容。 常见十六进制编辑器&#xff1a; HxD&#xff08;Windows&#xff09;Hex F…

铁路订票系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;管理员管理&#xff0c;车次信息管理&#xff0c;基础数据管理&#xff0c;论坛管理&#xff0c;通知公告管理&#xff0c;用户管理&#xff0c;轮播图信息 微信端账号功能包括&#xff1a;系统首页&a…

【linux学习指南】Linux编译器 gcc和g++使用

文章目录 &#x1f4dd;前言&#x1f320; gcc如何完成&#x1f309;预处理(进行宏替换) &#x1f320;编译&#xff08;生成汇编&#xff09;&#x1f309;汇编&#xff08;生成机器可识别代码&#xff09; &#x1f320;链接&#xff08;生成可执行文件或库文件&#xff09;&…

变压器制造5G智能工厂工业物联数字孪生平台,推进制造业数字化转型

变压器制造5G智能工厂工业物联数字孪生平台&#xff0c;推进制造业数字化转型。作为传统制造业的重要组成部分&#xff0c;变压器制造行业也不例外地踏上了数字化转型的快车道。而变压器制造5G智能工厂物联数字孪生平台的出现&#xff0c;更是为这一进程注入了强大的动力&#…

内卷时代无人机培训机构如何做大做强

在当今社会&#xff0c;随着科技的飞速发展&#xff0c;“内卷”一词频繁被提及&#xff0c;反映了各行业竞争日益激烈的现象。对于无人机培训行业而言&#xff0c;如何在这样的时代背景下脱颖而出&#xff0c;实现做大做强的目标&#xff0c;成为每个培训机构必须深思的问题。…

自学C语言-11

** 第3篇 高级应用 ** 第11章 结构体和共用体 迄今为止,我们在程序中用到的都是基本数据类型。但实际开发中,有时简单的变量类型无法满足程序中各种复杂的数据要求,因此C语言还提供了构造类型。构造类型数据是由基本类型数据按照一定规则组成的。 本章致力于使读者了解结…

【Nginx系列】Nginx中rewrite模块

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

《战锤40K:星际战士2》超越《黑神话》 登Steam热销榜首

《使命召唤&#xff1a;黑色行动6》将登陆 PC Game Pass看来确实影响了销量&#xff0c;因为这次在 Steam 上它的预购并没有占领 Steam 热销榜单之首。这次霸榜的则是即将推出的《战锤40K&#xff1a;星际战士2》。 根据 SteamDB 显示&#xff0c;这部将于9 月 10 日发售的游戏…

LabVIEW中Request Deallocation 功能

此功能会在包含该功能的 VI 运行之后释放未使用的内存。 该功能仅适用于高级性能优化。在某些情况下&#xff0c;释放未使用的内存可以提高性能。然而&#xff0c;过于频繁地释放内存可能导致 LabVIEW 反复重新分配空间&#xff0c;而不是重用已有的内存分配。如果您的 VI 分配…

rocky linux 9部署zabbix6

安装rocky9 阿里巴巴开源镜像站http://mirrors.aliyun.com 1、Rocky 2、初始化 防火墙 systemctl stop firewalld systemctl disable filewalld 或者 systemctl disable firewalld --now 3、selinux vi /etc/selinux/config 配置源sed -e s|^mirrorlist|#mirrorlist|g \-e s|^#…

24程序员转行,首选为什么是它?

今天文章的主人公暂且称他为 A 君。不过 A 君有点特别&#xff0c;非科班&#xff0c;工作 10 年后才转行 iOS 程序员。今年 36 岁&#xff0c;目前在某行业头部企业任职前端负责人&#xff0c;管理 40 人的前端团队。 废话不多说&#xff0c;我们开始 A 君&#xff08;为了描…

包机制,javadoc生成文档,用户交互scanner

包机制 在建包时com.kuang直接建线性一条龙的文件只会显示一个外层包&#xff0c;当再建一个包时才会显示出两个包。 import com.kuang.base 导入包的时候在后面加入星号就能把包全导进来 javadoc生成文档 public class Doc {String name;/*** * param name* return* throw…

Python可视化集大成之作 - Seaborn 介绍

我今天要介绍一款让你在数据可视化中游刃有余的利器——Seaborn包。作为Python数据可视化库中的一员&#xff0c;Seaborn不仅美观易用&#xff0c;而且功能丰富&#xff0c;是生物信息学中数据探索的好帮手。 为什么选择Seaborn&#xff1f; 1. 美观简洁 Seaborn的默认主题和颜…

跨系统环境下LabVIEW程序稳定运行

在LabVIEW开发中&#xff0c;不同电脑的配置和操作系统&#xff08;如Win11与Win7&#xff09;可能对程序的稳定运行产生影响。为了确保程序在不同平台上都能正常且稳定运行&#xff0c;需要从兼容性、驱动、以及性能优化等多个方面入手。本文将详细介绍如何在不同系统环境下&a…

PMP–一、二、三模–分类–变更–技巧–敏捷变更

文章目录 技巧高频考点分析&#xff08;一、过程&#xff1b;二、人员&#xff09;一、过程&#xff1a;1.1 变更管理&#xff1a;1.1.1 瀑布型变更&#xff08;一次交付、尽量限制、确定性需求 &#xff1e;风险储备&#xff09;1.1.2 敏捷型变更&#xff08;多次交付、拥抱变…

mybatis框架基础以及自定义插件开发

文章目录 框架概览框架预览MyBatis框架的核心组件MyBatis框架的工作原理MyBatis框架的配置MyBatis框架的最佳实践 自定义插件开发1. 添加依赖2. 创建插件类3. 配置插件4. 启动类中注册插件5. 测试插件 参考文献 框架概览 MyBatis是一个优秀的持久层框架&#xff0c;它支持自定…

多个vue项目部署到nginx服务器

文章目录 需求一、项目打包1.vue.config.js2.request.js文件3.打包 二、nginx配置 需求 同一个域名安装多个vue项目。 比如&#xff1a;域名为 https://domain.com 后缀。那么通过不同的后缀就能去访问不同的项目地址。 https://domain.com&#xff0c;不加任何后缀&#x…

OBItools:Linux下的DNA条形码分析神器

在生物信息学领域&#xff0c;DNA条形码分析是一种非常常见的研究方法&#xff0c;用于物种鉴定、生态学和进化生物学研究。今天要介绍的工具就是专为此设计的——OBItools。这个工具集专门用于处理生态学和进化生物学中的DNA条形码数据&#xff0c;在Linux环境下运行。无论你是…

linux下进行lvm分区及扩容

目录 LVM存储管理介绍 lvm磁盘扩容有两种方式 创建lvm磁盘 1. 首先先加入第一块儿新的磁盘 2. 对新磁盘 /dev/sdb 进行分区 通过LVM命令创建新卷 1. 创建物理卷 2.创建卷组 并将物理卷加入其中 3. 创建逻辑卷并分配大小 4.格式化刚刚创建的硬盘 5. 挂载磁盘 扩容lvm…

《Web性能权威指南》-网络技术概览-读书笔记

注&#xff1a;TCP/IP等知识牵涉面太广&#xff0c;且不说本文&#xff0c;哪怕是原书&#xff0c;限于篇幅&#xff0c;很多知识点都是大致介绍下。如果想深入理解&#xff0c;需要更一步Google相关页面资料。 延迟与带宽 WPO&#xff0c;Web Performance Optimization&…