Libseccomp: BUG: kompatibilitas dengan openmp

Dibuat pada 2 Sep 2017  ·  19Komentar  ·  Sumber: seccomp/libseccomp

Sifat multi-threading yang non-deterministik membuat yang satu ini sulit untuk di-debug, tetapi saya pikir, saya telah sampai pada kasus uji yang agak dapat direproduksi seccomp_omp.c.gz . Kompilasi program dengan -O0 -ggdb -fopenmp .

Jika kita menjalankannya hanya dengan satu utas dan membiarkannya melakukan panggilan sistem terlarang, program akan mati seperti yang diharapkan.

$ ./seccomp_omp -t 1 -k 0      
86195973
[1]    10103 invalid system call (core dumped)  ./seccomp_omp -t 1 -k 0

Namun, mengingat lebih banyak utas, itu berhenti berfungsi.

$ ./seccomp_omp -t 2 -k 0
86198868
86195973
86200317
^C

Program tidak mati, hanya berhenti melakukan apa pun. Saya bingung apa yang terjadi dengan sinyal SIGSYS, dan mengapa tidak ditangani?

Ini mungkin bukan masalah libseccomp sama sekali, tetapi saya tidak cukup mahir dengan seccomp, sinyal, dan sistem threading secara umum untuk men-debug penyebab utama. Semoga Anda bisa memahaminya.

bug prioritmedium

Semua 19 komentar

CATATAN: Saya belum melihat test case Anda, saya hanya menebak berdasarkan deskripsi yang ditulis dengan baik

Mempertimbangkan sifat multi-utas ini, sudahkah Anda mencoba menyetel atribut filter SCMP_FLTATR_CTL_TSYNC ke true sebelum memuat filter ke dalam kernel?

Saya tidak menyetel SCMP_FLTATR_CTL_TSYNC karena saya menambahkan filter seccomp, sebelum membelah menjadi utas. Jadi, kernel harus menerapkan filter ke semua utas (dan tampaknya memang demikian). Menambahkan SCMP_FLTATR_CTL_TSYNC ke testcase tidak ada bedanya (yang kurang dari 100 baris dengan penanganan kesalahan bertele-tele, btw).

Oke, saya hanya ingin menyebutkannya; sepertinya Anda melakukan sesuatu dengan benar (mengatur filter sebelum menelurkan utas baru). Saya harus melihat lebih dekat, tetapi saya mungkin tidak mendapatkan kesempatan untuk melakukannya segera.

Konfirmasi bahwa saya tidak melakukan hal-hal yang salah secara terang-terangan sudah cukup baik bagi saya. Gunakan waktumu!

Dalam contoh utas tunggal, ./seccomp_omp -t 1 -k 0 , openmp mengenali bahwa hanya satu utas yang akan dijalankan, jadi openmp melewati banyak sinkronisasi yang biasanya dilakukan dalam perulangan for multi-utas. Saya memverifikasi ini dengan mengamati syscalls yang diproses di seccomp_run_filters() di kernel. Seperti yang diharapkan, saya melihat panggilan ke __NR_write() dan panggilan ke __NR_madvise() yang meminta seccomp untuk menginstruksikan kernel untuk mematikan utas.

Dalam contoh multi-utas, ./seccomp_omp -t 2 -k 0 , openmp mencoba memparalelkan perulangan for. Jadi lebih banyak perpustakaan openmp digunakan. Ini terlihat ketika kembali menonton syscalls melalui seccomp_run_filters(). __NR_mmap(), __NR_mprotect(), __NR_clone(), __NR_futex(), dan lebih banyak syscalls dipanggil sebelum panggilan ke madvise(). Akhirnya salah satu dari dua utas akhirnya memanggil madvise() dan seccomp dengan benar membunuh utas itu. Tetapi openmp memiliki sinkronisasi antara utas dan sebelum utas kedua dapat memanggil madvise() (dan terbunuh), utas kedua memanggil futex() karena sedang menunggu sesuatu dari utas mati. Dan inilah mengapa program hang. Satu utas telah terbunuh dan utas kedua menunggu data (dari utas mati) yang tidak akan pernah tiba.

