60 关于 SegmentFault 的一些场景 (2)

2024-06-03 00:12
文章标签 场景 60 segmentfault

本文主要是介绍60 关于 SegmentFault 的一些场景 (2),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 前言

呵呵 此问题主要是来自于 帖子 月经结贴 -- 《Segmentation Fault in Linux》

这里主要也是 结合了作者的相关 case, 来做的一些 调试分享 

当然 很多的情况还是 蛮有意思 

 

本文主要问题如下 

1. 访问异常堆栈地址1
2. 访问异常堆栈地址2
3. 访问异常堆栈地址3
4. stack_size 为什么初始化为 136kb? 
5. 访问异常堆栈地址4
6. 访问异常堆栈地址5

 

 

1. 访问异常堆栈地址1

#include <stdio.h>
#include <stdlib.h>int* foo() {int a = 10;return &a;
}int main() {int* b;b = foo();printf ("%d\n", *b);
}

 

可以看到的是这里的 address 依然是 0, 应该是 编译时 有什么处理

然后 情况和 NPE 的情况一致 

直接走的 bad_area, 输出日志信息, 发送 SIGSEGV 给目标进程 

948bd755e59c40009f3cd695fff82a86.png

报错日志信息为 

(initramfs) ./Test16SigSegvAccessInvalidStackAddr01

[ 9895.840368] Test16SigSegvAc[265]: segfault at 0 ip 00000000004005e9 sp 00007ffda8506930 error 4 in Test16SigSegvAccessInvalidStackAddr01[400000+1000]

-

出现问题的进程为 265号进程, 异常访问的地址为 0x 0

出现问题的异常代码为 0x4005e9, 栈顶寄存器的值为 0x 7ffda8506930

错误编码为 4 表示 PF_USER

 

0x0 为 foo 返回的地址信息, 应该是被编译器处理过 

 

0x4005e9 为 main 中执行出现异常的代码段 

在准备 printf 的参数的时候, 出现的异常 

81b85483bbaa4509a754986535b7cac2.png

 

我们看一下 foo 的编译之后的结果

可以看到的是 返回值是直接 定义的 0, 然后 返回回去了

其他部分的增加了一个 局部变量 处理的意义不明 

f15be55d36b747158e13ba0e7700e16e.png

 

 

2. 访问异常堆栈地址2 

可以看到的是这里的 address 依然是 0, 应该是 编译时 有什么处理

然后 情况和 上面 的情况一致 

直接走的 bad_area, 输出日志信息, 发送 SIGSEGV 给目标进程 

6ca92732367145c8b66074e2fe9da0b9.png

 

报错日志信息为 

(initramfs) ./Test16SigSegvAccessInvalidStackAddr02

[10934.883918] Test16SigSegvAc[267]: segfault at 1388 ip 0000000000400644 sp 00007ffd1a7d5660 error 4 in Test16SigSegvAccessInvalidStackAddr02[400000+1000]

 

出现问题的进程为 267号进程, 异常访问的地址为 0x 1388  

出现问题的异常代码为 0x400644, 栈顶寄存器的值为 0x 7ffd1a7d5660

错误编码为 4 表示 PF_USER

 

 

3. 访问异常堆栈地址3

#include <stdio.h>
#include <stdlib.h>int main() {char* c;c = (char*)&c - 8192*2;*c = 'a';printf ("%c\n", *c);
}

 

可以看到的是这里的 address 是 140735643557120 这是真实的用户栈帧的空间地址 0x7FFF 920A9D00‬ ‬‬‬‬

然后这里 堆栈对应的 vma 有 136k, 然后这里的 address 是在这个 vma 的区间内, 并且可读可写, 因此 没有异常

17ea05625fac45c5bc525ba8b1a09525.png

 

然后是走 正常的缺页中断, 然后之后是 对于该内存的赋值处理

fb8f43b3d52e4b839114a947731cb217.png

 

regs->ip 为 0x4005bf

3b3a0fce8b01479aaf2420008fd3fb98.png 

 

4. stack_size 为什么初始化为 136kb?

如下代码, rlimit_stack 默认值为 8192kb

stack 对应的 vma 默认大小为 8kb, 然后 stack_expand 为 128kb

如下代码处理之后 stack 对应的 vma 为 136kb

