1 poin oleh GN⁺ 3 jam lalu | 1 komentar | Bagikan ke WhatsApp
  • Di Linux 7.2, penggunaan API strncpy di dalam kernel telah hilang, sehingga antarmuka penyalinan string yang sejak lama dijadwalkan untuk dihentikan akhirnya dihapus sepenuhnya
  • strncpy() menyalin sebanyak jumlah byte yang ditentukan, tetapi perilaku terminasi NUL-nya tidak intuitif sehingga selama bertahun-tahun tetap menjadi sumber bug di kernel
  • Karakteristiknya yang mengisi buffer tujuan dengan 0 secara tidak perlu bahkan menimbulkan masalah performa, dan butuh sekitar 6 tahun serta 362 commit untuk menyingkirkannya
  • Dalam merge pada hari Jumat, bukan hanya API utamanya yang dihapus, tetapi juga implementasi spesifik arsitektur per-CPU yang terakhir
  • Kode kernel kini harus memilih fungsi pengganti sesuai kebutuhan, seperti strscpy(), strscpy_pad(), strtomem_pad(), memcpy_and_pad(), dan memcpy()

strncpy yang menghilang di Linux 7.2

  • Linux 7.2 akhirnya menghapus API strncpy yang sejak lama direncanakan untuk dihentikan dari kernel
  • Setelah pekerjaan pembersihan selama 6 tahun, kini tidak ada lagi kode internal kernel yang menggunakan antarmuka strncpy
  • Perubahan ini bukan sekadar penggantian fungsi, melainkan lebih mendekati upaya menghapus praktik penyalinan string lama di seluruh kernel

Skala pekerjaan hingga penghapusan

  • Penghapusan strncpy membutuhkan sekitar 362 commit
  • Pekerjaan dilakukan dengan cara menghapus penggunaan strncpy di dalam kernel secara bertahap
  • Di Linux 7.2, pekerjaan pembersihan ini akhirnya mencapai titik penyelesaian

Mengapa strncpy menjadi masalah di kernel

  • strncpy telah lama dianggap sebagai penyebab bug yang terus berulang di dalam kernel Linux
  • Secara khusus, ada dua perilaku yang menjadi masalah
    • Makna dan perilaku terminasi NUL tidak intuitif sehingga mudah menimbulkan kesalahan penggunaan
    • Buffer tujuan diisi 0 secara berulang, sehingga menimbulkan biaya performa yang tidak perlu

Merge penghapusan yang sebenarnya

  • Merge yang dilakukan pada hari Jumat menghapus API strncpy
  • Dalam merge yang sama, implementasi strncpy spesifik arsitektur per-CPU yang terakhir juga ikut dihapus

API pengganti untuk kode kernel

  • Sebagai pengganti strncpy, kini perlu memilih fungsi yang sesuai dengan target penyalinan dan kondisi terminasi
    • strscpy(): digunakan untuk tujuan yang berakhir dengan NUL
    • strscpy_pad(): digunakan saat tujuan berakhir dengan NUL dan membutuhkan padding 0
    • strtomem_pad(): digunakan untuk field lebar tetap yang tidak diakhiri NUL
    • memcpy_and_pad(): digunakan untuk penyalinan terbatas dengan padding eksplisit
    • memcpy(): digunakan untuk penyalinan memori saat panjangnya sudah diketahui

