Stack smashing terdeteksi


246

Saya menjalankan file a.out saya. Setelah eksekusi, program berjalan selama beberapa waktu kemudian keluar dengan pesan:

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

Apa yang mungkin menjadi alasan untuk ini dan bagaimana cara memperbaikinya?


2
Bisakah Anda mengidentifikasi bagian mana dari kode Anda yang menyebabkan stack menabrak, dan mempostingnya? Maka kita mungkin akan dapat menunjukkan dengan tepat mengapa itu terjadi dan bagaimana cara memperbaikinya.
Bjarke Freund-Hansen

Saya pikir itu sinonim dengan kesalahan overflow. Misalnya jika Anda menginisialisasi dan array 5 elemen kesalahan ini akan muncul ketika mencoba untuk menulis elemen ke-6, atau elemen apa pun di luar batas array.
DorinPopescu

Jawaban:


349

Stack Smashing di sini sebenarnya disebabkan karena mekanisme perlindungan yang digunakan oleh gcc untuk mendeteksi kesalahan buffer overflow. Misalnya dalam cuplikan berikut:

#include <stdio.h>

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

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

Kompiler, (dalam hal ini gcc) menambahkan variabel perlindungan (disebut kenari) yang memiliki nilai yang diketahui. String input dengan ukuran lebih besar dari 10 menyebabkan korupsi pada variabel ini yang mengakibatkan SIGABRT menghentikan program.

Untuk mendapatkan beberapa wawasan, Anda dapat mencoba menonaktifkan perlindungan gcc ini menggunakan opsi -fno-stack-protector saat kompilasi. Dalam hal ini Anda akan mendapatkan kesalahan yang berbeda, kemungkinan besar kesalahan segmentasi saat Anda mencoba mengakses lokasi memori ilegal. Catatan yang -fstack-protectorharus selalu dinyalakan untuk rilis build karena merupakan fitur keamanan.

Anda bisa mendapatkan beberapa informasi tentang titik overflow dengan menjalankan program dengan debugger. Valgrind tidak bekerja dengan baik dengan kesalahan yang berhubungan dengan stack, tetapi seperti debugger, ini dapat membantu Anda menunjukkan lokasi dan alasan crash.


3
terima kasih atas jawaban ini! Saya menemukan bahwa dalam kasus saya, saya belum menginisialisasi variabel yang saya coba tulis
Ted Pennings

5
Valgrind tidak bekerja dengan baik untuk kesalahan yang berhubungan dengan stack, karena tidak dapat menambahkan zona merah di sana
toasted_flakes

7
Jawaban ini salah, dan memberikan saran berbahaya. Pertama-tama, menghapus stack protector bukanlah solusi yang tepat - jika Anda mendapatkan kesalahan stack stack, Anda mungkin memiliki kerentanan keamanan serius dalam kode Anda. Respons yang benar adalah memperbaiki kode kereta . Kedua, seperti yang ditunjukkan oleh grasGendarme, rekomendasi untuk mencoba Valgrind tidak akan efektif. Valgrind biasanya tidak bekerja untuk mendeteksi akses memori ilegal ke data yang dialokasikan stack.
DW

22
OP menanyakan kemungkinan alasan untuk perilaku ini, jawaban saya memberikan contoh dan bagaimana kaitannya dengan kesalahan yang cukup diketahui. Selain itu, menghapus stack-protector bukanlah solusi, ini semacam eksperimen yang bisa dilakukan untuk mendapatkan lebih banyak wawasan tentang masalah tersebut. Saran sebenarnya adalah untuk memperbaiki kesalahan entah bagaimana, terima kasih telah menunjukkan tentang valgrind, saya akan mengedit jawaban saya untuk mencerminkan ini.
sud03r

4
@DW perlindungan tumpukan harus dimatikan dalam versi rilis, karena pada awalnya - pesan pemecahan tumpukan terdeteksi adalah bantuan hanya untuk pengembang; pada detik - aplikasi bisa memiliki peluang untuk bertahan; dan ketiga - ini adalah optimasi kecil.
Hi-Angel

33

Contoh reproduksi minimal dengan analisis pembongkaran

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 hulu .

Kompilasi dan jalankan:

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

gagal seperti yang diinginkan:

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

Diuji pada Ubuntu 16.04, GCC 6.4.0.

Membongkar

Sekarang kita melihat pembongkaran:

objdump -D a.out

yang mengandung:

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 

Perhatikan komentar berguna secara otomatis ditambahkan oleh objdump's modul kecerdasan buatan .

Jika Anda menjalankan program ini beberapa kali melalui GDB, Anda akan melihat bahwa:

  • kenari mendapat nilai acak yang berbeda setiap kali
  • loop terakhir myfuncadalah apa yang memodifikasi alamat kenari