54d5746ddfbe4405b679a1c07cb3396d.png

 

具体的更新 stack 对应的 vma 的地方在如下地方

1157c50b4bee432eba40a06b117a9a46.png

 

看一下 stack 的初始化, 这里是关闭了 ASLR 的情况 

stack 初始化为 以 0x7ffffffff000 结束的 4kb

24495c4a9bba45729793c1adf7f2b42c.png

 

exec.setup_arg_pages 传入的新的 stack_top 是通过 binfmt_elf.randomize_stack_top 计算得到的, 默认的 STACK_TOP 是 0x7ffffffff000

然后 binfmt_elf.randomize_stack_top 的过程中会偏移 随机数个页面

d0e9a0afe5e74972a876abeab26369c3.png

 

拷贝参数的时候, 可能空间不够 向下扩充了 4kb, 目前合计 8kb

86782480199d44f4a0f5389de0f93c8e.png

 

然后就是 stack_expand, 扩充空间为 136kb

c7303114088c46a8b31666db95fc78cd.png

 

stack 空间观察的实际例子, 可以看到这里 stack 占用的是 0x7ffffffff000 - 0x7ffffffde000 = 0x21000 合计 132kb

估算大概就是 4kb[init] + 128kb[stack_expand] = 132kb

root@ubuntu:~/linux/linux-4.10.14# ps -ef | grep Test14
root     43248 42986  0 21:59 pts/0    00:00:00 gdb Test14ReadFileTwice
root     43250 43248  0 21:59 pts/0    00:00:00 /root/linux/tmp/Test14ReadFileTwice
root     43255 42488  0 21:59 pts/3    00:00:00 grep --color=auto Test14
root@ubuntu:~/linux/linux-4.10.14# cat /proc/43250/maps | grep stack
7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0                          [stack]

 

 

5. 访问异常堆栈地址4

#include <stdio.h>
#include <stdlib.h>int main() {//    int *p = (int *) 0x7ffffffcf100;int *p = (int *) 0x7ffffffdf100;*p = 10;}

 

首先需要关闭 linux 的地址随机化 

根据起始地址可以观察到的是 vma 是属于 堆栈所属的 vma, 大小为 136kb

stack 对应的 vma 区间为 0x7ffffffdd000 - 0x7ffffffff000

70fc9aa716e84c089b07b0e5399b9751.png

 

访问的地址属于 stack 的区间, 并且可读可写, 走正常的 缺页中断处理

4c7ee9d5d192461d9382ce60174066e9.png 

 

更新上面的 指针 p “int *p = (int *) 0x7ffffffcf100;”

从代码可以看出, 如果找不到 address 对应的 vma, 是允许 access [($sp – 65536 – 256), $sp] 的地址空间的 

可以的操作是将 sp 更新到 stack 对应的 vma 的低地址部分, 然后 在访问 sp – 64kb 范围内的内容, 这时候操作系统会 expan_stack 来扩充堆栈空间 

但是 我们这里 sp 还在 stack 对应的 vma 的高地址部分, 不满足这里 扩充堆栈 的条件, 因此 这里得到的是一个 SIGSEGV

15b57e461dd644fd8fe64ddedf383857.png

 

报错日志信息为 

(initramfs) ./Test16SigSegvAccessKernelAddr03

[78329.025697] Test16SigSegvAc[297]: segfault at 7ffffffcf100 ip 00000000004004ec sp 00007fffffffea60 error 6 in Test16SigSegvAccessKernelAddr03[400000+1000]

 

出现问题的进程为 297号进程, 异常访问的地址为 0x 7ffffffcf100

出现问题的异常代码为 0x4004ec, 栈顶寄存器的值为 0x 7fffffffea60

错误编码为 4 表示 PF_USER | PF_WRITE

 

 

6. 访问异常堆栈地址5

#include <stdio.h>
#include <stdlib.h>#define GET_rsp(rsp) do { \asm volatile ("movq %%rsp, %0\n\t" : "=m" (rsp)); \
} while (0)#define K 1024int main() {char* c;int i = 0;unsigned long rsp;GET_rsp (rsp);printf ("Current stack pointer is %#x\n", rsp);while (1) {c = (char*)rsp - i * K;*c = 'a';GET_rsp (rsp);printf ("rsp = %#x, overflow %dK\n", rsp, i);i ++;}
}

 

