ตรวจพบสแตกยอดเยี่ยม


246

ฉันกำลังเรียกใช้ไฟล์ a.out ของฉัน หลังจากการดำเนินการโปรแกรมจะทำงานเป็นระยะเวลาหนึ่งแล้วออกจากพร้อมข้อความ:

**** stack smashing detected ***: ./a.out terminated*
*======= Backtrace: =========*
*/lib/tls/i686/cmov/libc.so.6(__fortify_fail+0x48)Aborted*

อะไรคือสาเหตุที่เป็นไปได้สำหรับสิ่งนี้และฉันจะแก้ไขได้อย่างไร


2
บางทีคุณสามารถระบุรหัสส่วนใดของคุณที่ทำให้สแตกยอดเยี่ยมและโพสต์ได้หรือไม่ จากนั้นเราอาจจะสามารถชี้ให้เห็นอย่างชัดเจนว่าทำไมมันเกิดขึ้นและวิธีการแก้ไข
Bjarke Freund-Hansen

ฉันคิดว่ามันเป็นคำพ้องกับข้อผิดพลาดล้น ตัวอย่างเช่นถ้าคุณเริ่มต้นและอาร์เรย์ของ 5 องค์ประกอบข้อผิดพลาดนี้จะปรากฏขึ้นเมื่อพยายามที่จะเขียนองค์ประกอบที่ 6 หรือองค์ประกอบใด ๆ ที่อยู่นอกขอบเขตของอาร์เรย์
DorinPopescu

คำตอบ:


349

Stack Smashing ที่นี่เกิดขึ้นจริงเนื่องจากกลไกการป้องกันที่ใช้โดย gcc เพื่อตรวจสอบข้อผิดพลาดบัฟเฟอร์ล้น ตัวอย่างเช่นในตัวอย่างต่อไปนี้:

#include <stdio.h>

void func()
{
    char array[10];
    gets(array);
}

int main(int argc, char **argv)
{
    func();
}

คอมไพเลอร์ (ในกรณีนี้ gcc) เพิ่มตัวแปรการป้องกัน (เรียกว่า canaries) ซึ่งมีค่าที่รู้จัก สตริงอินพุตที่มีขนาดใหญ่กว่า 10 ทำให้เกิดความเสียหายของตัวแปรนี้ส่งผลให้ SIGABRT ยกเลิกโปรแกรม

หากต้องการรับข้อมูลเชิงลึกคุณสามารถลองปิดใช้งานการป้องกัน gcc นี้โดยใช้ตัวเลือก -fno-stack-protector ขณะรวบรวม ในกรณีดังกล่าวคุณจะได้รับข้อผิดพลาดที่แตกต่างกันส่วนใหญ่เป็นความผิดพลาดของการแบ่งกลุ่มเมื่อคุณพยายามเข้าถึงตำแหน่งหน่วยความจำที่ผิดกฎหมาย โปรดทราบว่า-fstack-protectorควรเปิดใช้งานสำหรับรุ่นบิลด์เสมอเนื่องจากเป็นคุณลักษณะด้านความปลอดภัย

คุณสามารถรับข้อมูลบางอย่างเกี่ยวกับจุดไหลล้นโดยเรียกใช้โปรแกรมด้วยดีบักเกอร์ Valgrind ทำงานได้ไม่ดีกับข้อผิดพลาดเกี่ยวกับสแต็ก แต่เช่นเดียวกับดีบักเกอร์อาจช่วยให้คุณระบุตำแหน่งและเหตุผลของความผิดพลาดได้


3
ขอบคุณสำหรับคำตอบนี้! ฉันพบว่าในกรณีของฉันฉันไม่ได้เริ่มต้นตัวแปรที่ฉันพยายามเขียนถึง
Ted Pennings

5
Valgrind ทำงานได้ไม่ดีสำหรับข้อผิดพลาดเกี่ยวกับสแต็กเนื่องจากไม่สามารถเพิ่มโซนสีแดงได้
toasted_flakes

