一个memset导致的血案
本文记录解答MIT 6.828 Lab 1 Exercise 10时遇到的一个Bug。
问题描述
在i386_init入口处设置断点并运行,发现执行memset(edata, 0, end - edata);
时,QEMU窗口会打印以下日志并卡住,GDB窗口会异常结束。这是什么原因?
代码如下所示:
void i386_init(void)
{
extern char edata[], end[];
// Before doing anything else, complete the ELF loading process.
// Clear the uninitialized global data (BSS) section of our program.
// This ensures that all static/global variables start out zero.
memset(edata, 0, end - edata);
// Initialize the console.
// Can't call cprintf until after we do this!
cons_init();
cprintf("6828 decimal is %o octal!\n", 6828);
// Test the stack backtrace function (lab 1 only)
test_backtrace(5);
// Drop into the kernel monitor.
while (1)
monitor(NULL);
}
QEMU窗口打印的错误日志:
EAX=00000000 EBX=00000000 ECX=000001a9 EDX=00000000
ESI=00000000 EDI=f0113000 EBP=f010ffd8 ESP=f010ffcc
EIP=f010171b EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
CS =0008 00000000 ffffffff 00cf9a00 DPL=0 CS32 [-R-]
SS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
DS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
FS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
GS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT= 00007c4c 00000017
IDT= 00000000 000003ff
CR0=80010011 CR2=00000040 CR3=00112000 CR4=00000000
DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000
DR6=ffff0ff0 DR7=00000400
EFER=0000000000000000
Triple fault. Halting for inspection via QEMU monitor.
GDB窗口打印的错误日志:
Program received signal SIGTRAP, Trace/breakpoint trap.
The target architecture is assumed to be i386
=> 0xf010171b <memset+73>: Error while running hook_stop:
Cannot access memory at address 0xf010171b
0xf010171b in memset (
v=<error reading variable: Cannot access memory at address 0xf010ffd0>,
c=<error reading variable: Cannot access memory at address 0xf010ffd4>,
n=<error reading variable: Cannot access memory at address 0xf010ffd8>) at lib/string.c:131
1: $ebp = (void *) 0xf010ffd8
2: $esp = (void *) 0xf010ffcc
3: /x $eax = 0x0
4: /x $ebx = 0x0
5: $ecx = 488
6: $edx = 0
8: /x $edi = 0xf0112f04
9: /x $esi = 0x0
10: *0xf0111300@10 = <error: Cannot access memory at address 0xf0111300>
11: *0xf0112f00@10 = <error: Cannot access memory at address 0xf0112f00>
12: *0xf01136a0@10 = <error: Cannot access memory at address 0xf01136a0> asm volatile("cld; rep stosl\n"
定位过程
memset的汇编实现中是重复执行stosl命令,将0依次传到0xf0111300~0xf01136a4这段内存空间,每次传4字节,共需重复2281次。调试中发现,当执行到第2281-488=1793次时,也就是将0传给0xf0112f04这个地址时系统就报错了。
从官方地址上下载一份干净的代码重新编译执行,发现同样在memset会崩溃,但我记得很早以前第一次下载代码来运行时是正常的,很奇怪。
注释掉memset这一行,发现可以继续运行,但跑到monitor时会在QEMU窗口不断打印乱码与"unknown command."信息。使用gdb逐步执行时发现是readline时用户根本没输入但依然能读到数据,显示出来是乱码,因此解析输入内容时会报“Unknown command”。
下午使用gdb跟踪readline及getchar的代码,最终跟踪到通过IN指令来获取输入数据的地方,但只能观察到用户没输入IN指令也能返回,确认不了原因。我怀疑是前面注释了memset语句,导致I/O需要用到的内存空间没初始化,进而出错。因此只能继续定位memset为什么出错。
晚上决定先确认下是否只有0xf0112f04这个地址的初始化才会有问题,于是memset时避开这个地址,发现果然memset可以成功,但跑到monitor时会崩溃。
memset(edata, 0, 0xf0112f04 - edata);
memset(0xf0112f08, 0, end - 0xf0112f08);
- 后来看代码注释时,发现memset语句的目的是初始化BSS段。
// Before doing anything else, complete the ELF loading process.
// Clear the uninitialized global data (BSS) section of our program.
// This ensures that all static/global variables start out zero.
memset(edata, 0, end - edata);
通过objdump -h obj/kern/kernel
命令查看发现,bss段的地址范围是0xf01130600xf01136a4,而我们要memset的地址范围却是0xf01113000xf0113604!这样除了初始化.bss段之外,还会初始化.got,.got.plt,.data.rel.local和.data.rel.ro.local等4个段。
Sections:
Idx Name Size VMA LMA File off Algn
5 .got 00000008 f0111300 00111300 00012300 2**2
CONTENTS, ALLOC, LOAD, DATA
6 .got.plt 0000000c f0111308 00111308 00012308 2**2
CONTENTS, ALLOC, LOAD, DATA
7 .data.rel.local 00001000 f0112000 00112000 00013000 2**12
CONTENTS, ALLOC, LOAD, DATA
8 .data.rel.ro.local 00000044 f0113000 00113000 00014000 2**2
CONTENTS, ALLOC, LOAD, DATA
9 .bss 00000644 f0113060 00113060 00014044 2**5
ALLOC
- 我尝试将memset的地址范围改为bss段的地址范围(0xf0113060~0xf01136a4),结果memset和monitor都正常运行了。先记录至此,以后再回头分析一下。
一个memset导致的血案的更多相关文章
- Replication的犄角旮旯(六)-- 一个DDL引发的血案(上)(如何近似估算DDL操作进度)
<Replication的犄角旮旯>系列导读 Replication的犄角旮旯(一)--变更订阅端表名的应用场景 Replication的犄角旮旯(二)--寻找订阅端丢失的记录 Repli ...
- Replication的犄角旮旯(七)-- 一个DDL引发的血案(下)(聊聊logreader的延迟)
<Replication的犄角旮旯>系列导读 Replication的犄角旮旯(一)--变更订阅端表名的应用场景 Replication的犄角旮旯(二)--寻找订阅端丢失的记录 Repli ...
- 一个字母引发的血案 java.io.File中mkdir()和mkdirs()
一个字母引发的血案 明天开始放年假了,临放假前有个爬虫的任务,其中需要把网络图片保存到本地,很简单,马上写完了代码: //省略部分代码... Long fileId= (Long) data.get( ...
- 10-多写一个@Autowired导致程序崩了
再是javaweb实验六中,是让我们改代码,让它跑起来,结果我少注释了一个,导致一直报错,检查许久没有找到,最后通过代码替换逐步查找,才发现问题.
- this.$Message.success('提示信息') 少写了一个c 导致报错
this.$Message.success('提示信息') 少写了一个c 导致报错 而且 $Message 输出还没显示,导致我以为是没有 $Message 对象了,其实全局对象直接调用即可
- 连接池设置导致的“血案” 原创: 一页破书 一页破书 5月6日 这个问题被投诉的几个月了,一直没重视——内部客户嘛😿 问题现象: 隔几周就会出现 A服务调用B服务超时 脚趾头想就是防火墙的问题,A、B两服务之间有防火墙 找运维查看防火墙日志确实断掉了tcp连接,但是是因为B服务5分钟没有回包,下面这个表情就是我当时的心情——其实我们在防火墙、A服务、B服务都抓包了,几十个G的t
连接池设置导致的“血案” 原创: 一页破书 一页破书 5月6日 这个问题被投诉的几个月了,一直没重视——内部客户嘛
- SQL实战——04. 查找所有已经分配部门的员工的last_name和first_name以及dept_no (一个逗号引发的血案)
查找所有已经分配部门的员工的last_name和first_name以及dept_noCREATE TABLE `dept_emp` (`emp_no` int(11) NOT NULL,`dept_ ...
- memset 导致的一个段错误
原型: void *memset(void *s, int c, size_t n); 解释: memset :是 逐字节 拷贝,即n是指整个变量所占字节,在用于数组时一定要注意n不一定是 数组元素. ...
- memset 导致的段错误(segmentation fault)
在调试Minixml库时,定义了一个结构体: struct ssid_info_s{ std::string wl_ssid_name; std::string wl_ssid_mac; std::s ...
随机推荐
- ora-28002
1.查看指定概要文件(如default)的密码有效期设置: SELECT * FROM dba_profiles s WHERE s.profile='DEFAULT' AND resource_na ...
- luogu 2114 [NOI2014]起床困难综合症 位运算+贪心
感觉这个思路非常巧妙啊~ code: #include <bits/stdc++.h> #define ll long long #define setIO(s) freopen(s&qu ...
- Linux下 Java 读取文件路径
一般文件路径在windows中用 \ 表示,但是在其他系统平台下比如linux中就不是 \ 所以java给我们提供了一个与平台无关的表示路径的常量 File.separator在windows中则表示 ...
- Shiro RememberMe 1.2.4远程代码执行漏洞-详细分析
本文首发于先知: https://xz.aliyun.com/t/6493 0x01.漏洞复现 环境配置 https://github.com/Medicean/VulApps/tree/master ...
- Linux ubuntu centos 下 grep显示前后几行信息
标准unix/linux下的grep通过下面参数控制上下文 grep -C 5 foo file 显示file文件里匹配foo字串那行以及上下5行grep -B 5 foo file 显示foo及前5 ...
- P5657 格雷码【民间数据】
P5657 格雷码[民间数据] 题解 其实这题水啊 打表找规律 [1]0 1 [2]00 01 11 10 [3]000 001 011 010 110 111 1 ...
- .NET笔记题库(一)
1 (1)面向对象的语言具有__继承性_性._封装性_性._多态性 性. (2)能用foreach遍历访问的对象需要实现 _ IEnumerable 接口或声明_ GetEnumerator 方法的类 ...
- python实现并发服务器实现方式(多线程/多进程/select/epoll)
python实现并发服务器实现方式(多线程/多进程/select/epoll) 并发服务器开发 并发服务器开发,使得一个服务器可以近乎同一时刻为多个客户端提供服务.实现并发的方式有多种,下面以多进 ...
- smarty {for}{forelse}
{for} {for}{forelse}用于创建一个简单的循环. 下面的几种方式都是支持的: {for $var=$start to $end}步长1的简单循环. {for $var=$start t ...
- oracle增删改查基本语法(DDL/DML)整理
DDL:Data Definition Language(数据库定义语言)DML:Data Manipulation Language(数据库操作语言) 一.表结构操作(create table.al ...