Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Assembly 在nasm程序集中调用函数时出现分段错误_Assembly_X86_Nasm - Fatal编程技术网

Assembly 在nasm程序集中调用函数时出现分段错误

Assembly 在nasm程序集中调用函数时出现分段错误,assembly,x86,nasm,Assembly,X86,Nasm,我试图在nasm中调用我自己的函数,它工作了2次,然后给出了分段错误。我创建了两个函数display1和display2,分别显示“thisismessage1”和“thisismessage2”。这些函数第一次是正确的,但在调用这些函数两次时显示分段错误 global _start section .text display1: mov eax, 0x4 mov ebx, 0x1 mov ecx, var1 mov edx, len1 int 0x8

我试图在nasm中调用我自己的函数,它工作了2次,然后给出了分段错误。我创建了两个函数display1和display2,分别显示“thisismessage1”和“thisismessage2”。这些函数第一次是正确的,但在调用这些函数两次时显示分段错误

global _start

section .text

display1:
    mov eax, 0x4
    mov ebx, 0x1
    mov ecx, var1
    mov edx, len1
    int 0x80
    ret
display2:
    mov eax, 0x4
    mov ebx, 0x1
    mov ecx, var2
    mov edx, var2
    int 0x80
    ret

_start:
    call display1
    call display2
    call display1
    call display2
    mov eax, 0x1
    mov ebx, 0x5
    int 0x80

section .data

    var1: db "This is message1", 0x0A, 0x00
    len1 equ $-var1
    var2: db "This is message2", 0x0A, 0x00
    len2 equ $-var2


恭喜你,你发现了一个内核bug(在非常旧的Ubuntu 12.04/Linux 3.13.0-32-generic 32位内核中)

mov-edx,var2
传递一个非常大的整数(地址)作为大小
。这就是为什么在第二条消息之后你会收到垃圾;
write
系统调用将内存读取到未映射页面附近的某个位置,然后停止

在一个没有bug的内核上,然后
write
返回并继续执行,直到
\u退出
系统调用

指令
int 0x80
导致分段错误

IDK这是否比破坏用户空间并导致以后出现故障更疯狂

可能不值得在任何地方报告这个内核错误。Ubuntu 12.04版本。这个错误在现代内核中并不存在,并且可能是由于偶然注意到的,或者是在内核更新后的7年中作为其他一些变化的一部分修复的


使用write()的无缺陷内核最终会从未映射的页面进行读取,会发生什么 绝对不记录在错误参数上发出信号的可能性,仅记录错误代码,如
EFAULT

我无法在带有x86-64 Linux内核5.0.1的Arch Linux上复制segfault;我会写入预期的垃圾,然后
write(2)
返回在垃圾到达未映射页面之前写入的字节数。然后继续执行,直到退出(5)系统调用,进程干净地退出,状态为5

我认为,当传递包含未映射页面的指针+大小时,即使在写入一些字节后,
write
也可能返回
-EFAULT
,但事实并非如此。手册页中的措辞没有提到这一具体情况,但通过书写部分检测到的其他错误如何处理的措辞与此一致。(通常,这些错误是由于磁盘变满或管道另一端关闭等原因造成的。)

请注意,成功的write()传输的字节数可能少于count字节。这种部分写入可能由于各种原因而发生

如果发生 部分写入,调用方可以进行另一个write()调用来传输剩余的字节。后续调用将传输更多字节 或者可能导致错误(例如,如果磁盘现在已满)

当您这样做时,Linux肯定不会一直传输到最后一个映射页面的末尾。但有趣的是,看看在不同的情况下会发生什么

它似乎是分块复制的,并在执行过程中检查每个块的可读性。当区块从未映射的页面读取时,会检测到错误,并返回部分写入。如果您用
address=buf+first\u retval
再次拨打电话,您可能会得到
-EFAULT
。因此,这非常类似于用部分写入填充磁盘,然后在尝试写入其余部分时通过获取
-ENOSPC
来检测它