7
คำตอบนี้ไม่ถูกต้องและให้คำแนะนำที่เป็นอันตราย ก่อนอื่นการลบตัวป้องกันสแต็กไม่ใช่โซลูชันที่ถูกต้อง - ถ้าคุณได้รับข้อผิดพลาดในการสแต็กยอดเยี่ยมคุณอาจมีช่องโหว่ด้านความปลอดภัยที่ร้ายแรงในรหัสของคุณ การตอบสนองที่ถูกต้องคือการแก้ไขรหัสรถ ประการที่สองตามที่ grasGendarme ชี้ให้เห็นข้อเสนอแนะเพื่อลอง Valgrind จะไม่มีประสิทธิภาพ โดยทั่วไปแล้ว Valgrind จะไม่ทำงานสำหรับการตรวจจับการเข้าถึงหน่วยความจำที่ผิดกฎหมายไปยังข้อมูลที่จัดสรรไว้
DW

22
OP ขอเหตุผลที่เป็นไปได้สำหรับพฤติกรรมนี้คำตอบของฉันให้ตัวอย่างและวิธีการที่เกี่ยวข้องกับข้อผิดพลาดที่รู้จักพอสมควร นอกจากนี้การลบตัวป้องกันสแต็กไม่ใช่วิธีแก้ปัญหามันเป็นการเรียงลำดับของการทดลองที่สามารถทำได้เพื่อรับข้อมูลเชิงลึกเพิ่มเติมเกี่ยวกับปัญหา คำแนะนำที่แท้จริงคือการแก้ไขข้อผิดพลาดอย่างใดขอบคุณที่ชี้ให้เห็นเกี่ยวกับ valgrind ฉันจะแก้ไขคำตอบของฉันเพื่อสะท้อนถึงสิ่งนี้
sud03r

4
@DW ควรปิดการป้องกันสแต็กในรุ่นที่วางจำหน่ายเพราะในตอนแรกข้อความสแต็กที่ตรวจพบการสแต็กเป็นความช่วยเหลือสำหรับนักพัฒนาเท่านั้น วินาที - แอปพลิเคชันอาจมีโอกาสรอดชีวิต และที่สาม - นี่เป็นการเพิ่มประสิทธิภาพเล็กน้อย
Hi-Angel

33

ตัวอย่างการทำซ้ำที่น้อยที่สุดพร้อมการวิเคราะห์การแยกชิ้นส่วน

main.c

void myfunc(char *const src, int len) {
    int i;
    for (i = 0; i < len; ++i) {
        src[i] = 42;
    }
}

int main(void) {
    char arr[] = {'a', 'b', 'c', 'd'};
    int len = sizeof(arr);
    myfunc(arr, len + 1);
    return 0;
}

GitHub ต้นน้ำ

รวบรวมและเรียกใช้:

gcc -fstack-protector -g -O0 -std=c99 main.c
ulimit -c unlimited && rm -f core
./a.out

ล้มเหลวตามต้องการ:

*** stack smashing detected ***: ./a.out terminated
Aborted (core dumped)

ทดสอบบน Ubuntu 16.04, GCC 6.4.0

ถอดชิ้นส่วน

ตอนนี้เรามองไปที่การถอดชิ้นส่วน:

objdump -D a.out

ซึ่งประกอบด้วย:

int main (void){
  400579:       55                      push   %rbp
  40057a:       48 89 e5                mov    %rsp,%rbp

  # Allocate 0x10 of stack space.
  40057d:       48 83 ec 10             sub    $0x10,%rsp

  # Put the 8 byte canary from %fs:0x28 to -0x8(%rbp),
  # which is right at the bottom of the stack.
  400581:       64 48 8b 04 25 28 00    mov    %fs:0x28,%rax
  400588:       00 00 
  40058a:       48 89 45 f8             mov    %rax,-0x8(%rbp)

  40058e:       31 c0                   xor    %eax,%eax
    char arr[] = {'a', 'b', 'c', 'd'};
  400590:       c6 45 f4 61             movb   $0x61,-0xc(%rbp)
  400594:       c6 45 f5 62             movb   $0x62,-0xb(%rbp)
  400598:       c6 45 f6 63             movb   $0x63,-0xa(%rbp)
  40059c:       c6 45 f7 64             movb   $0x64,-0x9(%rbp)
    int len = sizeof(arr);
  4005a0:       c7 45 f0 04 00 00 00    movl   $0x4,-0x10(%rbp)
    myfunc(arr, len + 1);
  4005a7:       8b 45 f0                mov    -0x10(%rbp),%eax
  4005aa:       8d 50 01                lea    0x1(%rax),%edx
  4005ad:       48 8d 45 f4             lea    -0xc(%rbp),%rax
  4005b1:       89 d6                   mov    %edx,%esi
  4005b3:       48 89 c7                mov    %rax,%rdi
  4005b6:       e8 8b ff ff ff          callq  400546 <myfunc>
    return 0;
  4005bb:       b8 00 00 00 00          mov    $0x0,%eax
}
  # Check that the canary at -0x8(%rbp) hasn't changed after calling myfunc.
  # If it has, jump to the failure point __stack_chk_fail.
  4005c0:       48 8b 4d f8             mov    -0x8(%rbp),%rcx
  4005c4:       64 48 33 0c 25 28 00    xor    %fs:0x28,%rcx
  4005cb:       00 00 
  4005cd:       74 05                   je     4005d4 <main+0x5b>
  4005cf:       e8 4c fe ff ff          callq  400420 <__stack_chk_fail@plt>

  # Otherwise, exit normally.
  4005d4:       c9                      leaveq 
  4005d5:       c3                      retq   
  4005d6:       66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  4005dd:       00 00 00 

ขอให้สังเกตความคิดเห็นที่มีประโยชน์เพิ่มโดยอัตโนมัติโดยobjdump's โมดูลปัญญาประดิษฐ์

หากคุณเรียกใช้โปรแกรมนี้หลายครั้งผ่าน GDB คุณจะเห็นว่า:

  • นกขมิ้นจะได้รับค่าสุ่มที่แตกต่างกันทุกครั้ง
  • ห่วงสุดท้ายของmyfuncสิ่งที่แก้ไขที่อยู่ของนกขมิ้น

นกขมิ้นสุ่มโดยการตั้งค่าด้วย%fs:0x28ซึ่งมีค่าสุ่มตามที่อธิบายไว้ที่:

แก้ไขข้อผิดพลาด

จากนี้ไปเราจะแก้ไขโค้ด:

    myfunc(arr, len + 1);

เป็นแทน:

    myfunc(arr, len);
    myfunc(arr, len + 1); /* line 12 */
    myfunc(arr, len);

น่าสนใจมากขึ้น

จากนั้นเราจะพยายามดูว่าเราสามารถระบุการ+ 1เรียกผู้ร้ายด้วยวิธีอัตโนมัติได้มากกว่าการอ่านและทำความเข้าใจซอร์สโค้ดทั้งหมดหรือไม่

gcc -fsanitize=address เพื่อเปิดใช้งานการล้างที่อยู่ของ Google (ASan)

หากคุณคอมไพล์อีกครั้งด้วยแฟล็กนี้และรันโปรแกรมมันจะเอาต์พุต:

#0 0x4008bf in myfunc /home/ciro/test/main.c:4
#1 0x40099b in main /home/ciro/test/main.c:12
#2 0x7fcd2e13d82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
#3 0x400798 in _start (/home/ciro/test/a.out+0x40079

ตามด้วยเอาท์พุทสีเพิ่มเติม

สิ่งนี้ชี้ให้เห็นอย่างชัดเจนถึงบรรทัดที่มีปัญหา 12