1 komentar

 
GN⁺ 3 jam lalu
Komentar Hacker News
  • Dulu orang suka mengejek pengembang kernel Linux—yang katanya termasuk pengembang C terbaik di dunia—karena tidak bisa membuat tipe stringbuffer atau stringview, tetapi saat itu memang belum ada konsensus seperti sekarang soal topik ini, jadi masih bisa dimaklumi
    Orang yang sudah melihat arah yang benar adalah Dennis Ritchie, yang pada 1990 mengusulkan tipe fat pointer untuk C. Itu akan menjadi tambahan yang sempurna andaikan masuk ke C99, dan kalau komite memasukkannya, dunia mungkin akan cukup berbeda
    Pada 2007 sempat ada kesempatan kedua saat tulisan Walter Bright “C's greatest mistake” terbit, yang pada dasarnya menjelaskan slice/stringview dengan lebih jelas—ide yang esensinya sama dengan Ritchie—tetapi tetap tidak masuk ke C11. Sekarang pun sudah sampai C23 dan itu masih belum ada; sebagai gantinya kita mendapat _Generic dan VLA, jadi rasanya seperti ya sudahlah, pesta saja

    • Tulisan Walter Bright tahun 2007 ada di sini: https://digitalmars.com/articles/C-biggest-mistake.html
      Saat mencari-cari saya juga melihat thread Reddit tentang topik yang sama, dan debat bikeshedding-nya lucu: https://www.reddit.com/r/C_Programming/comments/90uq7c/cs_bi...
      Saya penasaran kenapa perilaku array C yang meluruh menjadi pointer dirancang seperti itu. Ada penjelasan bahwa tujuannya agar kode B bisa dikompilasi ke C dengan perubahan seminimal mungkin; di B, deklarasi array ternyata benar-benar mendefinisikan pointer dan array, lalu pointer itu diinisialisasi agar menunjuk ke elemen pertama array
    • VLA diturunkan menjadi fitur opsional di C11, dan menurut saya itu hal yang bagus
      Masalah yang lebih besar sekarang adalah pustaka standar C masih terikat pada era K&R, dan bahkan fitur bahasa seperti argumen atau nilai balik struct yang ditambahkan di C99 pun tidak tercermin dalam API pustaka standarnya. Andai pustaka standar punya struct rentang berupa pasangan pointer/ukuran, plus fungsi string baru atau fungsi string yang diperbarui untuk memakainya, situasinya bisa jauh lebih baik
    • Tautan usulan Ritchie: https://web.archive.org/web/20150611114358/https://www.bell-...
    • Dalam kerja tim, ini pola yang paling mengganggu buat saya. Ada solusi A, B, C, masing-masing punya pro dan kontra, didiskusikan selama 2 minggu, lalu akhirnya tidak memilih apa pun
    • Itu cuma menunjukkan di mana prioritas WG14 berada
  • strncpy di dalam kernel Linux disebut sudah bertahun-tahun menjadi “sumber bug yang membandel” karena semantiknya yang tidak intuitif, penanganan terminasi NUL, dan masalah performa akibat mengisi tujuan dengan 0 secara tidak perlu
    Setiap kali saya diminta mereview kode C, saya mencari strncpy, dan hampir selalu menemukan bug di sana

  • Ada hal-hal yang sudah mengganggu selama 40 tahun. String berterminasi NUL, dan sekarang bahkan string non-UTF-8 untuk I/O pun termasuk di dalamnya
    Begitu juga kebiasaan menangani akhir baris sebagai LF, CR, atau CRLF, serta cara memisahkan field dengan pipa atau koma. Kalau sejak awal dipakai karakter ASCII yang tidak ambigu seperti GS, FS, dan RS, encoding/decoding akhir baris akan menjadi masalah I/O, sementara HT/VT/CR/LF/FF bisa tetap menjadi kode yang murni terkait output

    • Saya pernah mengerjakan proyek yang mengubah data berbingkai dengan karakter pemisah field/record ASCII seperti itu, dan hasilnya sangat mudah ditangani
      Kekacauan soal penanganan escape yang biasa muncul pada data berbasis koma hilang, jadi semuanya jauh lebih sederhana
    • Di Unicode sekarang bahkan ada lebih banyak pilihan. Ada NL Next line yang terasa seperti warisan EBCDIC, lalu LS Line separator dan PS Paragraph separator yang dibuat Unicode
      Standar Unicode mengatakan bahwa bukan hanya CR, LF, CRLF, dan karakter-karakter itu saja yang harus diperlakukan sebagai pemisah baris, tetapi juga vertical tab dan form feed
    • UTF-8 bekerja dengan sangat baik di input/output standar. Tentu, itu kalau Anda tidak memakai Windows yang tampaknya masih terjebak di awal 90-an dalam hal encoding teks internasional
      Akhir baris seperti LF, CR, dan CRLF juga merupakan konvensi sistem operasi, dan lebih baik bahasa pemrograman tidak mencoba “menebak” akhir baris yang benar. Itu menciptakan lebih banyak masalah daripada menyelesaikannya, dan sekali lagi ini kebanyakan masalah khas Windows, jadi Microsoft-lah yang harus membawa Windows ke abad ini
    • LF paling masuk akal, tetapi untuk file teks sebenarnya salah satu pun tidak masalah. Masalahnya adalah CSV itu bukan teks
      Terakhir kali saya harus menangani file CSV di bash, saya mengubahnya dulu secara internal menjadi RS dan FS untuk diproses
    • Menurut saya, pakai saja UTF-8 di mana-mana
  • Sebagai pengganti strncpy, di kode kernel Linux disarankan memakai strscpy() untuk tujuan berterminasi NUL, strscpy_pad() untuk tujuan berterminasi NUL yang butuh padding 0, strtomem_pad() untuk field lebar tetap yang tidak berterminasi NUL, memcpy_and_pad() untuk penyalinan berbatas dengan padding eksplisit, dan memcpy() untuk penyalinan memori dengan panjang yang sudah diketahui
    Ini terdengar seperti mimpi buruk, dan saya tidak tahu apakah memang harus serumit ini

    • Alasannya adalah performa. Fungsi serbaguna yang aman untuk menangani sebagian besar kasus ini pasti melambat karena percabangan internal, dan pilihan fungsi juga memuat niat si pengembang
      Saat membaca kode, saya rasa lebih baik kalau niat pengembang bisa langsung terlihat jelas hanya dari fungsi yang dipilih
    • Memakai strncpy dengan benar memang dari dulu selalu rumit
    • Paling tidak, apa nama-namanya tidak bisa dibuat sedikit lebih baik?
  • Pekerjaan repetitif yang membosankan seperti inilah tempat pekerjaan nyata rekayasa sistem benar-benar terjadi
    Proyek infrastruktur besar seperti ini, yang membuat kernel Linux lebih andal sambil tetap menjaganya bisa dipakai di dunia nyata sepanjang seluruh proses, bergerak dalam skala puluhan tahun, bukan hitungan bulan

    • Saya paham kenapa skalanya jadi puluhan tahun. Ekor panjang pengguna dan dependensinya memang sangat panjang
      Tapi saya tidak yakin apakah dengan kecepatan seperti itu masih bisa dihasilkan kemajuan jangka panjang yang benar-benar berarti. Ini bukan keluhan, lebih seperti paradoks infrastruktur inti
  • Ini pekerjaan yang luar biasa dan bikin rendah hati. Mengejutkan melihat begitu banyak orang ikut berkontribusi
    “Fitur baru yang keren” mudah mendapat pengakuan, tetapi pada sesuatu yang mendasar seperti kernel, menghapus fitur buruk justru bisa jadi lebih penting
    Jika 50 tahun lagi orang-orang lupa cara membaca source code, sementara ampas Claude/Codex diam-diam menumpuk dan membakar sebagian besar energi bumi, pekerjaan seperti ini mungkin akan tersisa sebagai legenda dari “era pendirian”

    • Jadi teringat Deepness in the Sky karya Vernor Vinge. Di sana ada seseorang yang memelihara pesawat antariksa lewat arkeologi perangkat lunak
      Sekaligus satu-satunya orang yang tahu apa itu Unix epoch
    • Sepertinya 50 tahun lagi tidak semua orang akan lupa cara memahami source code. Dorongan manusia untuk ingin tahu bagaimana sesuatu bekerja akan tetap ada saat itu
    • Kode gado-gado buatan AI menurut saya akan menjadi tak tertangani jauh sebelum itu
  • Saya rasa string berakhiran 0 adalah kesalahan terbesar dalam sejarah komputasi. String gaya Pascal jauh lebih aman

    • Ada juga titik tengah seperti BSTR yang dipakai Visual Basic, lalu belakangan COM
      Itu tetap pointer ke array karakter yang diakhiri 0, tetapi tepat sebelum byte pertama yang ditunjuk pointer ada field panjang. Dengan asumsi tidak ada NUL tertanam, ini juga kompatibel dengan string C, dan fungsi bertipe BSTR bisa memanfaatkan nilai panjangnya
    • Saya cukup setuju, tetapi pasti akan ada perdebatan soal tipe data untuk field ukuran. Jika panjangnya tidak variabel, itu juga akan jadi masalah, dan kalau variabel, akan muncul masalah lain
      Pada suatu masa, bahkan 16-bit mungkin terasa terlalu berlebihan, sedangkan sekarang 32-bit bisa terasa terlalu kecil. C yang disebut bahasa dengan “strong typing” justru cukup longgar di bagian yang sebenarnya penting
    • String berakhiran 0 adalah fondasi bagi sangat banyak perangkat lunak yang berguna. Menyebutnya kesalahan terbesar dalam komputasi agak berlebihan
      Saya sudah lebih dari 30 tahun tidak menulis kode Pascal, tetapi samar-samar ingat bahwa bahkan dulu pun sistem string-nya terasa terlalu merepotkan untuk dipakai
    • Bukankah 255 karakter seharusnya cukup untuk semua orang?
    • Sama buruknya dengan baris yang diakhiri newline
  • Terlalu banyak rasa sakit dan kerja sia-sia yang muncul hanya karena tidak ada tipe data string

    • Lebih tepatnya, bukan semata karena tidak ada tipe data string, melainkan karena rasa sakit dan kerja sia-sia yang timbul saat mencoba menyiasati fakta bahwa C tidak punya tipe data string
    • Kalau mau memperkenalkan strong typing di sini, pendekatan seperti apa yang mungkin? Rasanya perlu refaktor besar-besaran agar kode di sekitar strncpy juga memakai tipe dan fungsi itu, bukan?
  • Saya penasaran apa yang membuat penulisan ulang penggunaan strncpy begitu sulit sampai butuh 6 tahun
    Apakah karena cakupan pemakaiannya memang seluas itu, atau ini pekerjaan jangka panjang yang hanya diganti saat kebetulan menyentuh file yang sama, atau ada kesulitan lain?

  • Saya pernah menangani kode di aplikasi Win32 yang memakai string berpadding spasi. String tujuan dipadding dengan spasi, tetapi byte terakhirnya tetap null terminator
    Untuk operasi seperti panjang dan penyalinan, harus memakai versi khusus dari fungsi string. Saya tidak tahu alasannya, tetapi codebase-nya sangat tua, jadi mungkin berasal dari perilaku struct Pascal

    • Bisa jadi itu berasal dari string di field char database SQL. Bukan varchar, field char memang dipadding dengan spasi
    • Akar perilaku ini sepertinya bukan Pascal, melainkan COBOL
    • Bisa juga untuk mencegah realokasi saat ukuran string berubah, atau mungkin karena alignment cache line CPU