1 poin oleh GN⁺ 2024-04-05 | 1 komentar | Bagikan ke WhatsApp
  • -fanalyzer di GCC 14 mengikuti berbagai jalur eksekusi kode C dengan eksekusi simbolik, dan berfokus menemukan cacat yang sulit ditangkap sebelum runtime pada waktu kompilasi
  • Peningkatan kali ini terbagi menjadi deteksi loop tak berhingga, visualisasi buffer overflow, pelacakan string C, dan pengaktifan default analisis taint, dengan fokus membuat hasil diagnostik lebih mudah dipahami
  • -Wanalyzer-out-of-bounds melampaui peringatan sederhana dengan menampilkan hubungan antara buffer dan posisi penulisan melalui diagram teks, serta menunjukkan di bagian tengah karakter mana overflow terjadi pada string UTF-8
  • Atribut baru null_terminated_string_arg(PARAM_IDX) memberi tahu analyzer dan pembaca kode tentang syarat API yang mengharapkan string berterminasi null, sehingga jalur yang meneruskan buffer yang tidak berterminasi null lebih mudah terlihat
  • Analisis taint kini aktif secara default saat -fanalyzer dipilih, sehingga alur ketika nilai yang dikendalikan penyerang digunakan sebagai ukuran, indeks, offset, dan lainnya tanpa pemeriksaan batas atas dapat lebih mudah diperiksa

Cakupan masalah yang ditangani -fanalyzer

  • -fanalyzer adalah pass analisis statis GCC yang mencoba menemukan cacat pada waktu kompilasi dengan mengeksekusi secara simbolik berbagai jalur eksekusi dalam kode sumber C
  • Peningkatan GCC 14 dirangkum menjelang rilis resmi pada April 2024, dan pada saat artikel ditulis, rilis GCC 14.1 diperkirakan hadir pada April 2024
  • Prarilis GCC 14.0 sudah digunakan di Fedora 40 Beta
  • Contoh-contohnya dapat dicoba di Compiler Explorer dengan opsi compiler baru

Deteksi loop tak berhingga sederhana

  • GCC 14 menambahkan peringatan baru -Wanalyzer-infinite-loop
  • Kode contoh berbentuk for bersarang, dengan kondisi loop kedua j < n tetapi ekspresi increment masih i++
    • Ini merupakan kesalahan setelah menyalin pernyataan for pertama lalu lupa mengganti i pada ekspresi increment menjadi j
    • Analyzer mengikuti cabang true dari j < n terus-menerus pada jalur ini dan memperingatkan pengulangan loop
  • Output diagnostik saat ini lebih mudah dipahami jika dibaca berurutan dari nomor peristiwa (1) hingga (5)
  • Di GCC 15, upaya meningkatkan keterbacaan dengan cara seperti seni ASCII yang menyorot jalur aliran kontrol masih menjadi harapan
  • Kode tersebut dapat dijalankan di contoh Compiler Explorer

Memvisualisasikan buffer overflow dengan teks

  • Di GCC 13, analyzer mulai mendukung pemeriksaan batas melalui -Wanalyzer-out-of-bounds
  • Di GCC 14, hubungan spasial dari buffer overflow yang diprediksi dapat ditampilkan sebagai diagram berbasis teks
  • Pada contoh yang menjalankan strcpy(buf, "hello") pada char buf[10], lalu memanggil strcat(buf, " world!"), terdeteksi stack-based buffer overflow
    • Pesan sebelumnya menunjukkan kapasitas buf adalah 10 byte dan terjadi penulisan di luar batas dari byte 10 hingga byte 12
    • Diagram GCC 14 menampilkan buffer tujuan yang diisi oleh strcpy bersama byte terminator NUL yang sudah ada dan menjadi titik awal strcat
  • Pada contoh string non-ASCII, analyzer juga mengikuti representasi UTF-8 dan menunjukkan posisi overflow
    • Kode yang menyalin "サツキ" ke char buf[11] lalu menambahkan "メイ" digunakan sebagai contoh
    • Diagram menunjukkan overflow terjadi di tengah karakter , yaitu U+30E1
  • Kode terkait dapat dilihat di contoh string ASCII dan contoh string non-ASCII

Penguatan pelacakan operasi string C

  • Analyzer GCC 14 meningkatkan pelacakan operasi string C dengan menyimulasikan API yang memindai buffer untuk mencari byte terminator null
  • Jika pointer menunjuk ke buffer yang tidak berterminasi null dan ada jalur yang meneruskannya ke API tersebut, analyzer akan memperingatkannya
  • Atribut fungsi baru null_terminated_string_arg(PARAM_IDX) memberi tahu analyzer dan pembaca kode bahwa parameter tertentu harus berupa string berterminasi null
  • Dalam contoh, example_fn(const char *p) diberi atribut null_terminated_string_arg(1) dan nonnull
    • char str[3] = "abc"; tidak memiliki ruang untuk byte terminator null
    • Saat memanggil example_fn(str), analyzer memperingatkan stack-based buffer over-read yang membaca 1 byte setelah akhir str
    • Diagnostik juga menampilkan note bahwa argumen pertama example_fn harus berupa pointer ke string berterminasi null
  • Contoh ini dapat dijalankan di Compiler Explorer