Kenari diacak dengan menyetelnya %fs:0x28, yang berisi nilai acak seperti yang dijelaskan di:

Upaya debug

Mulai sekarang, kami memodifikasi kode:

    myfunc(arr, len + 1);

sebagai gantinya:

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

menjadi lebih menarik.

Kami kemudian akan mencoba untuk melihat apakah kami dapat menentukan + 1panggilan pelakunya dengan metode yang lebih otomatis daripada hanya membaca dan memahami seluruh kode sumber.

gcc -fsanitize=address untuk mengaktifkan Sanitizer Alamat Google (ASan)

Jika Anda mengkompilasi ulang dengan flag ini dan menjalankan program, itu output:

#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

diikuti oleh beberapa keluaran yang lebih berwarna.

Ini dengan jelas menunjukkan garis bermasalah 12.

Kode sumber untuk ini adalah di: https://github.com/google/sanitizers tetapi seperti yang kita lihat dari contoh, kode itu sudah di-upstream ke GCC.

ASan juga dapat mendeteksi masalah memori lain seperti kebocoran memori: Bagaimana menemukan kebocoran memori dalam kode / proyek C ++?

Valgrind SGCheck

Seperti yang disebutkan oleh orang lain , Valgrind tidak pandai memecahkan masalah seperti ini.

Itu memang memiliki alat percobaan yang disebut SGCheck :

SGCheck adalah alat untuk menemukan kelebihan tumpukan dan susunan global. Ini bekerja dengan menggunakan pendekatan heuristik yang berasal dari pengamatan tentang kemungkinan bentuk stack dan akses array global.

Jadi saya tidak terlalu terkejut ketika tidak menemukan kesalahan:

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

Pesan kesalahan akan terlihat seperti ini rupanya: Valgrind missing error

GDB

Pengamatan penting adalah bahwa jika Anda menjalankan program melalui GDB, atau memeriksa corefile setelah fakta:

gdb -nh -q a.out core

kemudian, seperti yang kita lihat di majelis, GDB akan mengarahkan Anda ke akhir fungsi yang melakukan pemeriksaan kenari:

(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)

Dan karena itu masalahnya kemungkinan ada di salah satu panggilan yang dibuat fungsi ini.

Selanjutnya kita mencoba untuk menentukan panggilan gagal yang tepat dengan langkah pertama setelah kenari ditetapkan:

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

dan memperhatikan alamat:

(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

Sekarang, ini benar-benar membuat kita berada pada instruksi yang menyinggung: len = 5dan i = 4, dalam kasus khusus ini, memang mengarahkan kita ke jalur pelakunya 12.

Namun, jejak balik rusak, dan berisi beberapa sampah. Backtrace yang benar akan terlihat seperti:

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

jadi mungkin ini bisa merusak tumpukan dan mencegah Anda melihat jejak.

Juga, metode ini membutuhkan mengetahui apa panggilan terakhir dari fungsi pemeriksaan kenari jika tidak Anda akan memiliki false positive, yang tidak akan selalu layak, kecuali jika Anda menggunakan debugging terbalik .


16

Silakan lihat situasi berikut:

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:$ 

Ketika saya menonaktifkan stack smashing protector, tidak ada kesalahan yang terdeteksi, yang seharusnya terjadi ketika saya menggunakan "./a.out wepassssssssssssssssssss"

Jadi untuk menjawab pertanyaan Anda di atas, pesan "** penghancuran tumpukan terdeteksi: xxx" ditampilkan karena pelindung penghancuran tumpukan Anda aktif dan menemukan bahwa ada kelebihan tumpukan di program Anda.

Cari tahu di mana itu terjadi, dan perbaiki.


7

Anda dapat mencoba men-debug masalah menggunakan valgrind :

Distribusi Valgrind saat ini mencakup enam alat berkualitas produksi: detektor kesalahan memori, dua detektor kesalahan benang, profiler cache dan prediksi cabang, profiler cache penghasil grafik panggilan, dan profiler tumpukan. Ini juga mencakup dua alat eksperimental: detektor heap / stack / overrun array global , dan generator vektor blok dasar SimPoint. Ini berjalan pada platform berikut: X86 / Linux, AMD64 / Linux, PPC32 / Linux, PPC64 / Linux, dan X86 / Darwin (Mac OS X).


2
Ya, tapi Valgrind tidak bekerja dengan baik untuk buffer buffer yang dialokasikan stack, yang merupakan situasi yang ditunjukkan oleh pesan kesalahan ini.
DW

4
Bagaimana kita bisa menggunakan stack overrun detector itu ? Bisakah Anda menguraikan?
Craig McQueen

@CraigMcQueen Saya sudah mencoba menggunakan heuristik eksperimental SGCheck stack smashing detector Valgrind pada contoh minimal: stackoverflow.com/a/51897264/895245 tetapi gagal.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

4

Itu berarti bahwa Anda menulis ke beberapa variabel di stack dengan cara ilegal, kemungkinan besar sebagai hasil dari Buffer overflow .


9
Stack overflow adalah tumpukan yang menabrak sesuatu yang lain. Ini dia sebaliknya: sesuatu telah menabrak tumpukan.
Peter Mortensen

5
Tidak juga. Ini adalah salah satu bagian dari tumpukan yang menabrak bagian lain. Jadi itu benar-benar buffer overflow, tidak hanya di atas tumpukan, tetapi "hanya" ke bagian lain dari tumpukan.
Bas Wijnen

2

Apa yang mungkin menjadi alasan untuk ini dan bagaimana cara memperbaikinya?

Satu skenario akan menjadi dalam contoh berikut:

#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--;
    }
}