ซอร์สโค้ดสำหรับสิ่งนี้อยู่ที่: https://github.com/google/sanitizersแต่ตามที่เราเห็นจากตัวอย่างมันอัปสตรีมเป็น GCC แล้ว

ASan ยังสามารถตรวจพบปัญหาหน่วยความจำอื่นเช่นการรั่วไหลของหน่วยความจำ: จะค้นหาหน่วยความจำรั่วในรหัส C ++ ได้อย่างไร

Valgrind SGCheck

ในฐานะที่เป็นที่กล่าวถึงโดยคนอื่น , Valgrind ไม่ดีที่การแก้ปัญหาชนิดนี้

มันมีเครื่องมือทดลองที่เรียกว่า SGCheck :

SGCheck เป็นเครื่องมือในการค้นหา overruns ของ stack และอาร์เรย์ระดับโลก มันทำงานโดยใช้วิธีการแก้ปัญหาที่ได้มาจากการสังเกตเกี่ยวกับรูปแบบที่น่าจะเป็นของสแต็กและการเข้าถึงอาร์เรย์ทั่วโลก

ดังนั้นฉันไม่แปลกใจมากเมื่อไม่พบข้อผิดพลาด:

valgrind --tool=exp-sgcheck ./a.out

ข้อความแสดงข้อผิดพลาดควรมีลักษณะดังนี้: Valgrind error error

GDB

ข้อสังเกตที่สำคัญคือถ้าคุณเรียกใช้โปรแกรมผ่าน GDB หรือตรวจสอบcoreไฟล์หลังจากข้อเท็จจริง:

gdb -nh -q a.out core

จากนั้นตามที่เราเห็นในชุดประกอบ GDB ควรนำคุณไปยังจุดสิ้นสุดของฟังก์ชั่นที่ทำการตรวจสอบนกขมิ้น:

(gdb) bt
#0  0x00007f0f66e20428 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
#1  0x00007f0f66e2202a in __GI_abort () at abort.c:89
#2  0x00007f0f66e627ea in __libc_message (do_abort=do_abort@entry=1, fmt=fmt@entry=0x7f0f66f7a49f "*** %s ***: %s terminated\n") at ../sysdeps/posix/libc_fatal.c:175
#3  0x00007f0f66f0415c in __GI___fortify_fail (msg=<optimized out>, msg@entry=0x7f0f66f7a481 "stack smashing detected") at fortify_fail.c:37
#4  0x00007f0f66f04100 in __stack_chk_fail () at stack_chk_fail.c:28
#5  0x00000000004005f6 in main () at main.c:15
(gdb) f 5
#5  0x00000000004005f6 in main () at main.c:15
15      }
(gdb)

และดังนั้นจึงมีปัญหาน่าจะเกิดจากหนึ่งในสายที่ฟังก์ชั่นนี้ทำ

ต่อไปเราพยายามที่จะระบุการเรียกที่ล้มเหลวโดยการก้าวขึ้นครั้งแรกหลังจากที่นกขมิ้นถูกตั้งค่า:

  400581:       64 48 8b 04 25 28 00    mov    %fs:0x28,%rax
  400588:       00 00 
  40058a:       48 89 45 f8             mov    %rax,-0x8(%rbp)

และดูที่อยู่:

(gdb) p $rbp - 0x8
$1 = (void *) 0x7fffffffcf18
(gdb) watch 0x7fffffffcf18
Hardware watchpoint 2: *0x7fffffffcf18
(gdb) c
Continuing.

Hardware watchpoint 2: *0x7fffffffcf18

