Libseccomp: BUG: gen_bpf_generate() tidak menangani kegagalan dengan benar

Dibuat pada 28 Mei 2020  ·  15Komentar  ·  Sumber: seccomp/libseccomp

Hai,

Pertama, terima kasih untuk libseccomp -- kami dengan senang hati menggunakannya dalam produksi selama beberapa tahun sekarang, dan tidak mengalami masalah apa pun (sampai sekarang). Saya tidak yakin apakah ini bug dalam kode kami, kesalahpahaman dokumentasi, atau sesuatu yang lain -- tetapi saya telah menghabiskan sebulan terakhir mencoba melacak ini tetapi tidak berhasil.

Kami baru-baru ini memutakhirkan paket dalam wadah Docker kami, yang mencakup peningkatan dari libseccomp 2.3.3 (versi dalam repo stabil Debian) ke 2.4.3. Ada paket sistem lain yang juga ditingkatkan, tetapi saya tidak merekamnya. Kernel kami tidak ditingkatkan, dan merupakan versi 4.19.0-8-amd64.

Kami menggunakan SCMP_ACT_TRACE , dan membuat filter yang hanya terdiri dari aturan SCMP_ACT_ALLOW yang ditambahkan menggunakan nomor syscall asli, bukan nomor semu libseccomp. Kami memotong proses helper 64-bit yang membangun dan memuat filter seccomp sebelum exec -ing biner 64-bit lainnya.

Sebagai referensi, ini adalah keseluruhan dari rutinitas inisialisasi seccomp kami, menggunakan pemeriksaan kesalahan yang serupa dengan halaman manual seccomp_rule_add .

Namun, panggilan kami ke seccomp_load telah mulai mengembalikan -EINVAL , pada urutan besarnya 1 / 100.000 inisialisasi proses. (Tidak dapat mereproduksi secara andal, hal ini membuat proses debug menjadi membosankan.) Tidak ada perubahan kode pada aplikasi kita selama ini. Panggilan sys yang ditambahkan ke filter identik di semua proses.

Adakah gagasan tentang apa yang salah (atau bahkan bagaimana menggali lebih jauh apa yang salah), atau apakah ini diharapkan dalam beberapa cara? Tidak banyak bagian yang bergerak dinamis, dan saya tidak dapat menemukan apa pun dalam dokumentasi tentang mengapa ini bisa terjadi.

bug prioritmedium

Komentar yang paling membantu

Belum, sayangnya. Setelah menambahkan tambalan ke seccomp_export_pfc , itu diam. Kemarin saya mendorong tambalan itu ke semua VM kami (bukan hanya satu uji coba) dengan harapan dapat menangkap masalah ketika itu akhirnya terjadi.

Saya menemukan keheningan itu aneh, tetapi untuk saat ini saya menganggapnya sebagai kebetulan karena semua logika debugging/ekspor terjadi _after_ gagal seccomp_load , jadi itu seharusnya tidak mempengaruhi kegagalan itu sendiri.

Semua 15 komentar

Hai @Xyene ,

Tidak banyak tempat yang mengembalikan -EINVAL di jalur kode seccomp_load() . Berdasarkan pemeriksaan cepat terhadap kode libseccomp v2.4.3, sepertinya ini disebabkan oleh scmp_filter_ctx tidak valid atau kernel yang mengeluh tentang panggilan prctl(...) yang memuat filter.

Mengingat v2.4.3 umumnya berfungsi, dan Anda belum mengubah kernel Anda, tampaknya meragukan bahwa panggilan prctl(...) adalah penyebab yang membawa kita ke konteks filter yang tidak valid. Pernahkah Anda memperhatikan perilaku aneh lainnya dalam program Anda sejak peningkatan? Saya ingin tahu apakah ada masalah kerusakan memori di tempat lain yang menyebabkan masalah.

Meskipun kesalahan selalu ada pada libseccomp, kami menjalankan setiap rilis melalui serangkaian pemeriksaan yang mencakup valgrind run untuk semua uji regresi kami serta analisis statis menggunakan dentang dan Coverity.

Selain itu, meskipun ini tidak membantu untuk v2.4.3, salah satu peningkatan yang kami targetkan untuk rilis v2.5.0 yang hampir siap adalah peningkatan dokumentasi dan penanganan kode kesalahan.