Dalam program ini, Anda dapat membalikkan String atau bagian dari string jika misalnya Anda memanggil reverse()dengan sesuatu seperti ini:

reverse( arr + 2 );

Jika Anda memutuskan untuk melewatkan panjang array seperti ini:

#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--;
    }
}

Bekerja dengan baik juga.

Tetapi ketika Anda melakukan ini:

revSTR( arr + 2, len );

Anda mendapatkan:

==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)

Dan ini terjadi karena dalam kode pertama, panjang arrdiperiksa di revSTR()dalamnya baik-baik saja, tetapi dalam kode kedua di mana Anda melewati panjang:

revSTR( arr + 2, len );

Panjangnya sekarang lebih panjang dari panjang sebenarnya yang Anda lewati ketika Anda mengatakannya arr + 2.

Panjang strlen ( arr + 2 )! = strlen ( arr ).


1
Saya suka contoh ini karena tidak bergantung pada fungsi perpustakaan standar seperti getsdan scrcpy. Saya ingin tahu apakah kita bisa memperkecil jika lebih jauh. Saya akan setidaknya menyingkirkan string.hdengan size_t len = sizeof( arr );. Diuji pada gcc 6.4, Ubuntu 16.04. Saya juga akan memberikan contoh gagal dengan arr + 2meminimalkan copy paste.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

1

Tumpukan korupsi yang disebabkan oleh buffer overflows. Anda dapat bertahan melawan mereka dengan pemrograman defensif.

Setiap kali Anda mengakses sebuah array, beri penegasan di depan array untuk memastikan aksesnya tidak di luar batas. Sebagai contoh:

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

Ini membuat Anda berpikir tentang batasan array dan juga membuat Anda berpikir tentang menambahkan tes untuk memicu mereka jika memungkinkan. Jika beberapa dari pernyataan ini dapat gagal selama penggunaan normal mengubahnya menjadi biasa if.


0

Saya mendapatkan kesalahan ini saat menggunakan malloc () untuk mengalokasikan sebagian memori ke struct * setelah menghabiskan beberapa kode debug ini, saya akhirnya menggunakan fungsi bebas () untuk membebaskan memori yang dialokasikan dan kemudian pesan kesalahan hilang :)


0

Sumber lain dari smashing stack adalah penggunaan yang salah ( vfork()bukan) fork().

Saya baru saja mendebat kasus ini, di mana proses anak tidak dapat mencapai execve()target yang dapat dieksekusi dan mengembalikan kode kesalahan daripada menelepon _exit().

Karena vfork()telah melahirkan anak itu, ia kembali ketika sebenarnya masih mengeksekusi di dalam ruang proses induk, tidak hanya merusak tumpukan induk, tetapi menyebabkan dua set diagnostik yang berbeda dicetak oleh kode "hilir".

Mengubah vfork()untuk fork()tetap kedua masalah, seperti yang dilakukan mengubah anak returnpernyataan untuk _exit()gantinya.

Tetapi karena kode anak mendahului execve()panggilan dengan panggilan ke rutinitas lain (untuk mengatur uid / gid, dalam kasus khusus ini), secara teknis tidak memenuhi persyaratan vfork(), jadi mengubahnya untuk digunakan fork()adalah benar di sini.

(Perhatikan bahwa returnpernyataan bermasalah sebenarnya tidak dikodekan seperti itu - sebagai gantinya, makro dipanggil, dan makro itu memutuskan apakah akan _exit()atau returndidasarkan pada variabel global. Jadi tidak segera jelas bahwa kode anak tidak sesuai untuk vfork()penggunaan. )

Untuk informasi lebih lanjut, lihat:

Perbedaan antara fork (), vfork (), exec () dan clone ()

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.