Old value = 1800814336
New value = 1800814378
myfunc (src=0x7fffffffcf14 "*****?Vk\266", <incomplete sequence \355\216>, len=5) at main.c:3
3           for (i = 0; i < len; ++i) {
(gdb) p len
$2 = 5
(gdb) p i
$3 = 4
(gdb) bt
#0  myfunc (src=0x7fffffffcf14 "*****?Vk\266", <incomplete sequence \355\216>, len=5) at main.c:3
#1  0x00000000004005cc in main () at main.c:12

ตอนนี้ไม่ใบเราได้ที่การเรียนการสอนที่กระทำผิดขวา: len = 5และi = 4และในกรณีนี้โดยเฉพาะได้จุดที่เราจะกระทำผิดเส้น 12

อย่างไรก็ตาม backtrace เสียหายและมีถังขยะอยู่บ้าง backtrace ที่ถูกต้องจะมีลักษณะดังนี้:

#0  myfunc (src=0x7fffffffcf14 "abcd", len=4) at main.c:3
#1  0x00000000004005b8 in main () at main.c:11

ดังนั้นนี่อาจทำให้สแต็กเสียหายและป้องกันไม่ให้คุณเห็นร่องรอย

นอกจากนี้วิธีนี้ต้องรู้ว่าสิ่งที่เป็นสายสุดท้ายของฟังก์ชั่นการตรวจสอบนกขมิ้นมิฉะนั้นคุณจะมีผลบวกปลอมซึ่งจะไม่เคยเป็นไปได้ถ้าคุณใช้การแก้จุดบกพร่องกลับ


16

โปรดดูสถานการณ์ต่อไปนี้:

ab@cd-x:$ cat test_overflow.c 
#include <stdio.h>
#include <string.h>

int check_password(char *password){
    int flag = 0;
    char buffer[20];
    strcpy(buffer, password);

    if(strcmp(buffer, "mypass") == 0){
        flag = 1;
    }
    if(strcmp(buffer, "yourpass") == 0){
        flag = 1;
    }
    return flag;
}

int main(int argc, char *argv[]){
    if(argc >= 2){
        if(check_password(argv[1])){
            printf("%s", "Access granted\n");
        }else{
            printf("%s", "Access denied\n");
        }
    }else{
        printf("%s", "Please enter password!\n");
    }
}
ab@cd-x:$ gcc -g -fno-stack-protector test_overflow.c 
ab@cd-x:$ ./a.out mypass
Access granted
ab@cd-x:$ ./a.out yourpass
Access granted
ab@cd-x:$ ./a.out wepass
Access denied
ab@cd-x:$ ./a.out wepassssssssssssssssss
Access granted

ab@cd-x:$ gcc -g -fstack-protector test_overflow.c 
ab@cd-x:$ ./a.out wepass
Access denied
ab@cd-x:$ ./a.out mypass
Access granted
ab@cd-x:$ ./a.out yourpass
Access granted
ab@cd-x:$ ./a.out wepassssssssssssssssss
*** stack smashing detected ***: ./a.out terminated
======= Backtrace: =========
/lib/tls/i686/cmov/libc.so.6(__fortify_fail+0x48)[0xce0ed8]
/lib/tls/i686/cmov/libc.so.6(__fortify_fail+0x0)[0xce0e90]
./a.out[0x8048524]
./a.out[0x8048545]
/lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe6)[0xc16b56]
./a.out[0x8048411]
======= Memory map: ========
007d9000-007f5000 r-xp 00000000 08:06 5776       /lib/libgcc_s.so.1
007f5000-007f6000 r--p 0001b000 08:06 5776       /lib/libgcc_s.so.1
007f6000-007f7000 rw-p 0001c000 08:06 5776       /lib/libgcc_s.so.1
0090a000-0090b000 r-xp 00000000 00:00 0          [vdso]
00c00000-00d3e000 r-xp 00000000 08:06 1183       /lib/tls/i686/cmov/libc-2.10.1.so
00d3e000-00d3f000 ---p 0013e000 08:06 1183       /lib/tls/i686/cmov/libc-2.10.1.so
00d3f000-00d41000 r--p 0013e000 08:06 1183       /lib/tls/i686/cmov/libc-2.10.1.so
00d41000-00d42000 rw-p 00140000 08:06 1183       /lib/tls/i686/cmov/libc-2.10.1.so
00d42000-00d45000 rw-p 00000000 00:00 0 
00e0c000-00e27000 r-xp 00000000 08:06 4213       /lib/ld-2.10.1.so
00e27000-00e28000 r--p 0001a000 08:06 4213       /lib/ld-2.10.1.so
00e28000-00e29000 rw-p 0001b000 08:06 4213       /lib/ld-2.10.1.so
08048000-08049000 r-xp 00000000 08:05 1056811    /dos/hacking/test/a.out
08049000-0804a000 r--p 00000000 08:05 1056811    /dos/hacking/test/a.out
0804a000-0804b000 rw-p 00001000 08:05 1056811    /dos/hacking/test/a.out
08675000-08696000 rw-p 00000000 00:00 0          [heap]
b76fe000-b76ff000 rw-p 00000000 00:00 0 
b7717000-b7719000 rw-p 00000000 00:00 0 
bfc1c000-bfc31000 rw-p 00000000 00:00 0          [stack]
Aborted
ab@cd-x:$ 