Kami baru-baru ini memutakhirkan paket dalam wadah Docker kami, yang mencakup peningkatan dari libseccomp 2.3.3 (versi dalam repo stabil Debian) ke 2.4.3. Ada paket sistem lain yang juga ditingkatkan, tetapi saya tidak merekamnya. Kernel kami tidak ditingkatkan, dan merupakan versi 4.19.0-8-amd64.

Terima kasih telah memverifikasi bahwa kode Anda dan kernel yang mendasarinya tidak berubah. Itu akan membantu melacak masalahnya.

Untuk referensi, ini adalah keseluruhan dari rutinitas inisialisasi seccomp kami, menggunakan pemeriksaan kesalahan yang serupa dengan halaman manual seccomp_rule_add .

Filter Anda terlihat masuk akal bagi saya.

Adakah gagasan tentang apa yang salah (atau bahkan bagaimana menggali lebih jauh apa yang salah), atau apakah ini diharapkan dalam beberapa cara? Tidak banyak bagian yang bergerak dinamis, dan saya tidak dapat menemukan apa pun dalam dokumentasi tentang mengapa ini bisa terjadi.

Saya melihat melalui kode v2.4.3 seccomp_load() , dan saya pikir hanya ada dua tempat di mana libseccomp menghasilkan kode pengembalian -EINVAL :

Kedua kesalahan di atas secara efektif disebabkan oleh filter yang tidak valid. Itu sepertinya tidak mungkin bagi saya berdasarkan kode filter Anda.

Perlu dicatat bahwa nilai pengembalian default kernel di seccomp_set_mode_filter() adalah -EINVAL , jadi mungkin ada hal lain pada sistem yang berubah, yang membuat kita jatuh ke jalur itu. Anda menyebutkan bahwa Anda menjalankan di Docker; apakah Anda menonaktifkan filter seccomp Docker default?

Saya akan tergoda untuk menambahkan beberapa debug lagi ke kode Anda di dalam if setelah seccomp_load() gagal. Misalnya, kita dapat menampilkan PFC dan/atau BPF dari filter itu sendiri untuk memverifikasi bahwa filter tersebut terlihat masuk akal. Lihat seccomp_export_pfc() dan seccomp_export_bpf() .

Saya melihat melalui kode v2.4.3 seccomp_load() , dan saya pikir hanya ada dua tempat di mana libseccomp menghasilkan kode pengembalian -EINVAL :

Ingatlah bahwa setiap kegagalan yang ditemukan di gen_bpf_generate(...) , atau di bawahnya, secara efektif digabungkan menjadi -ENOMEM oleh sys_filter_load(...) pada src/system.c:267 .

Aku benci jatuh kembali ke "korupsi memori!" begitu cepat, tapi sepertinya itu yang terjadi di sini.

Terima kasih atas balasan yang cepat dan terperinci! Mereka telah menghasilkan beberapa jalan eksplorasi :slightly_smiling_face:

Pernahkah Anda memperhatikan perilaku aneh lainnya dalam program Anda sejak peningkatan? Saya ingin tahu apakah ada masalah kerusakan memori di tempat lain yang menyebabkan masalah.

Tidak, hanya ini. Tes unit dan integrasi kami terus lulus, dan selain dari EINVAL sangat langka ini, tidak ada kesalahan yang dicatat dalam prod. Hal ini tentu membuatnya membingungkan; Saya juga mencurigai kerusakan memori, tetapi belum dapat menemukan bukti untuk mendukungnya :slightly_frowning_face:

Untuk sedikit lebih banyak konteks:

  • program ini adalah aplikasi Python yang memanggil beberapa C++ melalui Cython (GIL ditahan selama waktu ini, jadi Python tidak membuat alokasi di utas lain)
  • garpu sisi C++, dan pada anak mengatur filter seccomp sebelum dieksekusi
  • semua alokasi memori yang terjadi pasca-garpu, pra-exec berasal dari libseccomp itu sendiri, di seccomp_init dll.
  • tepatnya ada 4 malloc array antara memanggil ke kode C++ dan forking off, semuanya ada di Cython dan memiliki rentang yang sesuai saat menulis ke mereka (baris 435 memanggil kode seccomp yang ditautkan sebelumnya) -- semua alokasi/penulisan/dll lainnya. berada di dalam juru bahasa Python