日志输出如下

(initramfs) ./Test16SigSegvAccessInvalidStackAddr04
Current stack pointer is 0xffffea30
rsp = 0xffffea30, overflow 0K
rsp = 0xffffea30, overflow 1K
rsp = 0xffffea30, overflow 2K
rsp = 0xffffea30, overflow 3K
rsp = 0xffffea30, overflow 4K
rsp = 0xffffea30, overflow 5K
rsp = 0xffffea30, overflow 6K
rsp = 0xffffea30, overflow 7K
rsp = 0xffffea30, overflow 8K
rsp = 0xffffea30, overflow 9K
rsp = 0xffffea30, overflow 10K
// 省略 .. 一部分连续的输出 
rsp = 0xffffea30, overflow 8181K
rsp = 0xffffea30, overflow 8182K
rsp = 0xffffea30, overflow 8183K
rsp = 0xffffea30, overflow 8184K
rsp = 0xffffea30, overflow 8185K
rsp = 0xffffea30, overflow 8186K
rsp = 0xffffea30, overflow 8187K
rsp = 0xffffea30, overflow 8188K
rsp = 0xffffea30, overflow 8189K
rsp = 0xffffea30, overflow 8190K
[ 1569.537658] Test16SigSegvAc[273]: segfault at 7fffff7fee30 ip 0000000000400578 sp 00007fffffffea30 error 6 in Test16SigSegvAccessInvalidStackAddr04[400000+1000]
Segmentation fault

 

stack 默认有 136kb 合计 34 个 page 

当前 rsp 为 0x7fffffffea30, STACK_TOP 为 0x7ffffffff000, 当前已经使用的空间为 0x5d0 = 1488 合计约 1.5kb

从 1.5kb – 8kb 的区间有正常的物理内存, 并且可写, 无缺页中断 

从 8kb – 136kb 的区间走的是正常的缺页中断 

然后从 136kb – 8192kb 的区间, 开始以页为单位, 向下 expand_stack, 每次 扩展一个页面, 比如访问 135kb 所在的页面时, 会向下 expand 136kb – 140kb 对应的页面 

到达 STACK_LIMIT 8192kb 的时候, stack 根据约束无法向下再扩展, 访问到异常地址, 发生 SIGSEGV 信号 

 

8kb – 132kb 走正常的缺页中断 

这里 vma 区间是 0x7ffffffdd000 - 0x7ffffffff000, 需要访问的空间为 0x7fffffffce30

12c4945418f943059db19fa9aa54447e.png

 

然后从 136kb – 8192kb 的区间, 如果下一个页面未使用, 向下 expand_stack, 每次 扩展一个页面

这里访问的地址是 0x7ffffffdde30

230247c43e9d4c388efe4e31d478d25d.png

 

堆栈空间访问到 目前的最低一个页面的时候, 需要自动向下扩展一个页面 

d348559cf2a849c08b2a29f052e6037c.png

2898d8ac40d44a1983baaa0165c9cbc8.png 

 

访问到超出界限的地方 

这里访问的空间是 0x7fffffff7fe30, 是刚好超出 8192kb 的第一个页面 

这里是 处理缺页中断失败, 然后 接着走的 下面的 mm_fault_error

39197274ac01462997ba504309a7d77b.png

 

然后 fault 带上了 FAULT_SIGSEGV, 发送 SIGSEGV 信号 

3010f194a8094de2b72d5e5aed67304f.png 

然后这里的 缺页中断 异常主要是在 check_stack_guard_page 中, 发现 到达了 stack 前一块空间的地方, 返回没有内存 NOMEM, 然后 check_stack_guard_page 这里响应给上级 VM_FAULT_SIGSEGV 给上层, 再最上面就是 __do_page_fault 的处理了 

93a671156ef94ab9a44ab00709a9b2d1.png 

 

 

 

 

 

这篇关于60 关于 SegmentFault 的一些场景 (2)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1025492

相关文章

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

PostgreSQL核心功能特性与使用领域及场景分析

