linux - glibc scanf أخطاء التجزئة عند استدعائها من وظيفة لا تتوافق مع 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 لا. ماذا أفعل الخطأ مع scanf ذلك؟


استخدم sub rsp, 8 / add rsp, 8 في بداية / نهاية add rsp, 8 لإعادة محاذاة المكدس إلى 16 بايت قبل أن تقوم add rsp, 8 بإجراء call .

أو من الأفضل الضغط / تسجيل سجل وهمية ، على سبيل المثال push rdx / pop rcx ، أو حفظ / استعادة سجل محفوظ بالمكالمات مثل RBP.

عند إدخال الوظيفة ، يبعد RSP 8 بايت عن محاذاة 16 بايت لأن call دفعت عنوان إرجاع 8 بايت. انظر طباعة أرقام الفاصلة العائمة من x86-64 يبدو أنه يتطلب حفظ٪ rbp ، والمحاذاة الرئيسية والمكدسة ، والاتصال بـ printf في x86_64 باستخدام مجمّع غنو . هذا أحد متطلبات ABI التي اعتدت أن تكون قادرًا على الابتعاد عن انتهاكها عندما لا يكون هناك أي FP يدافع عن printf. لكن ليس بعد الآن.

يعتمد كود gcc الخاص بـ glibc scanf الآن على محاذاة مكدس سعة 16 بايت حتى عند AL == 0 .

يبدو أنه يحتوي على __GI__IO_vfscanf 16 بايت __GI__IO_vfscanf في مكان ما في __GI__IO_vfscanf ، والذي يستدعي إجراء __GI__IO_vfscanf منتظم بعد تسرب سجله إلى المكدس 1 . (تشترك العديد من الطرق المشابهة للاتصال بـ scanf في تطبيق واحد كبير كنهاية خلفية لنقاط إدخال libc المختلفة مثل scanf ، fscanf ، إلخ.)

قمت بتنزيل الحزمة الثنائية libc6 لـ Ubuntu 18.04: https://packages.ubuntu.com/bionic/amd64/libc6/download واستخراج الملفات (باستخدام 7z x blah.deb و tar xf data.tar ، لأن 7z يعرف كيفية استخراج الكثير من تنسيقات الملفات).

يمكنني LD_LIBRARY_PATH=/tmp/bionic-libc/lib/x86_64-linux-gnu ./bad-printf الخطأ الخاص بك باستخدام LD_LIBRARY_PATH=/tmp/bionic-libc/lib/x86_64-linux-gnu ./bad-printf ، كما يتحول إلى نظام glibc 2.27-3 على سطح مكتب Arch Linux الخاص بي.

مع GDB ، قمت set env LD_LIBRARY_PATH /tmp/bionic-libc/lib/x86_64-linux-gnu على البرنامج وقمت set env LD_LIBRARY_PATH /tmp/bionic-libc/lib/x86_64-linux-gnu ثم قم run . مع 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 أخطاء movaps [rbp-0x470],xmm0 .

لم أحصل على تصحيح أخطاء لمعرفة بالضبط أي جزء من مصدر C تحول إلى ذلك ، لكن الوظيفة مكتوبة بلغة C وتم تجميعها بواسطة GCC مع تمكين التحسين. تم السماح لدول مجلس التعاون الخليجي دائمًا بالقيام بذلك ، ولكن في الآونة الأخيرة فقط أصبحت ذكية بما يكفي للاستفادة بشكل أفضل من SSE2 بهذه الطريقة.

الحاشية 1: printf / scanf مع AL != 0 تطلب دائمًا محاذاة 16 بايت لأن الكود gcc الخاص بالوظائف varadic يستخدم اختبار al ، al / je لإسكاب سجلات XMM الكاملة 16 بايت xmm0..7 مع المخازن المتوافقة في هذه الحالة. __m128i يمكن أن تكون وسيطة لدالة varadic ، وليس فقط double ، و gcc لا تتحقق مما إذا كانت الوظيفة تقرأ بالفعل أي وسيطة FP 16 بايت.





calling-convention