Saat mengetik ini, saya punya ide: Saya telah mendengar cerita horor tentang malloc tidak aman untuk digunakan setelah forking, dan kami memiliki beberapa di dalam libseccomp itu sendiri. Aplikasi Python itu sendiri _is_ multithreaded, tetapi kami selalu memegang GIL saat dalam kode asli jadi ini harus aman (?). Saya hanya mendengar tentang kebuntuan yang terjadi melalui malloc-after-fork. (Saya kira ini membuat urutan bisnis berikutnya bergerak seccomp_init et al. sebelum fork, hanya memanggil seccomp_load post-fork, dan melihat apakah kesalahan terus terjadi.)

Saya akan tergoda untuk menambahkan beberapa debug lagi ke kode Anda di dalam if after seccomp_load() gagal.

Terima kasih untuk sarannya! Saya telah menambahkan panggilan ke seccomp_export_pfc , serta membuang konten input ke filter ( config->syscall_whitelist ). Saya akan menindaklanjuti saat ini gagal.

Hai @Xyene - karena sudah sekitar seminggu, saya hanya ingin memeriksa dan melihat apakah ada sesuatu yang baru yang Anda temukan?

Belum, sayangnya. Setelah menambahkan tambalan ke seccomp_export_pfc , itu diam. Kemarin saya mendorong tambalan itu ke semua VM kami (bukan hanya satu uji coba) dengan harapan dapat menangkap masalah ketika itu akhirnya terjadi.

Saya menemukan keheningan itu aneh, tetapi untuk saat ini saya menganggapnya sebagai kebetulan karena semua logika debugging/ekspor terjadi _after_ gagal seccomp_load , jadi itu seharusnya tidak mempengaruhi kegagalan itu sendiri.

Kemajuan!

Ternyata alasannya diam adalah karena seccomp_export_bpf adalah segfaulting (haruskah, jika dipanggil setelah seccomp_load ?), dan itu dilaporkan di tempat lain dan bukan di tempat saya mencari kegagalan seccomp. Lebih penting lagi, saya mengalami kasus di mana saya dapat dengan andal mereproduksi masalah dalam ~ 150 permintaan, jadi dengan beberapa pekerjaan pipa saya harus dapat mengekstrak beberapa dump inti.

Baiklah, saya mengeluarkan coredump, dan ini adalah jejaknya: https://Gist.github.com/Xyene/920f1cb098784a031f53c66a2f49d167

Ini agak mencurigakan, karena crash di dalam rutinitas realloc jemalloc. Selain itu, menggunakan glibc malloc sebagai gantinya menyelesaikan masalah (sayangnya, ini bukan opsi jangka panjang dalam kasus ini karena masalah fragmentasi).

Selanjutnya, saya menarik jemalloc, mengkompilasinya dengan simbol -O0 dan debugging, dan memutar ulang reproduksi. Kali ini crash di seccomp_load , bukan setelahnya! Saya telah mengunggah jejak itu di sini: https://Gist.github.com/Xyene/5da56168bcea337da85b2cd30704d12e

Cuplikan jejak itu:

#9  0x00007ff962698495 in free (ptr=0x5a5a5a5a5a5a5a5a) at src/jemalloc.c:2867
No locals.
#10 0x00007ff96062d087 in _program_free (prg=prg@entry=0x7ff95e963010) at gen_bpf.c:511
No locals.
#11 0x00007ff96062f605 in gen_bpf_release (program=program@entry=0x7ff95e963010) at gen_bpf.c:1986
No locals.
#12 0x00007ff96062c04f in sys_filter_load (col=col@entry=0x7ff95e9a5000) at system.c:293
        rc = -1
        prgm = 0x7ff95e963010
#13 0x00007ff96062b666 in seccomp_load (ctx=ctx@entry=0x7ff95e9a5000) at api.c:286
        col = 0x7ff95e9a5000

Mencari jemalloc, sepertinya 0x5a digunakan untuk menandai byte gratis sebagai free , dengan maksud khusus untuk merusak kode yang mencoba membebaskan sesuatu yang sudah dibebaskan.