เมื่อฉันปิดการใช้งานตัวป้องกันการยอดเยี่ยมแบบสแต็กไม่พบข้อผิดพลาดซึ่งควรเกิดขึ้นเมื่อฉันใช้ "./a.out wepassssssssssssssssssss"

ดังนั้นเพื่อตอบคำถามของคุณด้านบนข้อความ "** ตรวจพบการทับซ้อนของสแต็ก: xxx" ปรากฏขึ้นเนื่องจากตัวป้องกันการยอดเยี่ยมซ้อนของคุณทำงานอยู่และพบว่ามีสแต็กล้นในโปรแกรมของคุณ

เพียงค้นหาว่าเกิดอะไรขึ้นและแก้ไข


7

คุณสามารถลองแก้ไขข้อบกพร่องโดยใช้valgrind :

ปัจจุบันการแจกจ่าย Valgrind ประกอบด้วยเครื่องมือคุณภาพการผลิตหกรายการ ได้แก่ ตัวตรวจจับข้อผิดพลาดของหน่วยความจำตัวตรวจจับข้อผิดพลาดของเธรดสองตัวแคชและตัวสร้างการคาดคะเนสาขาการสร้างกราฟการสร้างแคชการสร้างโปรไฟล์ นอกจากนี้ยังมีเครื่องมือทดลองสองชุด: ตัว ตรวจจับการโอเวอร์เฮด/ กองซ้อน / โกลบอลและตัวสร้างเวกเตอร์บล็อกพื้นฐาน SimPoint มันทำงานบนแพลตฟอร์มดังต่อไปนี้: X86 / Linux, AMD64 / Linux, PPC32 / Linux, PPC64 / Linux และ X86 / Darwin (Mac OS X)


2
ใช่ แต่ Valgrind ใช้งานไม่ได้กับบัฟเฟอร์ล้นสแต็กซึ่งเป็นสถานการณ์ที่ข้อความแสดงข้อผิดพลาดนี้บ่งชี้
DW

4
เราจะใช้ตัวตรวจจับการบุกรุกที่ซ้อนทับของอาร์เรย์ได้อย่างไร คุณสามารถทำอย่างละเอียด?
Craig McQueen

@CraigMcQueen ฉันพยายามใช้ SGHeck heuristic SGCheck สแต็คตรวจหาสแมชชิ่งในตัวอย่างเล็ก ๆ น้อย ๆ : stackoverflow.com/a/51897264/895245แต่ล้มเหลว
Ciro Santilli 郝海东冠状病六四事件法轮功

4

ก็หมายความว่าคุณเขียนถึงตัวแปรบางอย่างเกี่ยวกับสแต็คในทางที่ผิดกฎหมายส่วนใหญ่น่าจะเป็นผลจากการที่หน่วยความจำล้น


9
Stack overflow คือ stack ที่ดีที่สุด นี่คือวิธีอื่น ๆ : มีบางสิ่งบางอย่างแตกเป็นกอง ๆ
Peter Mortensen