Saya belum banyak bekerja dengan openmp, tetapi tampaknya ada beberapa diskusi [ 1 , 2 , 3 , ...] tentang sinyal di blok paralel. Pada akhirnya sepertinya masalah sulit yang sebaiknya Anda hindari.

Sepertinya ada beberapa solusi mudah yang potensial:

  1. Jangan gunakan SCMP_ACT_KILL sebagai penangan default Anda. Alih-alih, alihkan menggunakan kode kesalahan, misalnya SCMP_ACT_ERROR(5) . Saya membuat perubahan ini di program contoh Anda, dan memverifikasi bahwa itu dihentikan dengan benar.

  2. Jika Anda tidak memerlukan kekuatan openmp, opsi lain mungkin beralih menggunakan solusi yang lebih umum seperti pthread_create() atau fork()

@drakenclimber jadi sepertinya masalahnya hanya openmp membuat syscalls tambahan yang biasanya bukan bagian dari filter? Atau apakah itu kehilangan poin yang lebih besar?

@drakenclimber Terima kasih banyak telah menginvestasikan waktu Anda. Menunggu utas mati menjelaskan gejalanya.

Untuk memberikan lebih banyak konteks: Saya menulis kode ilmiah, jadi saya ingin menjaga semuanya tetap sederhana dengan OpenMP. Namun, saya juga ingin melindungi pengguna saya dari diri mereka sendiri. Jadi jika program saya melakukan sesuatu yang tidak biasa, nuke saja. Saya menduga, apa yang akhirnya ingin saya capai adalah mematikan proses (# 96) dengan pesan kesalahan yang agak masuk akal.

Nilai apa yang harus saya gunakan untuk errno di SCMP_ACT_ERROR untuk meniru SECCOMP_RET_KILL_PROCESS ? Apakah ada sesuatu yang berfungsi dengan baik di semua syscalls?

@pcmoore - agak. Tapi saya pikir masalah yang lebih besar adalah openmp tidak menangani sinyal dengan anggun di dalam konstruksi paralelnya. Saya kira ini adalah dengan desain.

@kloetzl - Jangan khawatir - Anda dapat tetap menggunakan OpenMP. Sepertinya orang lain di komunitas OpenMP melakukan sesuatu seperti berikut:

ctx = seccomp_init(SCMP_ACT_ERRNO(ENOTBLK));
...
bool kill_process = false;
int rc;
#pragma omp parallel for num_threads(THREADS)
  for (int i = 0; i < THREADS * 2; i++) {
    fprintf(stderr, "%u\n", func(i));
    if (i == K) {
      rc = madvise(NULL, 0, 0); 
      if (rc < 0)
        kill_process = true;
      }   
    // sleep(10);
  }

  if (kill_process)
    goto error;

  seccomp_release(ctx);
  return 0;
error:
  seccomp_release(ctx);
  return -1; 
}

Adapun kesalahan apa yang harus dikembalikan SCMP_ACT_ERRNO() , itu benar-benar terserah Anda. SCMP_ACT_ERRNO() akan bekerja di semua syscalls. Dan program Anda yang akan menanganinya dan mengembalikan kesalahan kepada pengguna, sehingga Anda dapat memilih kesalahan apa pun yang paling cocok untuk Anda. Sebenarnya, memilih yang tidak jelas - katakanlah ENOTBLK - bisa menjadi cara Anda mengetahui kesalahan berasal dari seccomp memblokir panggilan.

tl;dr - Deteksi masalah dalam loop paralel, tetapi tunggu untuk menanganinya hingga loop selesai. Anda mungkin perlu menambahkan pengkodean defensif ekstra dalam loop karena syscall yang gagal.

@kloetzl - Saya akan melihat masalah #96 dan melihat seberapa baik itu dimainkan dengan OpenMP. Itu bisa menjadi solusi lain juga. Terima kasih!

Dan program Anda yang akan menanganinya dan mengembalikan kesalahan kepada pengguna, sehingga Anda dapat memilih kesalahan apa pun yang paling cocok untuk Anda. Sebenarnya, memilih yang tidak jelas - katakanlah ENOTBLK - bisa menjadi cara Anda mengetahui kesalahan itu berasal dari seccomp yang memblokir panggilan.