gen_bpf.c:511 di v2.4.3 adalah: https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/gen_bpf.c#L505 -L513

Tapi, ini tidak masuk akal karena masa pakai program hanyalah tubuh sys_filter_load :

https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/system.c#L260 -L296

Saya pikir saya telah melihat setidaknya satu masalah. Dalam gen_bpf_generate ;

https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/gen_bpf.c#L1963 -L1966

state.bpf = prgm selama zmalloc tidak gagal. Selanjutnya, _gen_bpf_build_bpf dipanggil, dan berdasarkan rc , state.bpf diatur ke NULL .

https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/gen_bpf.c#L1968 -L1971

Mempertimbangkan kasus di mana rc != 0 , state.bpf masih disetel ke prgm pada saat panggilan ke _state_release . Ini akan menyebabkan memori yang ditunjuk oleh prgm dibebaskan.

https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/gen_bpf.c#L539

Selanjutnya, gen_bpf_generate akan return prgm , yang meskipun telah dibebaskan, masih merupakan penunjuk bukan nol.

https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/gen_bpf.c#L1971 -L1974

Kembali di sys_filter_load , gen_bpf_generate kembali, dan prgm tidak- NULL jadi itu berlanjut.

https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/system.c#L265 -L267

Akhirnya, di akhir sys_filter_load , gen_bpf_release dipanggil pada prgm .

https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/system.c#L292 -L295

Ini tidak mengatasi kekhawatiran mengapa _gen_bpf_build_bpf akan gagal sejak awal, tetapi sepertinya sesuatu yang buruk dapat terjadi jika itu gagal.

Sunting: sebenarnya, ini sepertinya telah diperbaiki sebagai efek samping dari https://github.com/seccomp/libseccomp/commit/3a1d1c977065f204b96293cccfe7d3e5aa0d7ace.

Mempertimbangkan kasus di mana rc != 0, state.bpf masih disetel ke prgm pada saat panggilan ke _state_release. Ini akan menyebabkan memori yang ditunjuk oleh prgm dibebaskan.

Ah! Tangkapan bagus @Xyene!

Saya pikir kita perlu memperbaiki ini di luar 3a1d1c977065f204b96293cccfe7d3e5aa0d7ace, izinkan saya memikirkan ini sebentar ... Saya tidak berpikir perbaikannya akan terlalu sulit ... dan lihat apakah saya bisa membuat PR.

Saya pikir kita perlu memperbaiki ini di luar 3a1d1c9, izinkan saya memikirkan ini sebentar ... Saya tidak berpikir perbaikannya akan terlalu sulit ... dan lihat apakah saya bisa membuat PR.

Ups, saya sedang melihat kode lama ketika saya menulis itu; ya, saya percaya bahwa 3a1d1c9 memang memperbaiki ini untuk kami, tetapi kami akan membutuhkan tambalan untuk cabang rilis-2.4. Saya akan mengerjakannya sekarang.

_(Meta: Saya akan terus memperbarui pesan ini dengan temuan saya saat saya melanjutkan, jadi saya punya tempat untuk menuliskannya tanpa mengirim email spam ke kalian :)_

Baiklah, kembali ke 2.4.3 dengan tambalan diterapkan, saya sudah bisa mengeluarkan filter yang gagal: link .

Penyebab yang dilaporkan sekarang adalah ENOMEM bukannya EINVAL , yang saya kira diharapkan mengingat _gen_bpf_build_bpf gagal dan mengembalikan program NULL . PFC mencetak dengan baik. Memodifikasi kode seccomp untuk melaporkan nilai kembalian _gen_bpf_build_bpf menunjukkan EFAULT sebagai penyebabnya.

Sebagai peretasan cepat, saya menjalankan :%s/return -EFAULT/abort() lebih dari src/gen_bpf.c , dan dapat mengekstrak jejak tumpukan ini:

Jejak tumpukan EFAULT

(gdb) bt full
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
        set = {__val = {0, 140084028365964, 140083248439464, 140083248438968, 140083248431088, 140084028368143, 28659884033, 140083965300736, 
            140083248439464, 140083248438968, 140083248431088, 140084028351031, 140084019988760, 140083248439624, 140083248431200, 140084028372597}}
        pid = <optimized out>
        tid = <optimized out>
        ret = <optimized out>