5
ไม่ได้จริงๆ มันเป็นส่วนหนึ่งของกองที่ยอดเยี่ยมเข้าไปในส่วนอื่น ดังนั้นมันจึงเป็นบัฟเฟอร์โอเวอร์โฟลไม่เพียง แต่อยู่ด้านบนสุดของสแต็ก แต่ "เท่านั้น" ในส่วนอื่นของสแต็ก
Bas Wijnen

2

อะไรคือสาเหตุที่เป็นไปได้สำหรับสิ่งนี้และฉันจะแก้ไขได้อย่างไร

สถานการณ์หนึ่งจะอยู่ในตัวอย่างต่อไปนี้:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void swap ( char *a , char *b );
void revSTR ( char *const src );

int main ( void ){
    char arr[] = "A-B-C-D-E";

    revSTR( arr );
    printf("ARR = %s\n", arr );
}

void swap ( char *a , char *b ){
    char tmp = *a;
    *a = *b;
    *b = tmp;
}

void revSTR ( char *const src ){
    char *start = src;
    char *end   = start + ( strlen( src ) - 1 );

    while ( start < end ){
        swap( &( *start ) , &( *end ) );
        start++;
        end--;
    }
}

ในโปรแกรมนี้คุณสามารถย้อนกลับสตริงหรือส่วนหนึ่งของสตริงถ้าคุณเช่นโทรreverse()ด้วยสิ่งนี้:

reverse( arr + 2 );

หากคุณตัดสินใจที่จะส่งผ่านความยาวของอาร์เรย์ดังนี้:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void swap ( char *a , char *b );
void revSTR ( char *const src, size_t len );

int main ( void ){
    char arr[] = "A-B-C-D-E";
    size_t len = strlen( arr );

    revSTR( arr, len );
    printf("ARR = %s\n", arr );
}

void swap ( char *a , char *b ){
    char tmp = *a;
    *a = *b;
    *b = tmp;
}

void revSTR ( char *const src, size_t len ){
    char *start = src;
    char *end   = start + ( len - 1 );

    while ( start < end ){
        swap( &( *start ) , &( *end ) );
        start++;
        end--;
    }
}

ทำงานได้ดีเช่นกัน

แต่เมื่อคุณทำสิ่งนี้:

revSTR( arr + 2, len );

คุณจะได้รับ:

==7125== Command: ./program
==7125== 
ARR = A-
*** stack smashing detected ***: ./program terminated
==7125== 
==7125== Process terminating with default action of signal 6 (SIGABRT)
==7125==    at 0x4E6F428: raise (raise.c:54)
==7125==    by 0x4E71029: abort (abort.c:89)
==7125==    by 0x4EB17E9: __libc_message (libc_fatal.c:175)
==7125==    by 0x4F5311B: __fortify_fail (fortify_fail.c:37)
==7125==    by 0x4F530BF: __stack_chk_fail (stack_chk_fail.c:28)
==7125==    by 0x400637: main (program.c:14)

และสิ่งนี้เกิดขึ้นเพราะในรหัสแรกความยาวของarrถูกตรวจสอบภายในrevSTR()ซึ่งเป็นเรื่องดี แต่ในรหัสที่สองที่คุณผ่านความยาว:

revSTR( arr + 2, len );

ตอนนี้ความยาวก็จะยาวตามจริงแล้วความยาวที่คุณจะผ่านเมื่อคุณพูด arr + 2ความยาวอยู่ในขณะนี้อีกต่อไปแล้วความยาวจริงคุณผ่านเมื่อคุณพูด

ความยาวของstrlen ( arr + 2 )! strlen ( arr )=