将输出重定向到x86-64 Linux 5.0.1上的文件(在
tmpfs
中),我得到的
write()
大小为4078
4096-18=4078
,我使用了最近的
ld
(Binutils 2.32),因此
.data
部分在可执行文件中是4k对齐的,并且该部分的开头在内存中也是页面对齐的。因此,页面的结尾位于
var2+4096-len1

$ strace ./2write > foo
strace: [ Process PID=28961 runs in 32 bit mode. ]
write(1, "This is message1\n\0", 18)    = 18
write(1, "This is message2\n\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 134520850) = 4078
write(1, "This is message1\n\0", 18)    = 18
write(1, "This is message2\n\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 134520850) = 4078
exit(5)                                 = ?
+++ exited with 5 +++
与写入终端相比,我得到的大小为
2048

与写入
/dev/null
相比,我通过写入返回
134520850
获得了成功。
null
特殊块设备的驱动程序甚至不读取用户空间内存,它只是从
write
系统调用中返回成功,使其达到如此程度。因此,任何东西都不会检查
-EFAULT

通过管道将输出传输到
wc
,我在第一个错误调用中得到了令人惊讶的18字节部分写入,在下一个错误调用中得到了
-EFAULT

strace ./2write | wc
execve("./2write", ["./2write"], 0x7ffdba771cf0 /* 53 vars */) = 0
strace: [ Process PID=29008 runs in 32 bit mode. ]
write(1, "This is message1\n\0", 18)    = 18
write(1, "This is message2\n\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 134520850) = 18
write(1, "This is message1\n\0", 18)    = 18
write(1, "This is message2\n\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 134520850) = -1 EFAULT (Bad address)
exit(5)                                 = ?
+++ exited with 5 +++
      3       9      54
在随后的程序运行中,我立即得到了
-EFAULT
。我猜Linux可能在第一次调用后为管道缓冲区分配了更多内存,因此它能够在复制任何数据之前,向前看足够远,立即注意到错误的地址

peter@volta:/tmp$ strace ./2write | wc
execve("./2write", ["./2write"], 0x7fff868a41b0 /* 53 vars */) = 0
strace: [ Process PID=29015 runs in 32 bit mode. ]
write(1, "This is message1\n\0", 18)    = 18
write(1, "This is message2\n\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 134520850) = -1 EFAULT (Bad address)
write(1, "This is message1\n\0", 18)    = 18
write(1, "This is message2\n\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 134520850) = -1 EFAULT (Bad address)
exit(5)                                 = ?
      2       6      36

由于使用了
mov-edx,var2
作为长度,因此会收到大量垃圾。指针是大整数。但是,在将一些可读数据复制到给定的文件描述符之后,一旦到达未映射的页面,系统调用最终应该只返回
-EFAULT
。(在Linux 5.0.1上,当它们从未映射页面停止时,实际上返回
2048
,而不是错误)。然后它就会回来。EAX=1/int 0x80是
sys\u exit
无法复制,在Linux上运行良好(打印文本+垃圾,然后以状态=5退出),在与NASM组装并链接到带有
gcc-no pie-nostdlib-m32
ld-melf\u i386
的32位静态可执行文件之后。使用调试器执行单个步骤。也许还可以尝试
strace
来查看
\u exit
系统调用发生了什么。是的,得到了错误,它是
mov edx,len2```而不是````mov edx,var2
谢谢@PeterCordesRight,但这并不能解释segfault。我无法复制。这是奇怪的部分。可能是我用edx寄存器描述的长度得到了一些未定义的值,这些值使用了一些其他进程使用的内存的私有空间,并给了我SEGFULT错误。
peter@volta:/tmp$ strace ./2write | wc
execve("./2write", ["./2write"], 0x7fff868a41b0 /* 53 vars */) = 0
strace: [ Process PID=29015 runs in 32 bit mode. ]
write(1, "This is message1\n\0", 18)    = 18
write(1, "This is message2\n\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 134520850) = -1 EFAULT (Bad address)
write(1, "This is message1\n\0", 18)    = 18
write(1, "This is message2\n\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 134520850) = -1 EFAULT (Bad address)
exit(5)                                 = ?
      2       6      36