Masalahnya, madvise disebut jauh di lubuk hati malloc . Jadi, bukan saya yang menangani kesalahan, glibc adalah. Jadi pertanyaannya adalah, apakah glibc (dan semua perpustakaan lain yang melakukan syscalls) tahu bahwa syscall dapat mengembalikan kesalahan lain selain yang diberikan di halaman manualnya dan menanganinya dengan tepat?

Soalnya, madvise disebut jauh di lubuk hati malloc. Jadi, bukan saya yang menangani kesalahan, glibc adalah. Jadi pertanyaannya adalah, apakah glibc (dan semua perpustakaan lain yang melakukan syscalls) tahu bahwa syscall dapat mengembalikan kesalahan lain selain yang diberikan di halaman manualnya dan menanganinya dengan tepat?

Ahhh... ngambek. Saya harus melihat melalui kode glibc untuk memastikan, tetapi saya bersedia mengambil risiko tebakan berikut:

  • Jika madvise mengembalikan kesalahan, glibc akan _pasti_ juga mengembalikan kesalahan
  • Sangat mungkin glibc dapat mengembalikan kesalahan yang berbeda dari seccomp yang dikembalikan, jadi Anda harus berhati-hati jika Anda mengharapkan kode pengembalian "khusus" seperti ENOTBLK

Singkat cerita, glibc seharusnya tidak menekan kode kesalahan _any_, tetapi bisa mengembalikan kode kesalahan yang berbeda

Saya mencoba untuk mengikuti kalian, tetapi saya khawatir kurangnya latar belakang saya dengan OpenMP membuat saya sedikit ketinggalan ... berdasarkan komentar di atas sepertinya KILL_PROCESS bisa menjadi solusi yang bisa diterapkan di sini? Jika demikian, kami dapat meningkatkan prioritas masalah itu, meskipun ada dalam daftar hal-hal yang harus saya atasi sebelum rilis v2.4.

@pcmoore - Ya, saya percaya KILL_PROCESS adalah solusi yang layak untuk bug ini. Saya menguji program pengujian @kloetzl di atas menggunakan tindakan KILL_PROCESS dan hang tidak lagi terjadi.

Saya telah menerapkannya tetapi saat ini saya menemukan beberapa hambatan di python autotests. Saya harus memiliki patch keluar minggu depan.

@drakenclimber ooh, patch? Saya suka patch :)

Terima kasih teman-teman.

Saya dapat mengonfirmasi bahwa KILL_PROCESS menyelesaikan masalah: Saya juga meretas versi lokal libseccomp di Arch dan program pengujian gagal sebagaimana dimaksud. Namun, memberikan tes yang solid dan menjalankannya pada kernel yang berbeda mungkin menjadi tantangan yang lebih besar.

Terima kasih atas konfirmasinya @kloetzl.

Ya, menulis tes yang tepat bisa jadi membosankan, tapi ini penting. Sama pentingnya dengan kode yang diuji IMHO.

Saya menantikan PR dari @drakenclimber , kita bisa berdiskusi lebih banyak setelah kode itu diterbitkan.

Saya sedang melakukan pembersihan musim semi COVID-19 dan saya pikir kami telah menyelesaikan masalah Anda, apakah itu benar @kloetzl? Saya akan menutup masalah ini, tetapi jika saya salah dan Anda masih melihat masalah, beri tahu kami dan kami akan membukanya kembali!

Terima kasih telah mengingatkan saya, saya menguji sedikit di pihak saya.

Masalah ini sebagian besar telah diperbaiki, dengan hambatan kecil. Program tidak lagi hang di limbo ketika ditemui syscall yang tidak valid, yay! Dengan SCMP_ACT_TRAP seseorang bahkan dapat menghasilkan nama syscall yang buruk. Namun, OpenMP menimpa penangan sinyal saya, sehingga kembali ke pesan kesalahan default setelah beberapa utas muncul. Dari sudut pandang pengalaman pengguna, saya tidak menyukai perilaku ini, tetapi menurut saya itu sebagus yang didapat.

Ah, ya, saya tidak yakin ada banyak hal yang bisa kami lakukan tentang penangan sinyal yang ditimpa di libseccomp, maaf tentang itu.

Terima kasih telah memberi tahu kami bahwa sisanya berhasil!

Apakah halaman ini membantu?
0 / 5 - 0 peringkat