Analisis taint aktif secara default

Kasus analisis kernel melalui CVE-2011-2210

  • Cuplikan contoh dari CVE-2011-2210 di kernel Linux digunakan sebagai kasus analisis taint
  • Dengan menambahkan __attribute__((tainted_args)) ke makro __SYSCALL_DEFINEx, analyzer diberi tahu bahwa argumen osf_getsysinfo adalah nilai yang berasal dari luar batas kepercayaan dan harus diperlakukan sebagai nilai ter-taint
  • Analyzer GCC 14 memperingatkan bahwa di copy_to_user(buffer, hwrpb, nbytes), nilai nbytes yang dikendalikan penyerang digunakan sebagai ukuran tanpa pemeriksaan batas atas
    • Diagnostik menunjukkan bahwa nbytes hanya menjalani pemeriksaan batas bawah di if (nbytes < sizeof(*hwrpb))
    • Parameter ketiga copy_to_user ditandai sebagai parameter ukuran melalui atribut access(write_only, 1, 3)
  • Masalahnya adalah kondisi sanitasi ditulis sebagai if (nbytes < sizeof(*hwrpb))
  • Pekerjaan menjalankan analyzer pada kernel untuk menemukan kerentanan dan memperbaiki false positive analyzer masih terus berlangsung

1 komentar

 
GN⁺ 2024-04-05
Komentar Hacker News
  • Bagi saya, fanalyzer adalah salah satu fitur pembeda utama GCC dibanding Clang. Karena ia menjelaskan error, pemrograman C jadi jauh lebih mudah, dan pesan error-nya mulai terasa mirip Rust dalam hal ramah terhadap pengembang

    • Saya tahu Rust sangat disorot, terutama di HN, karena keamanan memori dan abstraksinya yang bagus, tetapi saya penasaran seberapa besar bagian dari popularitas Rust yang berasal dari pesan error-nya
      Salah satu alasan terbesar orang berhenti mempelajari teknologi adalah error yang menjengkelkan atau tidak jelas. Agak keluar topik, tapi maksud saya: saya suka C, dan saya akan lebih menyukainya lagi kalau punya pesan error setingkat Rust
    • Clang juga punya alat serupa, yaitu Clang Static Analyzer: https://clang-analyzer.llvm.org/
    • Pengalaman saya justru kebalikannya. Clang sering menampilkan pesan error yang jauh lebih baik daripada GCC, dan implementasi sebagian warning atau error-nya menangkap lebih banyak kasus, sementara clang-tidy memberi analisis statis yang jauh lebih baik
    • Ini mengingatkan saya pada salah satu alasan saya sangat tidak suka C++. Alih-alih error: missing semicolon, sering kali yang keluar adalah lebih dari 1000 baris pesan error tentang instansiasi template
    • Ini terdengar cukup mengejutkan. Saya penasaran apa yang ditangkap analyzer GCC yang belum dilaporkan oleh static analyzer Clang
      Saya sudah beberapa kali mencoba analyzer GCC, tetapi belum menemukan frontend yang bagus untuk membuat output-nya nyaman dibaca. Clang punya beberapa opsi seperti output HTML yang lumayan bagus, CodeChecker, integrasi Xcode, dan lain-lain; jadi saya penasaran orang membaca output GCC dengan cara apa. Selain itu, saya merasa GCC menghasilkan jauh lebih banyak false positive daripada Clang
  • Ada 36 komentar lagi di thread lain: https://news.ycombinator.com/item?id=39918278
    “GCC 14 Boasts Nice ASCII Art for Visualizing Buffer Overflows (phoronix.com)”, diposting 2 jam lalu

  • Beberapa bulan lalu saya membuat utilitas Linux kecil. Itu adalah shim drop-in untuk menggantikan executable apa pun; saat dipanggil, ia berpura-pura menjadi program asli, lalu melakukan fork terhadap aslinya dan terhubung ke stdout/stderr
    Output error dikirim ke asisten GPT kustom yang memahami konteks program tersebut, lalu asisten itu mengubah error asli menjadi bentuk yang lebih mudah dibaca manusia dan mengeluarkannya ke stderr milik shim. Saya memakainya karena lelah melihat dump compiler GCC/Clang yang bertumpuk untuk concept/template, tetapi sebenarnya bisa dipakai untuk program apa pun kalau diinginkan. Itu bekerja dengan baik, tetapi saya jatuh sakit parah dan tidak bisa melanjutkannya; rasanya ini bisa jadi proyek yang bagus kalau ada yang mau membuat ulang dengan benar dan menggeneralisasikannya

  • Akan bagus kalau hasil analisis punya format output yang lebih baik. Bentuk sekarang adalah neraka bagi screen reader

    • Sebagai catatan, saya mengimplementasikan output SARIF di GCC 13, dan misalnya bisa dilihat di VS Code lewat plugin. Namun ASCII art belum termasuk
      Contoh output bisa dilihat di sini: https://godbolt.org/z/aan6Kfxds
      Itu adalah contoh pertama dari artikel dengan opsi command line -fdiagnostics-format=sarif-stderr ditambahkan. Saya juga bereksperimen dengan menghasilkan diagram sebagai SVG, tetapi belum cukup dipoles untuk dimasukkan ke GCC 14
  • Kode aslinya berbentuk mengembalikan -1 jika nbytes < sizeof(*hwrpb), dan mengembalikan -2 jika copy_to_user(buffer, hwrpb, nbytes) gagal. Perbaikan yang diterapkan adalah pemeriksaan nbytes > sizeof(*hwrpb), tetapi menurut saya perbaikan yang benar adalah copy_to_user(buffer, hwrpb, sizeof(*hwrpb))
    Menyalin keluar ukuran dari pointer hwrpb selain sizeof(*hwrpb) tidak masuk akal

    • Jika pemanggil memberikan nbytes = 4 dan sizeof(hwrpb) adalah 16 byte, maka kita akan menyalin berlebihan 12 byte dari buffer pemanggil dan bisa membaca memori yang bukan miliknya. Menurut saya itu harus dihindari
      Solusi yang lebih baik adalah menyalin hanya jumlah byte minimum yang didukung oleh pemanggil dan yang dipanggil. Misalnya, nbytes = MIN(nbytes, sizeof(hwrpb));. Dengan asumsi informasi versi di hwrpb->size ditaati, ini bisa menjamin kompatibilitas mundur/maju meskipun sebagian struct belum diinisialisasi
    • Betul. Tetapi karena ukuran buffer sudah diberikan, juga tidak masuk akal menimpa akhir buffer pemanggil, jadi kita pun tidak bisa mengirim nilai yang lebih besar dari nbytes
  • Sangat hebat. Jumlah kerja yang masuk terlihat luar biasa. Tingkat kesulitannya tampak sebanding dengan menambahkan fat pointer/array view ke pustaka standar dan standar C

  • -Wstringop-overflow punya terlalu banyak false positive sehingga itu adalah warning pertama yang saya nonaktifkan. Saya ragu variannya di analyzer akan lebih baik

    • Rasanya seperti melepas baterai detektor karbon monoksida karena bunyinya terus-menerus bikin pusing dan mengantuk
  • Keren sekali. Saya tidak terlalu banyak menulis C akhir-akhir ini, jadi saya penasaran seberapa sering strcpy dan strcat masih dipakai. Terakhir kali saya cek, keduanya hampir termasuk hal-hal yang ditabukan seperti goto
    Tentu saya tahu bahwa dalam pengembangan kernel, goto justru sering dipilih. Saya penasaran seberapa membantu sebenarnya analisis string C ini

    • Dalam konteks tertentu, penggunaan goto jelas merupakan pilihan yang benar dan elegan. Jika dihindari secara mutlak, hasilnya bisa menjadi kode yang jelek, berbelit, dan sulit dirawat. Memang tidak sering, tetapi ada penggunaan yang valid
      Fungsi seperti strcpy memang kurang disarankan, tetapi ada juga situasi di mana penggunaannya dijamin benar selama invarian yang lebih kuat—misalnya invarian di tingkat bahasa—tidak dilanggar. Jika kasus seperti itu rusak, berarti sudah ada masalah yang jauh lebih besar. Jarang terjadi, tetapi bisa saja diperdebatkan bahwa alternatif yang secara nominal lebih aman sedikit kurang efisien tanpa memberi manfaat
    • Selama digunakan secara logis setara dengan konstruksi structured programming yang tidak ada di C, beberapa penggunaan goto masih idiomatis di C. Tetap harus hati-hati, tetapi ya, ini C
      Namun saya sama sekali tidak suka longjmp
    • Tidak ada masalah dengan penggunaan goto yang sederhana. Sebaliknya, keluarga strxcpy benar-benar berantakan dan tidak boleh dipakai dengan alasan apa pun. Fakta bahwa itu dipakai di kernel itu mengerikan
      Fungsi-fungsi itu dan semua upaya gagal untuk “memperbaikinya” seharusnya dibuang ke luar orbit
    • goto tidak masalah jika dipakai dengan hati-hati. strcpy dan strcat juga “tidak masalah” dalam arti Anda tahu kodenya benar dan kalau salah, akibatnya akan besar. Sayangnya, deskripsi itu berlaku untuk sebagian besar C
    • Saya tidak menganggap goto setabu strcat dan strcpy. goto itu oke, sedangkan strcat dan strcpy yang dipakai tanpa malloc berukuran tepat dalam scope yang sama lebih mendekati bau kode
  • Sangat bagus. Senang melihat semua laporan terperinci yang menjelaskan apa yang salah disertakan