#1  0x00007f67daa4d55b in __GI_abort () at abort.c:79
        save_stage = 1
        act = {__sigaction_handler = {sa_handler = 0x7f67d6f3eec0, sa_sigaction = 0x7f67d6f3eec0}, sa_mask = {__val = {140083965300736, 
              140083965300736, 0, 0, 140083248438968, 140083248438968, 140083248439464, 140083248431504, 140084028417173, 140083964793344, 
              140083965300736, 140083248431552, 140083994791895, 140083248431552, 140083994787642, 140083965300736}}, sa_flags = -1404894496, 
          sa_restorer = 0x0}
        sigs = {__val = {32, 0 <repeats 15 times>}}
#2  0x00007f67d8bfd455 in _gen_bpf_build_bpf (state=0x7f67ac4302e0, col=0x7f67d6f63040) at gen_bpf.c:1943
        rc = 0
        iter = 1
        h_val = 1425818561
        res_cnt = 0
        jmp_len = 0
        arch_x86_64 = 0
        arch_x32 = -1
        instr = {op = 32, jt = {tgt = {imm_j = 0 '\000', imm_k = 0, hash = 0, db = 0x0, blk = 0x0, nxt = 0}, type = TGT_NONE}, jf = {tgt = {
              imm_j = 0 '\000', imm_k = 0, hash = 0, db = 0x0, blk = 0x0, nxt = 0}, type = TGT_NONE}, k = {tgt = {imm_j = 4 '\004', imm_k = 4, 
              hash = 4, db = 0x4, blk = 0x4, nxt = 4}, type = TGT_K}}
        i_iter = 0x7f67d6fdcb60
        b_badarch = 0x7f67d6fd9000
        b_default = 0x7f67d6fd9060
        b_head = 0x7f67d6fda1a0
        b_tail = 0x7f67d6fd9000
        b_iter = 0x0
        b_new = 0x7f67d6fe3300
        b_jmp = 0x0
        db_secondary = 0x0
        pseudo_arch = {token = 0, token_bpf = 0, size = ARCH_SIZE_UNSPEC, endian = ARCH_ENDIAN_LITTLE, syscall_resolve_name = 0x0, 
          syscall_resolve_num = 0x0, syscall_rewrite = 0x0, rule_add = 0x0}
#3  0x00007f67d8bfd560 in gen_bpf_generate (col=0x7f67d6f63040) at gen_bpf.c:1971
        rc = 0
        state = {htbl = {0x0 <repeats 256 times>}, attr = 0x7f67d6f63044, bad_arch_hsh = 889798935, def_hsh = 742199527, arch = 0x7f67ac4301e0, 
          bpf = 0x7f67d6f64010}
        prgm = 0x7f67d6f64010
#4  0x00007f67d8bf64a7 in sys_filter_load (col=0x7f67d6f63040) at system.c:265
        rc = 32615
        prgm = 0x0
#5  0x00007f67d8bf4f10 in seccomp_load (ctx=0x7f67d6f63040) at api.c:287
        col = 0x7f67d6f63040

Itu sesuai dengan baris 1943:

https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/gen_bpf.c#L1935 -L1943

Mengingat sifat penggantinya, saya pikir kita dapat mengecualikan EFAULT apa pun dalam fungsi pembantu apa pun, karena itu akan dibatalkan terlebih dahulu.

Setelah ini, saya mencoba mereproduksi hal yang sama dengan HEAD -- itu masih terjadi. Selanjutnya, %s:/goto build_bpf_free_blks/abort() dan ulangi. Penyebabnya adalah:

https://github.com/seccomp/libseccomp/blob/34bf78abc9567b66c72dbe67e7f243072162a25f/src/gen_bpf.c#L2219 -L2220

Untungnya fungsi ini pendek, dan hanya memiliki beberapa titik kegagalan. Putaran lain dari penyisipan abort nanti;

Jejak

(gdb) bt full
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
        set = {__val = {0, 140050183343588, 0, 448, 140049402494880, 140049402509040, 140049402494832, 140050183342988, 140049402495088, 
            140049402509040, 140049402494896, 140050183343588, 4294967296, 140049402509040, 140049402509040, 140049402509040}}
        pid = <optimized out>
        tid = <optimized out>
        ret = <optimized out>
