linux - glibc स्कैनफुल सेगमेंटेशन दोष जब किसी फ़ंक्शन से बुलाया जाता है जो RSP को संरेखित नहीं करता है



assembly nasm (1)

कोड के नीचे संकलन करते समय:

global main
extern printf, scanf

section .data
   msg: db "Enter a number: ",10,0
   format:db "%d",0

section .bss
   number resb 4

section .text
main:
   mov rdi, msg
   mov al, 0
   call printf

   mov rsi, number
   mov rdi, format
   mov al, 0
   call scanf

   mov rdi,format
   mov rsi,[number]
   inc rsi
   mov rax,0
   call printf 

   ret

का उपयोग करते हुए:

nasm -f elf64 example.asm -o example.o
gcc -no-pie -m64 example.o -o example

और फिर चला

./example

यह चलता है, प्रिंट करें: एक नंबर दर्ज करें: लेकिन फिर क्रैश और प्रिंट करता है: विभाजन दोष (कोर डंप)

तो printf ठीक काम करता है, लेकिन scanf नहीं। मैं स्कैनफ के साथ गलत क्या कर रहा हूँ?


अपने फ़ंक्शन को call से पहले स्टैक को 16 बाइट्स में फिर से संरेखित करने के लिए अपने फ़ंक्शन के प्रारंभ / अंत में sub rsp, 8 / add rsp, 8

या एक डमी रजिस्टर को बेहतर पुश / पॉप करें, उदाहरण के push rdx / pop rcx push rdx , या push rdx जैसे कॉल-संरक्षित रजिस्टर को सहेजें / पुनर्स्थापित करें।

फ़ंक्शन प्रविष्टि पर, RSP 16-बाइट संरेखण से 8 बाइट्स है क्योंकि call ने 8-बाइट वापसी पते को धकेल दिया। X86-64 से प्रिंटिंग फ्लोटिंग पॉइंट नंबर देखें, लगता है कि GNU असेंबलर का उपयोग करके x86_64 में % rbp को सेव , मेन और स्टैक अलाइनमेंट और कॉलिंग प्रिंटफ की आवश्यकता है । यह एक ABI आवश्यकता है जिसका उपयोग आप उल्लंघन करने में सक्षम होने के लिए करते थे जब प्रिंटफ के लिए कोई भी एफपी आर्ग नहीं थे। लेकिन अब और नहीं।

glibc scanf के लिए gcc का कोड-जीन अब AL == 0 पर भी 16-बाइट स्टैक अलाइनमेंट पर निर्भर करता है

ऐसा लगता है कि __GI__IO_vfscanf में कहीं न कहीं 16 बाइट्स की ऑटो- __GI__IO_vfscanf कॉपी की __GI__IO_vfscanf , जो कि नियमित __GI__IO_vfscanf अपने रजिस्टर आर्गन्स को स्टैक 1 पर फैलाने के बाद कॉल करता है। (स्कैनफ को कॉल करने के कई समान तरीके scanf , fscanf , आदि जैसे विभिन्न libc प्रविष्टि बिंदुओं के पीछे के अंत के रूप में एक बड़ा कार्यान्वयन साझा करते हैं)

मैंने Ubuntu 18.04 का libc6 बाइनरी पैकेज डाउनलोड किया: https://packages.ubuntu.com/bionic/amd64/libc6/download और फ़ाइलों को निकाला ( 7z x blah.deb और tar xf data.tar 7z x blah.deb साथ, क्योंकि 7z जानता है कि कैसे फ़ाइल स्वरूपों का एक बहुत निकालें)।

मैं आपके बग को LD_LIBRARY_PATH=/tmp/bionic-libc/lib/x86_64-linux-gnu ./bad-printf , साथ ही यह मेरे आर्क लिनक्स डेस्कटॉप पर सिस्टम glcc 2.27-3 के साथ बदल जाता है।

GDB के साथ, मैंने इसे आपके प्रोग्राम पर चलाया और set env LD_LIBRARY_PATH /tmp/bionic-libc/lib/x86_64-linux-gnu फिर सेट किया। layout reg साथ, डिस्सैम्ड विंडो इस बिंदु पर इस तरह दिखती है, जहां उसे SIGSEGV प्राप्त हुआ:

   │0x7ffff786b49a <_IO_vfscanf+602>        cmp    r12b,0x25                                                                                             │
   │0x7ffff786b49e <_IO_vfscanf+606>        jne    0x7ffff786b3ff <_IO_vfscanf+447>                                                                      │
   │0x7ffff786b4a4 <_IO_vfscanf+612>        mov    rax,QWORD PTR [rbp-0x460]                                                                             │
   │0x7ffff786b4ab <_IO_vfscanf+619>        add    rax,QWORD PTR [rbp-0x458]                                                                             │
   │0x7ffff786b4b2 <_IO_vfscanf+626>        movq   xmm0,QWORD PTR [rbp-0x460]                                                                            │
   │0x7ffff786b4ba <_IO_vfscanf+634>        mov    DWORD PTR [rbp-0x678],0x0                                                                             │
   │0x7ffff786b4c4 <_IO_vfscanf+644>        mov    QWORD PTR [rbp-0x608],rax                                                                             │
   │0x7ffff786b4cb <_IO_vfscanf+651>        movzx  eax,BYTE PTR [rbx+0x1]                                                                                │
   │0x7ffff786b4cf <_IO_vfscanf+655>        movhps xmm0,QWORD PTR [rbp-0x608]                                                                            │
  >│0x7ffff786b4d6 <_IO_vfscanf+662>        movaps XMMWORD PTR [rbp-0x470],xmm0                                                                          │

इसलिए इसने दो 8-बाइट ऑब्जेक्ट्स को स्टैक करने के लिए movq + movhps साथ स्टैक किया और स्टोर करने के लिए movaps किया। लेकिन स्टैक के साथ गुमराह किया गया, movaps [rbp-0x470],xmm0 दोष।

मैंने यह जानने के लिए एक डीबग बिल्ड नहीं पकड़ा कि C स्रोत का कौन सा भाग इस में बदल गया, लेकिन फ़ंक्शन C में लिखा गया है और GCC द्वारा ऑप्टिमाइज़ किए गए ऑप्टिमाइज़ किए गए हैं। जीसीसी को हमेशा ऐसा करने की अनुमति दी गई है, लेकिन केवल हाल ही में इस तरह से एसएसई 2 का बेहतर लाभ उठाने के लिए पर्याप्त स्मार्ट हो गया।

फुटनोट 1: AL != 0 साथ प्रिंटफ / स्कैनफ AL != 0 को हमेशा 16-बाइट संरेखण की आवश्यकता होती है क्योंकि वेरिक कार्यों के लिए gcc का कोड-जीन परीक्षण अल, अल / जेई का उपयोग करता है, पूर्ण 16-बाइट XMM को xmm0..7 के साथ संरेखित स्टोर के साथ फैलाने के लिए। उस मामले में। __m128i एक वेरिएडिक फंक्शन का एक तर्क हो सकता है, न कि केवल double , और gcc यह चेक नहीं करता है कि फंक्शन कभी भी किसी 16-बाइट एफपी आर्ग्स को पढ़ता है या नहीं।





calling-convention