PostgreSQL有什么优点? 开源和免费 PostgreSQL是一个开源的数据库管理系统,可以免费使用和修改。这降低了企业的成本,并为开发者提供了一个活跃的社区和丰富的资源。 高度兼容 PostgreSQL支持多种操作系统(如Linux、Windows、macOS等)和编程语言(如C、C++、Java、Python、Ruby等),并提供了多种接口(如JDBC、ODBC、ADO.NET等

嵌入式技术的核心技术有哪些?请详细列举并解释每项技术的主要功能和应用场景。

嵌入式技术的核心技术包括处理器技术、IC技术和设计/验证技术。 1. 处理器技术    通用处理器:这类处理器适用于不同类型的应用,其主要特征是存储程序和通用的数据路径,使其能够处理各种计算任务。例如,在智能家居中,通用处理器可以用于控制和管理家庭设备,如灯光、空调和安全系统。    单用途处理器:这些处理器执行特定程序,如JPEG编解码器,专门用于视频信息的压缩或解压。在数字相机中,单用途

『功能项目』更换URP场景【32】

上一章已经将项目从普通管线升级到了URP管线 现在我们打开上一篇31项目优化 - 默认管线转URP的项目, 进入战斗场景 将Land的子级全部隐藏 将新的URP场景预制体拖拽至Land子级 对场景预制体完全解压缩 将Terrain拖拽至Land的直接子级 将Terrain设置为Land 与 静态Static 清除烘培 重新烘培 修改脚本:LoadRe

70-java write类应用场景

在Java中,我们可以使用java.io包中的FileWriter和BufferedWriter类来写入数据到文件。以下是一个简单的例子,展示了如何使用FileWriter和BufferedWriter来写入数据到文件: import java.io.BufferedWriter;import java.io.FileWriter;import java.io.IOException;pub

消息队列的理解和应用场景

知乎上的一个通俗理解的优秀答案 by 祁达方 小红是小明的姐姐。 小红希望小明多读书,常寻找好书给小明看,之前的方式是这样:小红问小明什么时候有空,把书给小明送去,并亲眼监督小明读完书才走。久而久之,两人都觉得麻烦。 后来的方式改成了:小红对小明说「我放到书架上的书你都要看」,然后小红每次发现不错的书都放到书架上,小明则看到书架上有书就拿下来看。 书架就是一个消息队列,小红是生产者,小明是

828华为云征文|基于Flexus云服务器X实例的应用场景-拥有一款自己的ssl监控工具

先看这里 写在前面效果图华为云Flexus云服务器X实例介绍特点可选配置购买 连接服务器Uptime-kuma简介开源信息部署准备工作:docker部署命令访问uptime-kuma 基本配置总结 写在前面 作为一个个人开发者,相信你手里肯定也有不少自己的服务,有的服务呢也是https的。 以前ssl各厂都是可以免费申请一年的,我们更换的频率还好,比较小;但是最近,各厂都

【语音告警】博灵智能语音报警灯JavaScript循环播报场景实例-语音报警灯|声光报警器|网络信号灯

功能说明 本文将以JavaScript代码为实例,讲解如何通过JavaScript代码调用博灵语音通知终端 A4实现声光语音告警。主要博灵语音通知终端如何实现无线循环播报或者周期播报的功能。 本代码实现HTTP接口的声光语音播报,并指定循环次数、播报内容。由于通知终端采用TTS语音合成技术,所以本次案例中无需预先录制音频。 代码实战 为了通过JavaScript调用博灵语音通知终端,实现HT

文本分类场景下微调BERT

How to Fine-Tune BERT for Text Classification 论文《How to Fine-Tune BERT for Text Classification?》是2019年发表的一篇论文。这篇文章做了一些实验来分析了如何在文本分类场景下微调BERT,是后面网上讨论如何微调BERT时经常提到的论文。 结论与思路 先来看一下论文的实验结论: BERT模型上面的

黑神话:悟空》增加草地绘制距离MOD使游戏场景看起来更加广阔与自然,增强了游戏的沉浸式体验

《黑神话:悟空》增加草地绘制距离MOD为玩家提供了一种全新的视觉体验,通过扩展游戏中草地的绘制距离,增加了场景的深度和真实感。该MOD通过增加草地的绘制距离,使游戏场景看起来更加广阔与自然,增强了游戏的沉浸式体验。 增加草地绘制距离MOD安装 1、在%userprofile%AppDataLocalb1SavedConfigWindows目录下找到Engine.ini文件。 2、使用记事本编辑