#1  0x00007f5ff953055b in __GI_abort () at abort.c:79
        save_stage = 1
        act = {__sigaction_handler = {sa_handler = 0x7f5ff595d260, sa_sigaction = 0x7f5ff595d260}, sa_mask = {__val = {139642271694862, 
              140050119389792, 0, 0, 140049402502840, 0, 140049402503336, 140049402502888, 140049402502840, 112, 384, 140049402502840, 140050149861504, 
              140049402495328, 140050149857273, 392}}, sa_flags = 448, sa_restorer = 0x7f5ff595d240}
        sigs = {__val = {32, 0 <repeats 15 times>}}
#2  0x00007f5ff76edee5 in _bpf_append_blk (prg=0x7f5ff5964010, blk=0x7f5ff59df1a0) at gen_bpf.c:452
        rc = -12
        i_new = 0x0
        i_iter = 0x7f5ff59fa178
        old_cnt = 48
        iter = 1
#3  0x00007f5ff76f3716 in _gen_bpf_build_bpf (state=0x7f5fcae302d0, col=0x7f5ff59c5000) at gen_bpf.c:2223
        rc = 0
        iter = 1
        h_val = 1425818561
        res_cnt = 0
        jmp_len = 0
        arch_x86_64 = 0
        arch_x32 = -1
        instr = {op = 32, jt = {tgt = {imm_j = 0 '\000', imm_k = 0, hash = 0, db = 0x0, blk = 0x0, nxt = 0}, type = TGT_NONE}, jf = {tgt = {
              imm_j = 0 '\000', imm_k = 0, hash = 0, db = 0x0, blk = 0x0, nxt = 0}, type = TGT_NONE}, k = {tgt = {imm_j = 4 '\004', imm_k = 4, 
              hash = 4, db = 0x4, blk = 0x4, nxt = 4}, type = TGT_K}}
        i_iter = 0x7f5ff59e1b60
        b_badarch = 0x7f5ff59de000
        b_default = 0x7f5ff59de060
        b_head = 0x7f5ff59df1a0
        b_tail = 0x7f5ff59de000
        b_iter = 0x7f5ff59df1a0
        b_new = 0x7f5ff59e8300
        b_jmp = 0x7f5ff59df0e0
        db_secondary = 0x0
        pseudo_arch = {token = 0, token_bpf = 0, size = ARCH_SIZE_UNSPEC, endian = ARCH_ENDIAN_LITTLE, syscall_resolve_name = 0x0, 
          syscall_resolve_num = 0x0, syscall_rewrite = 0x0, rule_add = 0x0}
#4  0x00007f5ff76f3874 in gen_bpf_generate (col=0x7f5ff59c5000, prgm_ptr=0x7f5fcae30b40) at gen_bpf.c:2270
        rc = 0
        state = {htbl = {0x0, 0x7f5ff593ef80, 0x7f5ff593efe0, 0x7f5ff593efc0, 0x0, 0x7f5ff595d000, 0x7f5ff593ef60, 0x7f5ff593ef00, 
            0x0 <repeats 248 times>}, attr = 0x7f5ff59c5004, bad_arch_hsh = 889798935, def_hsh = 742199527, bpf = 0x7f5ff5964010, 
          arch = 0x7f5fcae301c0, b_head = 0x7f5ff59e8300, b_tail = 0x7f5ff59de120, b_new = 0x7f5ff59e8300}
        prgm = <optimized out>
#5  0x00007f5ff76eb275 in sys_filter_load (col=0x7f5ff59c5000, rawrc=false) at system.c:307
        rc = 0
        prgm = 0x0
#6  0x00007f5ff76e9505 in seccomp_load (ctx=0x7f5ff59c5000) at api.c:386
        col = 0x7f5ff59c5000
        rawrc = false

https://github.com/seccomp/libseccomp/blob/34bf78abc9567b66c72dbe67e7f243072162a25f/src/gen_bpf.c#L449 -L452

