- Di Windows 11 24H2, masalah pesawat amfibi Skimmer yang menghilang atau pemain terpental sangat tinggi ke langit segera setelah spawn berhasil direproduksi, dan penyebabnya bukan OS melainkan bug lama pada pemrosesan data internal game
- Baris
vehicles.ide milik Skimmer kekurangan dua nilai skala roda yang dibutuhkan pesawat, tetapi CFileLoader::LoadVehicleObject tidak memeriksa nilai balik sscanf sehingga langsung memakai variabel lokal yang belum diinisialisasi
- Di lingkungan Windows lama, nilai skala roda
0.7 milik kendaraan sebelumnya, TopFun, kebetulan tertinggal di stack sehingga Skimmer tampak normal, tetapi di Windows 11 24H2 penggunaan stack oleh LeaveCriticalSection berubah dan kebetulan itu pun hilang
- Skala roda yang salah merusak perhitungan suspensi dan koordinat Z collision box, lalu menyebar sampai ke tinggi spawn dan perhitungan kecepatan baling-baling, memicu posisi kamera aneh, efek burn-in, dan loop macet di lingkungan SilentPatch
- Solusinya adalah menambahkan
-1, 0.7, 0.7, -1 ke baris Skimmer di vehicles.ide atau menerapkan hotfix SilentPatch berikutnya, dan validasi data input serta pengelolaan peringatan kompilasi berpengaruh langsung pada kompatibilitas jangka panjang
Gejala Skimmer yang muncul di Windows 11 24H2
- Di issue tracker SilentPatch, muncul laporan bahwa setelah pembaruan Windows 11 24H2, pesawat Skimmer benar-benar hilang dari game
- Tidak bisa di-spawn bahkan dengan trainer, dan juga tidak ditemukan di lokasi spawn aslinya
- Masalah ini bisa direproduksi baik pada game yang memakai mod maupun pada salinan vanilla yang hanya dipasangi SilentPatch
- Di GTAForums, masalah yang sama juga dilaporkan sejak November 2024, dan meski sebagian pengguna mencurigai SilentPatch, gejala yang sama muncul bahkan pada game tanpa mod sama sekali
- Di Windows 10 22H2 dan Windows 11 23H2, Skimmer spawn normal, sedangkan pengguna Windows 11 24H2 mengalami bug yang sama
- Hasil remote debugging pada VM 24H2 menunjukkan pesawat dan kapal lain normal, dan hanya Skimmer yang menghilang
Ketinggian abnormal dan loop baling-baling yang tak pernah selesai
- Saat Skimmer dipaksa dibuat lewat skrip dan CJ dinaikkan ke dalamnya, pemain terpental ke
1.0287648030984853e+0031 m, sekitar 10,3 noniliun meter ke atas
- Jika SilentPatch terpasang, game masuk ke loop dan macet segera setelah pemain dilontarkan ke atas
- Tanpa SilentPatch, game tidak macet, tetapi muncul efek burn-in terkenal yang terjadi ketika kamera berpindah ke posisi yang mendekati tak hingga
- Titik macet berada di loop normalisasi sudut rotor dalam
CPlane::PreRender
- Nilai
m_fBladeSpeed membesar hingga 3.73340132e+29
- Walaupun
6.2831855 terus dikurangi berulang kali, nilainya tidak berubah dalam representasi floating-point sehingga loop tidak pernah selesai
- Karena kecepatan baling-baling diturunkan dari nilai yang proporsional terhadap ketinggian pesawat, ini menjadi petunjuk bahwa Skimmer sejak awal dibuat pada posisi yang sangat tidak normal
Perhitungan suspensi yang merusak collision box
- Fungsi pembuatan lewat skrip
CCarCtrl::CreateCarForScript menambahkan hasil GetDistanceFromCentreOfMassToBaseOfModel ke koordinat Z yang diberikan
- Saat collision box Skimmer diperiksa,
bbox.sup.z ternyata sudah tercemar dengan nilai tidak masuk akal seperti -4.30747210e+33
- Penelusuran dengan data breakpoint menunjukkan bahwa nilai collision box saat loading awal sebenarnya normal
- Nilai awal
bbox.sup.z adalah -2.21952772
- Setelah kendaraan pertama kali spawn,
SetupSuspensionLines memperbarui koordinat Z collision box dengan memperhitungkan tinggi suspensi
- Salah satu nilai input yang masuk ke perhitungan garis suspensi itulah sumber masalahnya
- Perhitungan memakai batas atas dan bawah suspensi dari
handling.cfg serta skala roda dari vehicles.ide
- Nilai Skimmer di
handling.cfg tidak jauh berbeda dari pesawat lain
Baris vehicles.ide Skimmer yang terlalu pendek
- Definisi Skimmer di
vehicles.ide lebih pendek daripada pesawat lain, dan empat parameter terakhir hilang
- Dua dari nilai yang hilang itu adalah skala roda depan dan belakang
- Pada kapal, tidak adanya nilai ini tidak menimbulkan masalah, tetapi Skimmer adalah satu-satunya pesawat yang menghilangkan parameter tersebut
- Skimmer tampaknya awalnya didefinisikan sebagai kapal di Vice City lalu diubah menjadi pesawat di San Andreas, namun parameter baru yang diperlukan tidak ikut ditambahkan
- Jika parameter yang hilang dikembalikan, Skimmer kembali berfungsi normal
Loader yang tidak memeriksa nilai balik sscanf
CFileLoader::LoadVehicleObject mem-parse satu baris vehicles.ide dengan sscanf sambil berasumsi semua parameter selalu ada
- Fungsi ini tidak memeriksa nilai balik
sscanf, dan juga tidak memberi nilai default pada sebagian besar parameter terakhir
wheelModelID tidak diinisialisasi
frontWheelScale dan rearWheelScale juga tidak diinisialisasi
- Hanya
wheelUpgradeClass yang diinisialisasi ke -1
- Pada baris yang kekurangan nilai seperti Skimmer, variabel skala roda tetap belum diinisialisasi dan nilainya lalu menyebar ke data kendaraan
- Perbaikan SilentPatch membungkus pemanggilan
sscanf dan memberi nilai default pada empat nilai terakhir
wheelModelID = -1
frontWheelSize = 0.7f
rearWheelSize = 0.7f
wheelUpgradeClass = -1
- Commit perbaikannya tercermin di repositori SilentPatch
Mengapa bug ini tersembunyi selama 20 tahun
- San Andreas memakai CRT yang dikompilasi statis, jadi bukan berarti hotfix tingkat CRT di Windows mengubah perilaku
sscanf
- Di Windows 10, nilai
0.7 masih tertinggal di lokasi variabel lokal tepat sebelum parsing Skimmer
- Nilai ini cocok dengan skala roda TopFun yang didefinisikan tepat sebelum Skimmer
- Baris TopFun berisi
-1, 0.7, 0.7, -1
vehicles.ide dibaca berurutan, dan LoadVehicleObject dipanggil untuk setiap baris
- Di Windows 10, posisi stack tersebut tidak tertimpa di antara panggilan
LoadVehicleObject, sehingga Skimmer secara kebetulan mewarisi skala roda TopFun
- Di Windows 11 24H2,
LeaveCriticalSection di dalam fgets memakai ruang stack lebih banyak saat membaca baris berikutnya, sehingga nilai yang tersisa tadi tertimpa
Windows 11 24H2 hanyalah pemicunya
- Cara fungsi internal WinAPI memakai stack bukan perilaku yang dijamin, dan bisa berubah tanpa pemberitahuan sebelumnya
- Windows 11 24H2 hanya menghapus nilai sisa di stack yang selama ini secara kebetulan diandalkan game; akar masalah sebenarnya adalah undefined behavior di dalam game
- Bahkan di Windows 10, variabel lokal tepat setelah skala roda sudah pernah tertimpa oleh
LeaveCriticalSection, jadi game sebenarnya sudah lama berada dalam kondisi yang bisa memicu bug ini
- Karena San Andreas juga mendukung Windows 98, bug ini setidaknya tidak sengaja tersembunyi di sekitar belasan versi Windows dan beberapa rilis Wine
- Di patch resmi PC 1.01, bug ini tidak diperbaiki, tetapi pada rilis Xbox aslinya sudah ada perbaikan yang memberi nilai default
1.0
- Steam 3.0, newsteam, dan RGL mewarisi perbaikan ini karena berbasis cabang kode Xbox
- Rilis Android, X360, PS3, dan Definitive Edition dari War Drum Studios juga tidak terdampak
Mengapa SilentPatch memilih 0.7 sebagai nilai default
- SilentPatch memakai skala roda default
0.7, bukan 1.0 seperti perbaikan Xbox dari Rockstar
- Ada tiga alasan untuk pilihan ini
- Di versi PC, selama ini Skimmer pada praktiknya memang berjalan dengan skala roda
0.7 milik TopFun
- Sea Sparrow dan Vortex, dua kendaraan non-kapal lain yang mengapung di air, juga memakai skala roda
0.7
- Banyak mobil lain di dalam game juga memakai skala roda
0.7
Cara memperbaikinya sendiri
- Perbaikan kode akan dimasukkan ke hotfix SilentPatch berikutnya
- Untuk memperbaikinya sekarang juga, buka
data\vehicles.ide di direktori San Andreas dengan Notepad lalu ganti baris yang diawali 460, skimmer
- Baris penggantinya adalah sebagai berikut
460, skimmer, skimmer, plane, SEAPLANE, SKIMMER, null, ignore, 5, 0, 0, -1, 0.7, 0.7, -1
Pelajaran dari kompatibilitas game lama
- Masalah ini hanyalah bug sederhana di San Andreas, dan fungsi tersebut sejak awal memang berupa kode yang tidak mungkin bekerja dengan benar
- Perubahan layout stack pada implementasi internal pun dapat berubah menjadi masalah kompatibilitas jika aplikasi yang bug bergantung secara kebetulan pada perilaku tertentu
- Contoh serupa adalah Bully: Scholarship Edition yang rusak di Windows 10 karena bergantung pada asumsi yang salah hingga masalahnya muncul saat OS berubah
- Masalah mendasar San Andreas adalah tidak adanya validasi data input yang mampu menolak baris konfigurasi yang tidak lengkap
- Kode ini kemungkinan besar sejak awal memunculkan peringatan kompilasi, dan jika peringatan diabaikan atau dinonaktifkan, bug yang lama tersembunyi dapat muncul sebagai masalah nyata bagi pengguna
1 komentar
Opini Hacker News
Tulisan seperti ini rasanya hanya bisa diharapkan dari Raymond Chen, dan itu pujian yang sangat besar
Senang melihat penulisnya menggali lebih jauh sampai menemukan persis alasannya
Secara pribadi, menurut saya jika itu adalah perilaku yang tidak termasuk dalam kontrak, sebaiknya dibuat acak
Misalnya, jika sebuah bahasa tidak menjamin urutan iterasi map, maka urutannya seharusnya sengaja diacak
Kalau tidak, akan muncul kode rapuh yang “berjalan baik sampai suatu hari tiba-tiba rusak”
-ftrivial-auto-var-inityang menginisialisasi variabel yang belum diinisialisasi dengan nilai tertentu atau nilai acakNamun mengacak atau mengisi nol seluruh isi stack pada setiap pemanggilan fungsi akan menimbulkan penurunan performa yang mengerikan, jadi biasanya itu tidak dilakukan
Ada tool yang melakukan hal seperti ini untuk tujuan debugging, tetapi dalam mode itu program berjalan jauh lebih lambat
Mungkin inilah alasan para maintainer kernel Linux bersikeras agar user space jangan pernah dirusak
Jika pengguna API cukup banyak, apa yang dijanjikan dalam kontrak tidak lagi penting; akan selalu ada seseorang yang bergantung pada setiap perilaku sistem yang dapat diamati
Jika Anda menjanjikan pengacakan, akan ada seseorang yang bergantung juga pada pengacakan itu
Maka itu pun tidak akan pernah bisa dihapus
Anda tidak dipaksa membayar overhead yang tidak perlu seperti inisialisasi variabel yang tidak digunakan
Pada bagian “jangan abaikan peringatan kompilasi”, saya tidak tahu error compiler seperti apa yang bisa diharapkan di sini
Mungkin sebatas tidak memeriksa apakah nilai balik
scanfsesuai dengan jumlah argumen? Selain itu, ini tampak seperti kesalahan file data yang tidak bisa diketahui compilersscanftidak diperiksaDalam contoh kecil, bahkan dengan
g++ -Wall -Wextra -Wunused-resultpun tidak muncul peringatanNamun karena seluruh baris diparsing dengan satu pemanggilan
sscanf, analisis statis compiler mau tidak mau harus mengasumsikan nilai-nilai itu kini sudah diinisialisasiSepertinya tidak ada metode analisis statis umum untuk menangkap bug ini
Namun mungkin bisa dibuat peringatan khusus untuk
scanfyang memaksa nilai yang sudah diinisialisasi sebelumnya diteruskan, atau nilai baliknya diperiksaMembaca tulisan analisis teknis mendalam seperti ini selalu menyenangkan
Saya penasaran apakah tulisan seperti ini akan makin langka di era AI atau tidak
AI tidak akan menggantikan mereka, dan berbagai inovasi pengembangan software selama lebih dari 50 tahun terakhir pun tidak berhasil melakukannya
Jutaan, mungkin puluhan juta developer bahasa pemrograman tingkat tinggi hanya mengenal perbedaan stack dan heap sebagai teori samar yang pernah dipelajari di sekolah, dan karena tidak perlu memikirkannya dalam pekerjaan sehari-hari, mereka juga tidak tertarik
Saya lebih penasaran apa yang berubah pada implementasi lock/unlock critical section di versi Windows ini
Apakah hanya saya yang terganggu oleh kode ini?
while (this->m_fBladeAngle > 6.2831855) { this->m_fBladeAngle = this->m_fBladeAngle - 6.2831855; }Rasanya seperti memakai loop
whileyang bisa saja menjadi loop tak berujung hanya karena malas melakukan pembagianNamun melihat mereka pernah mem-parsing JSON dengan
sscanfsampai waktu loading GTA5 bisa bertambah 5 menit, ekspektasi saya tidak terlalu tinggiCompiler juga mungkin punya teknik untuk mengoptimalkannya lebih jauh
Sebenarnya nyaris tidak ada cara agar ini menjadi loop tak berujung. Underflow memang mungkin, tetapi untuk itu sudutnya harus sudah lebih kecil dari
2*pi, sehingga loop akan keluarfmodBagi yang bermasalah mengaksesnya, bisa memakai tautan ini
https://web.archive.org/web/20250423144746/https://cookieplm...
Karena tahu C/C++, sejak awal blog saya sudah bisa menebak kira-kira apa yang terjadi, yaitu masalah variabel yang belum diinisialisasi
Mengejutkan bahwa ada bahasa yang membiarkan variabel dibiarkan tanpa diinisialisasi. Hal ini telah menyebabkan bug yang tak terhitung jumlahnya, termasuk bug produksi yang pernah saya lihat sendiri, dan untuk menangkapnya sering kali harus bergantung pada flag compiler tambahan, tool analisis statis, Valgrind, dan sebagainya
Bahasa yang lebih modern memilih solusi lain, seperti memakai nilai default 0 atau mewajibkan inisialisasi sebelum digunakan, tetapi orang-orang tetap kembali ke C/C++
Bagian “Semua temuan ini membuktikan bahwa bug tersebut bukan masalah Windows 11 24H2. Hal seperti cara fungsi WinAPI internal menggunakan stack bukanlah kontrak, dan dapat berubah kapan saja tanpa pemberitahuan sebelumnya” mengingatkan saya pada tulisan bagus yang pernah saya baca
Intinya, pada API yang cukup sukses, tidak ada yang namanya API privat