1
ฉันเหมือนตัวอย่างนี้เพราะมันไม่ได้ขึ้นอยู่กับการทำงานห้องสมุดมาตรฐานเช่นและgets scrcpyฉันสงสัยว่าถ้าเราลดให้น้อยลงไปอีก ฉันจะอย่างน้อยกำจัดด้วยstring.h size_t len = sizeof( arr );ทดสอบกับ gcc 6.4, Ubuntu 16.04 ฉันจะให้ตัวอย่างที่ล้มเหลวด้วยการarr + 2เพื่อลดการคัดลอกวาง
Ciro Santilli 郝海东冠状病六四事件法轮功

1

ความเสียหายของสแต็กที่เกิดจากการใช้บัฟเฟอร์มากเกินไป คุณสามารถป้องกันพวกเขาโดยการตั้งโปรแกรมป้องกัน

เมื่อใดก็ตามที่คุณเข้าถึงอาเรย์ใส่ assert มาก่อนเพื่อให้แน่ใจว่าการเข้าถึงนั้นไม่อยู่นอกขอบเขต ตัวอย่างเช่น:

assert(i + 1 < N);
assert(i < N);
a[i + 1] = a[i];

สิ่งนี้ทำให้คุณคิดเกี่ยวกับขอบเขตของอาร์เรย์และทำให้คุณคิดเกี่ยวกับการเพิ่มการทดสอบเพื่อกระตุ้นพวกเขาหากเป็นไปได้ ifหากบางส่วนของเหล่านี้อ้างสามารถล้มเหลวในระหว่างการใช้งานปกติเปิดให้เป็นปกติ


0

ฉันได้รับข้อผิดพลาดนี้ในขณะที่ใช้ malloc () เพื่อจัดสรรหน่วยความจำบางส่วนให้กับ struct * หลังจากใช้การดีบักโค้ดในที่สุดฉันก็ใช้ฟังก์ชั่น free () เพื่อเพิ่มหน่วยความจำที่จัดสรรแล้วก็เกิดข้อผิดพลาดขึ้น :)


0

แหล่งที่มาของการสแต็คยอดเยี่ยมก็คือการใช้ (ไม่ถูกต้อง) vfork()แทนfork()แทน

ฉันเพียงแค่บั๊กกรณีนี้ซึ่งกระบวนการเด็กก็ไม่สามารถไปปฏิบัติเป้าหมายและกลับรหัสข้อผิดพลาดแทนที่จะเรียกexecve()_exit()

เพราะvfork()กลับกลายเป็นเด็กคนนั้นมันกลับมาในขณะที่ยังคงดำเนินการภายในพื้นที่กระบวนการของผู้ปกครองไม่เพียง แต่ทำลายกองสแต็กของผู้ปกครอง แต่ก่อให้เกิดชุดการวินิจฉัยที่แตกต่างกันสองชุดที่จะพิมพ์ด้วยรหัส "ดาวน์สตรีม"

การเปลี่ยนแปลงvfork()ที่จะfork()ได้รับการแก้ไขปัญหาทั้งสองเช่นเดียวกับการเปลี่ยนแปลงของเด็กที่returnคำสั่งให้_exit()แทน

แต่เนื่องจากโค้ดลูกนำหน้าการexecve()เรียกด้วยการเรียกไปยังรูทีนอื่น (เพื่อตั้งค่า uid / gid ในกรณีนี้), ในทางเทคนิคแล้วมันไม่เป็นไปตามข้อกำหนดสำหรับvfork()ดังนั้นการเปลี่ยนการใช้fork()จึงถูกต้องที่นี่

(หมายเหตุว่าปัญหาreturnคำสั่งไม่ได้รหัสจริงเป็นเช่น - แทนแมโครถูกเรียกและแมโครที่ตัดสินใจว่าจะ_exit()หรือreturnขึ้นอยู่กับตัวแปรทั่วโลกดังนั้นมันก็ไม่ชัดเจนทันทีว่ารหัสเป็นเด็กที่ไม่ลงรอยกันสำหรับ.vfork()การใช้งาน )

สำหรับข้อมูลเพิ่มเติมดู:

ความแตกต่างระหว่าง fork (), vfork (), exec () และ clone ()

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.