You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

x86-64汇编read系统调用缓冲区溢出致Shell执行多余输入问题

x86-64汇编程序输入异常问题分析与修复

问题背景

在Linux x86-64汇编程序中,.bss段预留了16字节的name缓冲区用于读取用户姓名并输出问候信息。当输入字符超过16个时,多余的字符会被zsh立即执行。需要解答以下问题:

  • 这是否属于缓冲区溢出?
  • 为何多余字符会被执行而非程序崩溃?
  • 这些字符存储在何处?
  • 如何修复该问题?

程序源码

section .data
    question1   db "What is your name? "
    greeting    db "Hello, "

section .bss
    name        resb 16     ; Reserve 16 bytes in memory for "name"

section .text
    global _start           ; 定义程序入口标签

_start:
    call _printQuestion
    call _getName
    call _printGreeting
    call _printName
    
    ; 调用sys_exit退出程序
    mov     rax, 60         ; sys_exit的系统调用号
    mov     rdi, 0          ; 错误码:0表示无错误
    syscall                 ; 执行系统调用

_getName:
    mov     rax, 0          ; sys_read的系统调用号
    mov rdi, 0          ; 标准输入文件描述符
    mov rsi, name       
    mov rdx, 16             ; 最多读取16字节
    syscall
    ret

_printQuestion:
    mov     rax, 1          ; sys_write的系统调用号
    mov rdi, 1          ; 标准输出文件描述符
    mov rsi, question1
    mov rdx, 19             ; 输出字符串长度
    syscall
    ret

_printGreeting:
    mov     rax, 1
    mov rdi, 1
    mov rsi, greeting
    mov     rdx, 7          ; 输出字符串长度
    syscall
    ret

_printName:
    mov rax, 1
    mov rdi, 1
    mov rsi, name
    mov rdx, 16             ; 输出最多16字节
    syscall
    ret

问题解答

1. 是否属于缓冲区溢出?

不属于传统意义上导致程序内存被破坏的缓冲区溢出。你的程序中sys_read明确指定了读取16字节,内核会严格按照这个长度将数据写入name缓冲区,不会超出16字节覆盖程序内存的其他区域。你看到的异常是终端输入缓冲区的剩余数据被shell读取执行的行为,并非程序内存溢出导致的代码执行。

2. 为何多余字符会被执行而非程序崩溃?

Linux终端默认处于行缓冲模式:用户输入的整行字符(直到按下回车)会先被内核tty子系统存入输入队列。你的程序调用sys_read只读取了前16字节,剩余字符仍留在内核的终端输入队列中。当程序调用sys_exit退出后,父进程zsh会恢复终端控制权,然后从输入队列中读取剩余字符,将其当作shell命令执行——这就是你看到“多余字符被立即执行”的原因,并非程序本身执行了这些字符。

3. 这些字符存储在何处?

  • 程序读取的前16字节存储在.bss段的name缓冲区中;
  • 未被程序读取的多余字符存储在**内核管理的终端输入队列(tty输入缓冲区)**里,不属于你的程序进程内存空间。

4. 如何修复该问题?

核心思路是在程序退出前清空终端输入队列中剩余的未读取字符,避免shell读取这些字符当作命令。可以在_start的sys_exit前添加以下逻辑:

; 清空终端输入缓冲区的剩余字符
_flushInput:
    mov rax, 0          ; sys_read系统调用号
    mov rdi, 0          ; 标准输入文件描述符
    mov rsi, name       ; 复用name缓冲区(无需保存数据)
    mov rdx, 1          ; 每次读取1字节
    syscall
    cmp al, 0xA         ; 判断是否读到换行符(用户输入的回车)
    jne _flushInput     ; 未读到换行则继续读取

修改后的_start部分:

_start:
    call _printQuestion
    call _getName
    call _printGreeting
    call _printName

    ; 清空终端输入缓冲区剩余字符
_flushInput:
    mov rax, 0
    mov rdi, 0
    mov rsi, name
    mov rdx, 1
    syscall
    cmp al, 0xA
    jne _flushInput
    
    ; 退出程序
    mov     rax, 60
    mov     rdi, 0
    syscall

这个逻辑会持续读取输入直到遇到换行符,把用户输入的整行剩余字符全部读完,程序退出后shell的输入队列即为空,不会执行多余字符。

内容的提问来源于stack exchange,提问作者L. Landgraf

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 17:13:15