Jadi realloc gagal lagi, dan _bpf_append_blk mengembalikan -ENOMEM yang ditutupi oleh _gen_bpf_build_bpf dan berubah menjadi -EFAULT . Ini bukan masalah besar, tetapi karena Anda mengatakan pelaporan kesalahan yang lebih baik adalah target 2,5, saya pikir saya akan menyebutkannya karena ini terlihat dalam cakupan :slightly_smiling_face:

Beberapa menyodok dengan GDB:

(gdb) f 2
#2  0x00007f5ff76edee5 in _bpf_append_blk (prg=0x7f5ff5964010, blk=0x7f5ff59df1a0) at gen_bpf.c:452
452         abort();
(gdb) info args
prg = 0x7f5ff5964010
blk = 0x7f5ff59df1a0
(gdb) print prg->blks
$4 = (bpf_instr_raw *) 0x7f5ff59fa000
(gdb) x/32bx &prg->blks
0x7f5ff5964018: 0x00    0xa0    0x9f    0xf5    0x5f    0x7f    0x00    0x00
0x7f5ff5964020: 0x5a    0x5a    0x5a    0x5a    0x5a    0x5a    0x5a    0x5a
0x7f5ff5964028: 0x5a    0x5a    0x5a    0x5a    0x5a    0x5a    0x5a    0x5a
0x7f5ff5964030: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
(gdb) print ((prg)->blk_cnt * sizeof(*((prg)->blks)))
$5 = 392
(gdb) print prg->blk_cnt
$6 = 49

Yang ini benar-benar mulai terlihat seperti kegagalan pengalokasi ...

Aha, cerita ini akhirnya mencapai kesimpulan _thrilling_ — Saya telah mengetahui apa yang terjadi, dan memverifikasi perbaikannya :slightly_smiling_face:

Karena itu mungkin membuat cerita yang menarik, ini dia:

Proses utama yang memotong pekerja biasanya berada di ~80mb RSS. Setelah bercabang, ia membatasi penggunaan memori melalui rlimit , terkadang hingga 64mb. Ini menempatkannya pada posisi di mana penggunaan memori saat ini melebihi batasnya, tetapi ini diizinkan oleh rlimit . _Sebagian besar_ waktu, pengalokasi memori akan memiliki cukup memori bebas yang tersedia untuk melayani rutinitas inisialisasi libseccomp tanpa meminta lebih banyak dari kernel. Tetapi ketika _tidak_, dan perlu meminta ruang untuk arena tambahan atau sesuatu, kernel tidak akan menyediakannya karena prosesnya sudah melewati batasnya.

Di 2.4.3, kegagalan untuk mendapatkan memori ini diwujudkan dalam EINVAL dan double-free. Di master post- https://github.com/seccomp/libseccomp/commit/3a1d1c977065f204b96293cccfe7d3e5aa0d7ace , EFAULT dilaporkan sebagai gantinya. Dengan https://github.com/seccomp/libseccomp/pull/257 diterapkan, ENOMEM dilaporkan dengan benar.

Alasan mengapa hal ini jarang terjadi kemudian menjadi jelas: sepenuhnya bergantung pada apakah pengalokasi memiliki cukup memori untuk membangun program BPF tanpa meminta lebih banyak dari kernel. pengalokasi glibc lebih longgar tentang memungkinkan fragmentasi untuk membangun, jadi ini tidak pernah terjadi dengan itu di tempat. jemalloc menempatkan batas yang lebih ketat, dan mengarah pada peningkatan kemungkinan perlunya meminta memori selama seccomp_load — cukup untuk melihat kegagalan yang dihasilkan, tetapi masih menyebalkan untuk dilacak.

Maka, cara mengatasinya adalah dengan memindahkan semua panggilan setrlimit ke _after_ seccomp_load . Dengan demikian, realloc tidak lagi gagal dalam _bpf_append_blk , dan filter berhasil dimuat. Ini berarti bahwa filter harus mengizinkan setrlimit , tetapi dalam kasus saya ini dapat diterima. Secara lebih umum, saya pikir masalah ini akan diselesaikan dengan sesuatu seperti https://github.com/seccomp/libseccomp/issues/123 .

@pcmoore , @drakenclimber -- sekali lagi terima kasih atas semua bantuan Anda dalam

Apakah halaman ini membantu?
0 / 5 - 0 peringkat