Menggunakan pembelajaran mesin dalam analisis statis kode sumber program

Menggunakan program pembelajaran dalam program analisis statistik kode sumber

Pembelajaran mesin berakar dalam di berbagai bidang aktivitas manusia: dari pengenalan ucapan hingga diagnosa medis. Popularitas pendekatan ini begitu besar sehingga mereka mencoba menggunakannya sedapat mungkin. Beberapa upaya untuk menggantikan pendekatan klasik dengan jaringan saraf tidak begitu berhasil. Mari kita lihat pembelajaran mesin dari sudut pandang membuat penganalisis kode statis yang efektif untuk menemukan bug dan kerentanan potensial.

Tim PVS-Studio sering ditanya apakah kita ingin mulai menggunakan pembelajaran mesin untuk menemukan kesalahan dalam kode sumber program. Jawaban singkat: ya, tetapi sangat terbatas. Kami percaya bahwa dengan penggunaan pembelajaran mesin dalam masalah analisis kode, ada banyak jebakan. Di bagian kedua artikel ini kita akan membicarakannya. Mari kita mulai dengan ulasan tentang solusi dan ide baru.

Pendekatan baru


Saat ini, sudah ada banyak implementasi analisa statis berdasarkan atau menggunakan pembelajaran mesin, termasuk pembelajaran mendalam dan NLP untuk deteksi kesalahan. Tidak hanya penggemar, tetapi juga perusahaan besar, seperti Facebook, Amazon atau Mozilla, menarik perhatian pada potensi pembelajaran mesin ketika mencari kesalahan. Beberapa proyek tidak sepenuhnya merupakan analisa statis, tetapi hanya di antaranya menemukan beberapa kesalahan spesifik selama melakukan.

Menariknya, hampir semua diposisikan sebagai produk pengubah permainan yang, dengan bantuan kecerdasan buatan, akan mengubah proses pengembangan.


Perhatikan beberapa contoh terkenal:

  1. Deepcode
  2. Infer, Sapienz, SapFix
  3. Embold
  4. Sumber {d}
  5. Pintar-Komit, Asisten Komit
  6. CodeGuru

Deepcode


Deep Code adalah alat pencarian kerentanan dalam kode program yang ditulis dalam Java, JavaScript, TypeScript dan Python, di mana pembelajaran mesin hadir sebagai komponen. Menurut Boris Paskalev, lebih dari 250 ribu aturan sudah berfungsi. Alat ini dilatih berdasarkan perubahan yang dilakukan oleh pengembang terhadap kode sumber proyek terbuka (satu juta repositori). Perusahaan itu sendiri mengatakan proyek mereka adalah Grammarly untuk pengembang.



Intinya, penganalisa ini membandingkan solusi Anda dengan database proyeknya dan menawarkan kepada Anda perkiraan solusi terbaik dari pengalaman pengembang lain.

Pada Mei 2018, pengembang menulis bahwa dukungan untuk bahasa C ++ sedang dipersiapkan, namun, bahasa ini masih belum didukung. Meskipun ditunjukkan di situs itu sendiri bahwa bahasa baru dapat ditambahkan dalam hitungan minggu, karena fakta bahwa hanya satu langkah tergantung pada penguraian bahasa.





Sekelompok publikasi tentang metode yang menjadi dasar analisis juga diterbitkan di situs.

Infer


Facebook berusaha cukup luas untuk memperkenalkan pendekatan baru dalam produknya. Mereka tidak mengabaikan perhatian dan pembelajaran mesin mereka. Pada 2013, mereka membeli startup yang sedang mengembangkan analisa statis berbasis mesin. Dan pada 2015, kode sumber proyek menjadi terbuka .

Infer adalah penganalisa statis untuk proyek yang ditulis dalam Java, C, C ++, dan Objective-C, yang dikembangkan oleh Facebook. Menurut situs, itu juga digunakan di Amazon Web Services, Oculus, Uber, dan proyek populer lainnya.

Infer saat ini mampu mendeteksi kesalahan yang berkaitan dengan dereferencing pointer nol, kebocoran memori. Infer didasarkan pada logika Hoar, logika pemisahan dan penculikan, serta teori interpretasi abstrak. Menggunakan pendekatan ini memungkinkan penganalisa untuk memecah program menjadi blok-blok kecil (potongan) dan menganalisanya secara independen satu sama lain.

Anda dapat mencoba menggunakan Infer pada proyek Anda, namun, pengembang memperingatkan bahwa meskipun di Facebook memproyeksikan tanggapan yang bermanfaat mencakup 80% dari hasil, pada proyek lain sejumlah kecil kesalahan positif tidak dijamin. Beberapa kesalahan yang belum dapat ditemukan Infer, tetapi pengembang sedang berupaya memperkenalkan pemicu seperti itu:

  • keluar dari array;
  • pengecualian typecast;
  • kebocoran data yang tidak diverifikasi;
  • kondisi lomba pacuan.

Sapfix


SapFix adalah alat pengeditan otomatis. Ia menerima informasi dari Sapienz, alat otomatisasi pengujian, dan penganalisa statis Infer, dan berdasarkan perubahan dan pesan terbaru, Infer memilih salah satu dari beberapa strategi untuk memperbaiki kesalahan.



Dalam beberapa kasus, SapFix memutar kembali semua atau sebagian dari perubahan. Dalam kasus lain, ia mencoba menyelesaikan masalah dengan membuat tambalan dari set pola perbaikannya. Set ini dibentuk dari template pengeditan yang dikompilasi oleh programmer sendiri dari set pengeditan yang sudah dilakukan sekali. Jika template semacam itu tidak memperbaiki kesalahan, SapFix mencoba untuk menyesuaikan template dengan situasi, membuat modifikasi kecil di pohon sintaksis abstrak hingga solusi potensial ditemukan.

Tetapi satu solusi potensial tidak cukup, jadi SapFix mengumpulkan beberapa solusi yang dipilih berdasarkan tiga pertanyaan: apakah ada kesalahan kompilasi, apakah ada crash, apakah edit memperkenalkan crash baru. Setelah pengeditan sepenuhnya diuji, tambalan dikirim untuk ditinjau kepada programmer yang memutuskan yang mana dari pengeditan terbaik yang menyelesaikan masalah.

Embold


Embold adalah platform startup untuk analisis statis kode sumber program, yang sebelum diganti namanya disebut Gamma. Analisis statis dilakukan berdasarkan diagnostik kami sendiri, serta berdasarkan analisis bawaan seperti CppCheck, SpotBugs, SQL Check, dan lainnya.



Selain diagnosa itu sendiri, penekanannya adalah pada kemampuan untuk menampilkan infografis secara visual dengan memuat basis kode dan dengan mudah melihat kesalahan yang ditemukan, serta mencari refactoring. Selain itu, penganalisa ini memiliki serangkaian pola-anti yang memungkinkan Anda untuk mendeteksi masalah dalam struktur kode di tingkat kelas dan metode, dan berbagai metrik untuk menghitung kualitas sistem.



Salah satu keuntungan utama adalah solusi cerdas dan sistem saran revisi, yang, selain diagnostik biasa, memeriksa revisi berdasarkan informasi tentang perubahan sebelumnya.



Menggunakan NLP, Embold memecah kode menjadi beberapa bagian dan mencari interkoneksi dan dependensi antara fungsi dan metode di antara mereka, yang menghemat waktu refactoring.



Dengan demikian, Embold terutama menawarkan visualisasi yang nyaman dari hasil analisis kode sumber Anda oleh berbagai analis, serta diagnostiknya sendiri, beberapa di antaranya didasarkan pada pembelajaran mesin.

Sumber {d}


Sumber {d} adalah yang paling terbuka dalam hal bagaimana menerapkannya dari analisis yang kami periksa. Ini juga merupakan solusi open source . Di situs web mereka, Anda dapat (sebagai ganti alamat email Anda) mendapatkan buklet dengan deskripsi teknologi yang mereka gunakan. Selain itu, ini berisi tautan ke basis publikasi yang telah mereka kumpulkan terkait dengan penggunaan pembelajaran mesin untuk analisis kode, serta repositori dengan dataset untuk pelatihan kode. Produk itu sendiri adalah platform keseluruhan untuk menganalisis kode sumber dan produk perangkat lunak, dan berfokus, bukan pada pengembang, tetapi pada tautan manajer. Di antara kemampuannya ada fungsional untuk mengidentifikasi volume utang teknis, hambatan dalam proses pengembangan dan statistik global lainnya pada proyek.



Mereka mendasarkan pendekatan mereka pada analisis kode berbantuan mesin pada Hipotesis Alami, yang diformulasikan dalam artikel " On the Naturalness of Software ".

“Bahasa pemrograman, secara teori, kompleks fleksibel dan kuat, tetapi program yang orang-orang sebenarnya benar-benar tulis sebagian besar sederhana dan cukup berulang, dan karena itu mereka memiliki sifat statistik yang berguna dan dapat diprediksi yang dapat dinyatakan dalam statistik model bahasa dan digunakan untuk tugas pengembangan perangkat lunak. "

Berdasarkan hipotesis ini, semakin besar basis kode untuk melatih penganalisa, semakin banyak sifat statistik yang menonjol dan semakin akurat metrik yang dicapai melalui pelatihan.

Untuk menganalisis kode, sumber {d} menggunakan layanan Babelfish, yang dapat mengurai file kode dalam salah satu bahasa yang tersedia, dapatkan pohon sintaksis abstrak dan mengonversinya menjadi struktur sintaksis universal.



Namun, sumber {d} tidak mencari kesalahan dalam kode. Berdasarkan pohon, menggunakan pembelajaran mesin berdasarkan seluruh proyek, sumber {d} mengungkapkan bagaimana kode diformat, gaya pengkodean apa yang digunakan dalam proyek dan ketika melakukan, dan jika kode baru tidak cocok dengan gaya kode proyek, itu membuat perubahan yang sesuai.





Pelatihan dipandu oleh beberapa elemen dasar: spasi, tab, jeda baris, dll.



Anda dapat membaca lebih lanjut tentang ini dalam publikasi mereka: " STYLE-ANALYZER: memperbaiki ketidakkonsistenan gaya kode dengan algoritma yang tidak dapat ditafsirkan yang dapat ditafsirkan ".

Secara umum, sumber {d} adalah platform luas untuk mengumpulkan berbagai statistik tentang kode sumber dan proses pengembangan proyek, mulai dari menghitung efektivitas pengembang hingga mengidentifikasi biaya waktu untuk tinjauan kode.

Komitmen pintar


Clever-Commit adalah penganalisa yang dibuat oleh Mozilla bekerja sama dengan Ubisoft. Ini didasarkan pada studi Ubisoft's CLEVER (Menggabungkan Tingkat Pencegahan Bug dan Teknik Resolusi), dan Asisten Komit berbasis produknya, yang mengidentifikasi komitmen mencurigakan yang kemungkinan mengandung kesalahan. Karena fakta bahwa CLEVER didasarkan pada perbandingan kode, itu tidak hanya menunjukkan kode berbahaya, tetapi juga membuat saran tentang kemungkinan koreksi. Menurut uraian, dalam 60-70% kasus Clever-Commit menemukan area masalah dan dengan frekuensi yang sama menawarkan koreksi yang benar untuk mereka. Secara umum, ada sedikit informasi tentang proyek ini dan tentang kesalahan yang dapat ditemukan.

CodeGuru


Dan baru-baru ini, daftar analis yang menggunakan pembelajaran mesin telah diisi ulang dengan produk dari Amazon yang disebut CodeGuru. Layanan ini didasarkan pada pembelajaran mesin, yang memungkinkan Anda menemukan kesalahan dalam kode, serta mengidentifikasi bagian yang mahal di dalamnya. Sejauh ini, analisis hanya untuk kode Java, tetapi mereka menulis tentang dukungan untuk bahasa lain di masa depan. Meskipun diumumkan baru-baru ini, CEO AWS (Amazon Web Services) Andy Jassi mengatakan dia telah menggunakannya untuk waktu yang lama di Amazon sendiri.

Situs tersebut mengatakan bahwa pelatihan dilakukan pada basis kode Amazon sendiri, serta pada lebih dari 10.000 proyek sumber terbuka.

Bahkan, layanan ini dibagi menjadi dua bagian: Peninjau KodeGuru, dilatih dengan mencari aturan asosiatif dan mencari kesalahan dalam kode, dan CodeGuru Profiler, yang memantau kinerja aplikasi.



Secara umum, tidak banyak informasi yang telah dipublikasikan tentang proyek ini. Situs tersebut mengatakan bahwa untuk mempelajari cara menangkap penyimpangan dari "praktik terbaik", Reviewer menganalisis basis kode Amazon dan mencari permintaan tarik yang berisi panggilan API AWS di dalamnya. Kemudian dia melihat perubahan yang dilakukan dan membandingkannya dengan data dari dokumentasi, yang dianalisis secara paralel. Hasilnya adalah model "praktik terbaik."

Juga dikatakan bahwa rekomendasi untuk kode kustom meningkat setelah menerima umpan balik tentang rekomendasi tersebut.

Daftar kesalahan yang direspon oleh Reviewer agak kabur, karena tidak ada dokumentasi khusus untuk kesalahan yang telah dipublikasikan:
  • Praktik Terbaik AWS
  • Konkurensi
  • Kebocoran sumber daya
  • Kebocoran Informasi Rahasia
  • "Praktik Terbaik" yang umum untuk Pengkodean

Skeptisisme kami


Sekarang mari kita lihat masalah menemukan kesalahan melalui mata tim kami, yang telah mengembangkan analisa statis selama bertahun-tahun. Kami melihat sejumlah masalah tingkat tinggi dari penerapan pelatihan, yang ingin kami bicarakan. Tetapi pada awalnya kami secara kasar membagi semua pendekatan ML menjadi dua jenis:

  1. Melatih penganalisa statis secara manual untuk mencari berbagai masalah menggunakan contoh kode sintetik dan nyata;
  2. Latih algoritma pada sejumlah besar kode sumber terbuka (GitHub) dan ubah riwayat, setelah itu penganalisa sendiri akan mulai mendeteksi kesalahan dan bahkan menyarankan perbaikan.

Kami akan berbicara tentang setiap arah secara terpisah, karena mereka akan memiliki berbagai kekurangan. Setelah itu, saya pikir, akan menjadi jelas bagi pembaca mengapa kita tidak menyangkal kemungkinan pembelajaran mesin, tetapi juga tidak berbagi antusiasme.

Catatan Kami melihat dari perspektif mengembangkan alat analisa statik universal untuk keperluan umum. Kami fokus pada pengembangan alat analisis yang berfokus bukan pada basis kode tertentu, tetapi yang dapat digunakan tim mana pun dalam proyek apa pun.

Pelatihan penganalisa statis manual


Misalkan kita ingin menggunakan ML sehingga penganalisa mulai mencari anomali dari bentuk berikut dalam kode:

if (A == A) 

Sungguh aneh membandingkan suatu variabel dengan dirinya sendiri. Kami dapat menulis banyak contoh kode yang benar dan salah dan melatih penganalisa untuk mencari kesalahan tersebut. Selain itu, dimungkinkan untuk menambahkan contoh nyata dari kesalahan yang telah ditemukan ke tes. Pertanyaannya, tentu saja, adalah dari mana mendapatkan contoh-contoh ini. Tetapi kami akan mempertimbangkan bahwa itu mungkin. Misalnya, kami telah mengumpulkan sejumlah contoh kesalahan seperti itu: V501 , V3001 , V6001 .

Jadi, apakah mungkin untuk mencari cacat dalam kode menggunakan algoritma pembelajaran mesin? Kamu bisa. Tetapi tidak jelas mengapa melakukan ini!

Lihat, untuk melatih penganalisa kita perlu menghabiskan banyak upaya menyiapkan contoh untuk pelatihan. Atau tandai kode aplikasi nyata, yang menunjukkan di mana harus bersumpah dan di mana tidak. Bagaimanapun, banyak pekerjaan yang harus dilakukan, karena harus ada ribuan contoh untuk pelatihan. Atau puluhan ribu.

Bagaimanapun, kami ingin mencari tidak hanya untuk kasus (A == A), tetapi juga untuk:

  • if (X&& A == A)
  • jika (A + 1 == A + 1)
  • if (A [i] == A [i])
  • if ((A) == (A))
  • dan sebagainya.



Sekarang mari kita lihat bagaimana diagnosis sederhana seperti itu akan diterapkan di PVS-Studio:

 void RulePrototype_V501(VivaWalker &walker, const Ptree *left, const Ptree *right, const Ptree *operation) { if (SafeEq(operation, "==") && SafeEqual(left, right)) { walker.AddError(" , !", left, 501, Level_1, "CWE-571"); } } 

Dan itu dia. Tidak diperlukan basis pelatihan sampel!

Di masa depan, diagnostik harus diajarkan untuk memperhitungkan sejumlah pengecualian dan memahami bahwa Anda perlu bersumpah (A [0] == A [1-1]). Namun, semua ini sangat mudah diprogram. Tetapi hanya dengan dasar contoh untuk pelatihan, semuanya akan menjadi buruk.

Perhatikan bahwa dalam kedua kasus sistem pengujian, penulisan dokumentasi, dan sebagainya masih akan diperlukan. Namun, upaya untuk menciptakan diagnostik baru jelas berada di sisi pendekatan klasik, di mana aturannya hanya berupa kode yang sulit.

Mari kita lihat beberapa aturan lain. Misalnya, bahwa hasil dari beberapa fungsi harus digunakan. Tidak masuk akal untuk memanggil mereka tanpa menggunakan hasilnya. Berikut adalah beberapa fitur ini:
  • malloc
  • memcmp
  • string :: kosong

Secara umum, inilah yang dilakukan diagnostik V530 di PVS-Studio.

Jadi, kami ingin mencari panggilan ke fungsi seperti itu di mana hasil pekerjaan mereka tidak digunakan. Untuk melakukan ini, Anda dapat menghasilkan banyak tes. Dan kami pikir semuanya akan bekerja dengan baik. Tetapi sekali lagi, tidak jelas mengapa ini perlu.

Implementasi diagnostik V530 dengan semua pengecualian dalam alat analisa PVS-Studio adalah 258 baris kode, di mana 64 baris adalah komentar. Plus ada tabel dengan penjelasan fungsi, di mana dicatat bahwa hasilnya harus digunakan. Mengisi ulang tabel ini jauh lebih mudah daripada membuat contoh sintetis.

Situasi akan lebih buruk dengan diagnostik yang menggunakan analisis aliran data. Misalnya, penganalisa PVS-Studio dapat melacak nilai pointer, yang memungkinkan menemukan kebocoran memori seperti itu:

 uint32_t* BnNew() { uint32_t* result = new uint32_t[kBigIntSize]; memset(result, 0, kBigIntSize * sizeof(uint32_t)); return result; } std::string AndroidRSAPublicKey(crypto::RSAPrivateKey* key) { .... uint32_t* n = BnNew(); .... RSAPublicKey pkey; pkey.len = kRSANumWords; pkey.exponent = 65537; // Fixed public exponent pkey.n0inv = 0 - ModInverse(n0, 0x100000000LL); if (pkey.n0inv == 0) return kDummyRSAPublicKey; // <= .... } 

Contoh diambil dari artikel " Chromium: kebocoran memori ." Jika kondisi (pkey.n0inv == 0) terpenuhi , maka fungsi keluar tanpa membebaskan buffer, penunjuk yang disimpan dalam variabel n .

Dari sudut pandang PVS-Studio, tidak ada yang rumit. Penganalisis mempelajari fungsi BnNew dan ingat bahwa itu mengembalikan pointer ke blok memori yang dialokasikan. Dalam fungsi lain, ia memperhatikan bahwa suatu situasi dimungkinkan di mana buffer tidak dibebaskan, dan pointer ke sana hilang ketika fungsi keluar.

Algoritma pelacakan nilai umum berfungsi. Tidak masalah bagaimana kode ditulis. Tidak masalah apa pun yang ada dalam fungsi yang tidak terkait dengan bekerja dengan pointer. Algoritma bersifat universal dan diagnostik V773 menemukan banyak kesalahan dalam berbagai proyek. Lihat betapa berbedanya fragmen kode di mana kesalahan terdeteksi!

Kami bukan ahli dalam pembelajaran mesin, tetapi tampaknya akan ada masalah besar. Ada banyak cara Anda dapat menulis kode dengan kebocoran memori. Bahkan jika mesin dilatih untuk melacak nilai variabel, perlu melatihnya untuk memahami bahwa ada panggilan fungsi.

Ada kecurigaan bahwa begitu banyak contoh akan diperlukan untuk pelatihan sehingga tugas menjadi menakutkan. Kami tidak mengatakan bahwa itu tidak dapat direalisasikan. Kami ragu bahwa biaya untuk membuat analisa akan terbayar.

Analogi. Sebuah analogi muncul dalam pikiran dengan kalkulator, di mana alih-alih diagnostik, perlu untuk memprogram operasi aritmatika. Kami yakin Anda dapat mengajarkan kalkulator berbasis ML untuk menambahkan angka dengan baik dengan memperkenalkan basis pengetahuan tentang hasil operasi 1 + 1 = 2, 1 + 2 = 3, 2 + 1 = 3, 100 + 200 = 300, dan seterusnya. Seperti yang Anda ketahui, kelayakan pengembangan kalkulator semacam itu adalah pertanyaan besar (jika hibah tidak dialokasikan untuk itu :). Kalkulator yang lebih sederhana, lebih cepat, lebih akurat, dan andal dapat ditulis menggunakan operasi "+" biasa dalam kode.

Kesimpulan Metode ini akan bekerja. Tetapi untuk menggunakannya, menurut pendapat kami, tidak masuk akal secara praktis. Pengembangan akan lebih memakan waktu, dan hasilnya kurang dapat diandalkan dan akurat, terutama jika menyangkut implementasi diagnostik yang kompleks berdasarkan analisis aliran data.

Belajar dari banyak sumber terbuka


Ya, kami sudah menemukan contoh sintetik manual, tetapi ada GitHub. Anda dapat melacak riwayat commit dan mendapatkan pola perubahan / koreksi kode. Kemudian Anda dapat menunjukkan tidak hanya bagian dari kode yang mencurigakan, tetapi bahkan mungkin menyarankan cara untuk memperbaikinya.

Jika Anda berhenti pada tingkat detail ini, maka semuanya terlihat bagus. Iblis, seperti biasa, ada dalam perinciannya. Mari kita bicarakan detail ini.

Nuansa pertama. Sumber data.

Suntingan di GitHub cukup kacau dan bervariasi. Orang-orang sering terlalu malas untuk membuat komitmen atom dan membuat beberapa perubahan pada kode sekaligus. Anda sendiri tahu bagaimana hal itu terjadi: mereka memperbaiki kesalahan, dan pada saat yang sama sedikit refactored ("Dan di sini saya akan menambahkan pemrosesan kasus seperti itu pada saat yang sama ..."). Meski begitu, mungkin tidak jelas bagi seseorang apakah perubahan ini terkait satu sama lain atau tidak.

Masalahnya adalah bagaimana membedakan kesalahan aktual dari menambahkan fungsionalitas baru atau sesuatu yang lain. Anda tentu saja dapat menanam 1.000 orang secara manual untuk menandai komitmen. Orang harus menunjukkan bahwa mereka memperbaiki kesalahan di sini, refactoring di sini, fungsionalitas baru di sini, persyaratan berubah di sini, dan sebagainya.

Apakah markup ini mungkin? Mungkin Namun perhatikan seberapa cepat perubahan itu terjadi. Alih-alih "mempelajari algoritme itu sendiri berdasarkan GitHub", kami sudah membahas cara membuat teka-teki ratusan orang untuk waktu yang lama. Biaya tenaga kerja dan biaya pembuatan alat meningkat tajam.

Anda dapat mencoba mengidentifikasi secara otomatis di mana kesalahan itu diperbaiki. Untuk melakukan ini, Anda harus menganalisis komentar tentang komit, memperhatikan suntingan lokal kecil, yang, kemungkinan besar, adalah revisi kesalahan. Sulit untuk mengatakan seberapa baik Anda dapat secara otomatis mencari perbaikan bug. Bagaimanapun, ini adalah tugas besar yang membutuhkan penelitian dan pemrograman terpisah.

Jadi, kami belum mencapai pelatihan, tetapi sudah ada nuansa :).

Nuansa kedua. Tertunda dalam pengembangan.

Analisis yang akan dilatih berdasarkan database seperti GitHub akan selalu mengalami sindrom seperti "keterbelakangan mental." Ini karena bahasa pemrograman berubah seiring waktu.

C # 8.0 memperkenalkan jenis Referensi Nullable untuk membantu menangani Pengecualian Referensi Null (NRE). JDK 12 memperkenalkan pernyataan pergantian baru ( JEP 325 ). Dalam C ++ 17, menjadi mungkin untuk mengeksekusi konstruksi kondisional pada tahap kompilasi ( constexpr if ). Dan sebagainya.

Bahasa pemrograman berkembang. Selain itu, seperti C ++ sangat cepat dan aktif. Desain baru muncul di dalamnya, fungsi standar baru ditambahkan, dan sebagainya. Seiring dengan fitur baru, pola kesalahan baru juga muncul yang kami juga ingin mengidentifikasi menggunakan analisis kode statis.

Dan di sini metode pengajaran yang dipertimbangkan memiliki masalah: pola kesalahan mungkin sudah diketahui, ada keinginan untuk mengidentifikasi itu, tetapi tidak ada yang bisa dipelajari.

Mari kita lihat masalah ini dengan contoh spesifik. Berbasis rentang untuk loop muncul di C ++ 11. Dan Anda dapat menulis kode berikut, mengulangi semua elemen dalam wadah:

 std::vector<int> numbers; .... for (int num : numbers) foo(num); 

Siklus baru membawa serta pola kesalahan baru. Jika wadah diubah di dalam loop, ini akan menyebabkan pembatalan iterator "bayangan".

Pertimbangkan kode yang salah berikut ini:

 for (int num : numbers) { numbers.push_back(num * 2); } 

Kompiler akan mengubahnya menjadi seperti ini:

 for (auto __begin = begin(numbers), __end = end(numbers); __begin != __end; ++__begin) { int num = *__begin; numbers.push_back(num * 2); } 

Selama operasi push_back , pembatalan __begin dan __end iterators dapat terjadi jika alokasi memori di dalam vektor terjadi. Hasilnya adalah perilaku program yang tidak ditentukan.

Jadi, pola kesalahan telah lama dikenal dan dijelaskan dalam literatur. Penganalisa PVS-Studio mendiagnosisnya menggunakan diagnostik V789 dan telah menemukan kesalahan nyata dalam proyek terbuka.

Seberapa cepat akan ada cukup kode baru di GitHub untuk memperhatikan pola ini? Pertanyaan bagus ... Anda harus mengerti bahwa jika range-based for loop muncul, ini tidak berarti bahwa semua programmer segera mulai menggunakannya secara masif. Mungkin perlu bertahun-tahun sebelum banyak kode muncul menggunakan loop baru. Selain itu, banyak kesalahan harus dilakukan, dan kemudian mereka harus diperbaiki sehingga algoritme dapat melihat pola dalam perubahan.

Berapa tahun harus berlalu? Lima? Sepuluh?

Sepuluh terlalu banyak, dan apakah kita pesimis? Tidak semuanya. Delapan tahun telah berlalu pada saat artikel ini ditulis, karena range-based for loop muncul di C ++ 11. Namun sejauh ini, hanya tiga kasus kesalahan semacam itu yang telah dituliskan dalam basis data kami. Tiga kesalahan itu tidak banyak dan tidak sedikit. Tidak ada kesimpulan yang harus diambil dari jumlah mereka. Hal utama adalah Anda dapat mengonfirmasi bahwa pola kesalahan seperti itu nyata dan masuk akal untuk mendeteksinya.

Sekarang bandingkan jumlah ini, misalnya, dengan pola kesalahan ini: pointer direferensikan sebelum verifikasi . Secara total, ketika memeriksa proyek sumber terbuka, kami telah mengidentifikasi 1716 kasus seperti itu.

Mungkin Anda seharusnya tidak mencari kesalahan loop berbasis sama sekali? Tidak. Hanya programmer yang inersia, dan operator ini sangat lambat mendapatkan popularitas. Secara bertahap, akan ada banyak kode dengan partisipasinya, dan karenanya, juga akan ada lebih banyak kesalahan.

Kemungkinan besar, ini akan terjadi hanya setelah 10-15 tahun dari saat C ++ 11 muncul. Dan sekarang menjadi pertanyaan filosofis. Sudah mengetahui pola kesalahan, kita hanya akan menunggu selama bertahun-tahun sampai banyak kesalahan menumpuk di proyek terbuka?

Jika jawabannya "ya", maka mungkin untuk secara wajar mendiagnosis semua analisis berdasarkan ML, diagnosis "keterbelakangan mental".

Jika jawabannya tidak, lalu apa yang harus saya lakukan? Tidak ada contoh. Untuk menulisnya secara manual? Tapi kemudian kita kembali ke bab sebelumnya, di mana kami menganggap menulis seseorang banyak contoh untuk belajar.

Ini bisa dilakukan, tetapi sekali lagi muncul pertanyaan tentang kemanfaatan. Penerapan diagnostik V789 dengan semua pengecualian dalam penganalisa PVS-Studio hanya 118 baris kode, di mana 13 baris adalah komentar. Yaitu Ini adalah diagnosis yang sangat sederhana yang dapat dengan mudah diambil dan diprogram dengan cara klasik.

Situasi serupa akan terjadi dengan inovasi lain yang muncul dalam bahasa lain. Seperti yang mereka katakan, ada sesuatu untuk dipikirkan.

Nuansa ketiga. Dokumentasi

Komponen penting dari penganalisa statis adalah dokumentasi yang menjelaskan setiap diagnostik. Tanpa itu, menggunakan alat analisa akan sangat sulit atau bahkan tidak mungkin. Dalam dokumentasi untuk PVS-Studio, kami memiliki deskripsi masing-masing diagnostik, yang memberikan contoh kode yang salah dan cara memperbaikinya. Ada juga tautan ke CWE di mana Anda dapat membaca deskripsi alternatif masalah. Dan sama saja, terkadang ada sesuatu yang tidak bisa dipahami oleh pengguna, dan mereka menanyakan pertanyaan klarifikasi kepada kami.

Dalam kasus analisis statis, yang didasarkan pada algoritma pembelajaran mesin, masalah dokumentasi entah bagaimana ditutup. Diasumsikan bahwa penganalisa hanya menunjukkan tempat yang tampaknya mencurigakan baginya dan, mungkin, bahkan menyarankan cara memperbaikinya. Keputusan untuk melakukan perubahan atau tidak tetap ada pada orang tersebut. Dan di sini ... ahem ... Tidak mudah untuk membuat keputusan, tidak bisa membaca, atas dasar di mana penganalisa kelihatannya curiga terhadap satu atau tempat lain dalam kode.

Tentu saja, dalam beberapa kasus semuanya akan jelas. Misalkan penganalisa menunjuk ke kode ini:

 char *p = (char *)malloc(strlen(src + 1)); strcpy(p, src); 

Dan akan menawarkan untuk menggantinya dengan:

 char *p = (char *)malloc(strlen(src) + 1); strcpy(p, src); 

Segera jelas bahwa programmer menutup dan menambahkan 1 ke tempat yang salah. Akibatnya, lebih sedikit memori yang akan dialokasikan.

Di sini, tanpa dokumentasi, semuanya jelas. Namun, ini tidak selalu menjadi masalah.

Bayangkan bahwa penganalisa secara diam-diam menunjuk ke kode ini:

 char check(const uint8 *hash_stage2) { .... return memcmp(hash_stage2, hash_stage2_reassured, SHA1_HASH_SIZE); } 

Dan menyarankan untuk mengubah jenis nilai pengembalian dari char ke int:

 int check(const uint8 *hash_stage2) { .... return memcmp(hash_stage2, hash_stage2_reassured, SHA1_HASH_SIZE); } 

Tidak ada dokumentasi untuk peringatan itu. Dan tampaknya, teks peringatan itu sendiri, seperti yang kita pahami, tidak akan menjadi baik, jika kita berbicara tentang penganalisa yang sepenuhnya independen.

Apa yang harus dilakukan Apa bedanya? Haruskah saya melakukan penggantian seperti itu?

Pada prinsipnya, di sini Anda dapat mengambil risiko dan setuju untuk memperbaiki kode. Meskipun menerima pengeditan tanpa memahaminya, ini adalah praktik biasa ... :) Anda dapat melihat deskripsi fungsi memcmp dan membaca bahwa fungsi tersebut benar-benar mengembalikan nilai dari tipe int : 0, lebih besar dari nol dan kurang dari nol. Tetapi semua sama, mungkin tidak jelas mengapa harus melakukan perubahan jika kode sudah bekerja dengan sukses.

Sekarang, jika Anda tidak tahu apa gunanya hasil edit tersebut, lihat deskripsi diagnostik V642 . Segera menjadi jelas bahwa ini adalah kesalahan nyata. Selain itu, dapat menyebabkan kerentanan.

Mungkin contohnya tampak tidak meyakinkan. Bagaimanapun, penganalisa mengusulkan kode yang mungkin lebih baik. Ok Mari kita lihat contoh pseudocode lain, kali ini, untuk perubahan, di Jawa.

 ObjectOutputStream out = new ObjectOutputStream(....); SerializedObject obj = new SerializedObject(); obj.state = 100; out.writeObject(obj); obj.state = 200; out.writeObject(obj); out.close(); 

Ada beberapa jenis objek. Ini adalah serial. Kemudian keadaan objek berubah, dan serialisasi lagi. Segalanya tampak baik-baik saja. Sekarang bayangkan penganalisa tiba-tiba tidak menyukai kode ini, dan ia menyarankan untuk menggantinya dengan:

 ObjectOutputStream out = new ObjectOutputStream(....); SerializedObject obj = new SerializedObject(); obj.state = 100; out.writeObject(obj); obj = new SerializedObject(); //    obj.state = 200; out.writeObject(obj); out.close(); 

Alih-alih mengubah objek dan merekam ulang, objek baru dibuat dan sudah serial.

Tidak ada deskripsi masalah. Tidak ada dokumentasi. Kode menjadi lebih panjang. Untuk beberapa alasan, penciptaan objek baru telah ditambahkan. Apakah Anda siap melakukan pengeditan seperti itu dalam kode Anda?

Anda akan mengatakan bahwa itu tidak jelas. Memang tidak jelas. Dan itu akan menjadi tidak bisa dipahami sepanjang waktu. Bekerja dengan penganalisa "sunyi" seperti itu akan menjadi studi tanpa akhir dalam upaya untuk memahami mengapa penganalisa tidak menyukai sesuatu.

Jika ada dokumentasi, maka semuanya menjadi transparan. Kelas java.io.ObjectOuputStream , yang digunakan untuk serialisasi, membuat cache objek yang dapat ditulis. Ini berarti bahwa objek yang sama tidak akan di-serialkan dua kali. Setelah kelas membuat serial objek, dan kedua kalinya ia hanya menulis tautan ke objek pertama yang sama ke dalam aliran. Baca selengkapnya: V6076 - Serialisasi serialisasi akan menggunakan keadaan objek yang di-cache dari serialisasi pertama.

Kami berharap kami dapat menjelaskan pentingnya memiliki dokumentasi. Dan sekarang pertanyaannya. Bagaimana dokumentasi untuk alat analisis berdasarkan ML muncul?

Ketika penganalisa kode klasik dikembangkan, maka semuanya sederhana dan jelas. Ada pola kesalahan tertentu. Kami menjelaskannya dalam dokumentasi dan menerapkan diagnostik.

Dalam kasus ML, yang terjadi adalah sebaliknya. Ya, penganalisa dapat melihat anomali dalam kode dan menunjukkannya. Tapi dia tidak tahu apa-apa tentang esensi cacat. Dia tidak mengerti dan tidak akan mengatakan mengapa kode tidak dapat ditulis seperti itu. Ini adalah abstraksi tingkat tinggi. Kemudian penganalisa juga harus belajar membaca dan memahami dokumentasi untuk fungsinya.

Seperti yang saya katakan, karena topik dokumentasi tercakup dalam artikel tentang pembelajaran mesin, kami tidak siap untuk berbicara lebih lanjut. Hanya nuansa besar lain yang kami bawa untuk ditinjau.

Catatan Dapat dikatakan bahwa dokumentasi itu opsional. Penganalisa dapat mengarah ke banyak contoh perbaikan pada GitHub dan seseorang, melihat komitmen dan komentar pada mereka, akan mencari tahu apa apa. Ya itu. Namun idenya tidak terlihat menarik. Alih-alih asisten, penganalisa bertindak sebagai alat yang akan lebih membingungkan programmer.

Nuansa keempat. Bahasa yang sangat terspesialisasi.

Pendekatan yang diuraikan tidak berlaku untuk bahasa yang sangat terspesialisasi yang analisis statisnya juga bisa sangat berguna. Alasannya adalah bahwa GitHub dan sumber lainnya tidak memiliki basis kode sumber yang cukup besar untuk memberikan pelatihan yang efektif.

Pertimbangkan ini dengan contoh khusus. Untuk memulai, buka GitHub dan cari repositori untuk bahasa Java yang populer.

Hasil: bahasa: "Java": 3.128.884 hasil repositori yang tersedia

Sekarang mari kita ambil bahasa khusus "1C Enterprise" yang digunakan dalam aplikasi akuntansi yang dikeluarkan oleh perusahaan Rusia 1C .

Hasil: bahasa: “1C Enterprise”: 551 hasil repositori yang tersedia

Mungkin analisa untuk bahasa ini tidak diperlukan? Dibutuhkan. Ada kebutuhan praktis untuk analisis program semacam itu, dan analisis yang sesuai sudah ada.Misalnya, ada Plugin SonarQube 1C (BSL) yang diproduksi oleh Silver Bullet .

Saya pikir tidak diperlukan penjelasan khusus mengapa pendekatan pembelajaran mesin akan sulit untuk bahasa khusus.

Nuansa kelima. C, C ++, #include .

Artikel yang dikhususkan untuk penelitian di bidang analisis statis kode berbasis ML condong ke bahasa seperti Java, JavaScript, Python. Ini dijelaskan oleh popularitas mereka yang ekstrem. Tapi bahasa C dan C ++ entah bagaimana memotong, meskipun mereka tentu tidak bisa disebut tidak populer.

Kami memiliki hipotesis bahwa ini bukan masalah popularitas / janji, tetapi ada masalah dengan bahasa C dan C ++. Dan sekarang kita akan "menarik" satu masalah yang tidak nyaman untuk ditinjau.

File c / cpp abstrak bisa sangat sulit untuk dikompilasi. Setidaknya, itu tidak akan berfungsi untuk mengunduh proyek dari GitHub, pilih beberapa file cpp dan cukup kompilasi. Sekarang kami akan menjelaskan bagaimana semua ini berhubungan dengan ML.

Jadi, kami ingin melatih penganalisa. Kami mengunduh proyek dari GitHub. Kami tahu tambalan dan menganggap itu memperbaiki kesalahan. Kami ingin hasil edit ini menjadi salah satu contoh untuk pelatihan. Dengan kata lain, kami memiliki file .cpp sebelum dan sesudah diedit.

Di sinilah masalahnya dimulai. Tidak cukup hanya mempelajari koreksi. Anda harus memiliki konteks yang lengkap. Anda perlu mengetahui deskripsi kelas yang digunakan, Anda perlu tahu prototipe fungsi yang digunakan, Anda perlu tahu bagaimana makro dibuka, dan sebagainya. Dan untuk ini, Anda perlu melakukan preprocessing penuhfile.

Pertimbangkan sebuah contoh. Pada awalnya, kode itu terlihat seperti ini:

 bool Class::IsMagicWord() { return m_name == "ML"; } 

Itu diperbaiki seperti ini:

 bool Class::IsMagicWord() { return strcmp(m_name, "ML") == 0; } 

Haruskah saya mulai belajar berdasarkan kasus ini, di masa depan saya sarankan mengganti (x == "y") dengan strcmp (x, "y")?

Pertanyaan ini tidak dapat dijawab tanpa mengetahui bagaimana anggota m_name dideklarasikan di kelas. Mungkin ada, misalnya, opsi seperti:

 class Class { .... char *m_name; }; class Class { .... std::string m_name; }; 

Pengeditan akan dilakukan jika kita berbicara tentang indeks biasa. Jika Anda tidak mempertimbangkan jenis variabel, Anda dapat belajar memberikan peringatan berbahaya bersama dengan peringatan yang berguna (untuk kasus std :: string ).

Deklarasi kelas biasanya ditemukan dalam file header .h. Jadi kami sampai pada kebutuhan untuk melakukan preprocessing untuk mendapatkan semua informasi yang diperlukan. Ini sangat, sangat penting untuk C dan C ++.

Jika seseorang mengatakan bahwa Anda dapat melakukannya tanpa preprocessing, maka ia adalah penipu atau tidak cukup akrab dengan bahasa C atau C ++.

Untuk mengumpulkan semua informasi yang diperlukan, Anda harus melakukan preproses dengan benar. Untuk melakukan ini, Anda perlu tahu di mana dan file header mana yang terletak, makro mana yang ditetapkan selama proses pembuatan. Untuk melakukan ini, Anda perlu tahu bagaimana file cpp tertentu dibangun.

Ini masalahnya. Anda tidak bisa hanya mengambil dan mengkompilasi file (atau lebih tepatnya, tentukan kunci untuk kompiler sehingga menghasilkan file preprocessed). Anda perlu mencari tahu bagaimana file ini dikompilasi. Informasi ini ada dalam skrip build, tetapi inilah cara mengeluarkannya, dalam kasus umum, tugasnya sangat sulit.



Apalagi banyak proyek di GitHub yang berantakan. Jika Anda mengambil proyek abstrak dari sana, Anda sering harus mengotak-atiknya untuk mengkompilasinya. Beberapa perpustakaan tidak ada dan perlu ditemukan dan diunduh secara manual. Ia menggunakan semacam sistem perakitan samopisny, yang harus ditangani. Mungkin apa saja. Terkadang proyek yang diunduh, pada prinsipnya, menolak untuk berkumpul dan perlu “dimodifikasi dengan file”. Tidak perlu hanya mengambil proyek dan secara otomatis mendapatkan representasi preprocessed (.i) mereka untuk file .cpp. Ini bahkan bisa sulit dilakukan secara manual.

Bisa dibilang, masalah dengan proyek yang tidak dirakit bisa dimengerti, tapi tidak menakutkan. Mari kita bekerja hanya dengan proyek-proyek yang dapat dirakit. Lagi pula, tugas preprocessing file tertentu tetap. Dan kami tidak akan mengatakan apa-apa tentang kasus-kasus itu ketika beberapa kompiler khusus akan digunakan, misalnya, untuk sistem embedded.

Secara umum, masalah yang dijelaskan tidak dapat diatasi. Namun, semua ini sangat sulit dan memakan waktu. Dalam kasus C dan C ++, kode sumber di GitHub saja tidak menghasilkan apa-apa. Sejumlah besar pekerjaan perlu dilakukan untuk mempelajari cara memulai kompiler secara otomatis.

Catatan. Jika pembaca masih tidak memahami masalah, maka kami sarankan agar ia melakukan percobaan berikut. Ambil sepuluh proyek C ++ berukuran sedang dari GitHub dan cobalah untuk mengompilasinya, lalu dapatkan versi pra-prosesnya untuk file .cpp. Setelah itu, pertanyaan tentang kompleksitas tugas ini akan hilang :).

Bahasa lain mungkin memiliki masalah yang sama, tetapi dalam C dan C ++ mereka terutama diucapkan.

Nuansa keenam. Harga menghilangkan positif palsu.

Analisis statis cenderung menghasilkan positif palsu dan harus terus memperbaiki diagnostik untuk mengurangi jumlah mereka.

Mari kita kembali ke diagnostik V789 yang telah dibahas sebelumnyamengungkapkan perubahan kontainer di dalam Rentang berbasis untuk loop. Misalkan kita tidak cukup berhati-hati dalam perkembangannya, dan klien melaporkan positif palsu. Dia menulis bahwa penganalisa tidak memperhitungkan skenario ketika, setelah memodifikasi wadah, siklus berakhir, dan karena itu tidak ada masalah. Dan dia memberikan contoh kode berikut, di mana penganalisa menghasilkan false positive:

 std::vector<int> numbers; .... for (int num : numbers) { if (num < 5) { numbers.push_back(0); break; // , , return } } 

Ya, ini cacat. Dalam analisa klasik, eliminasi ini sangat cepat dan murah. Dalam PVS-Studio, implementasi pengecualian ini terdiri dari 26 baris kode.

Cacat ini dapat dihilangkan dalam kasus ketika analisa dibangun pada algoritma pembelajaran. Tentunya, Anda dapat menyelesaikannya dengan membuat puluhan atau ratusan contoh kode yang harus dianggap benar.

Di sini sekali lagi, pertanyaannya bukan kelayakan, tetapi kepraktisan. Ada kecurigaan bahwa perlawanan terhadap kesalahan positif spesifik yang mengganggu pelanggan jauh lebih mahal dalam kasus ML. Yaitudukungan pelanggan dalam hal menghilangkan kesalahan positif akan membutuhkan lebih banyak uang.

Nuansa ketujuh. Fitur yang jarang digunakan dan ekor yang panjang.

Sebelumnya, masalah bahasa yang sangat khusus dipertimbangkan, yang mungkin tidak ada kode sumber yang cukup untuk belajar. Masalah serupa dengan fungsi yang jarang digunakan (sistem, WinAPI, dari perpustakaan populer, dll.).

Jika kita berbicara tentang fungsi-fungsi bahasa C seperti strcmp , maka ada dasar untuk belajar. GitHub, hasil kode yang tersedia:

  • strcmp - 40.462.158
  • stricmp - 1.256.053

Ya, ada banyak contoh penggunaan. Mungkin penganalisa akan belajar memperhatikan, misalnya, pola berikut:
  • Sungguh aneh jika sebuah string dibandingkan dengan sendirinya. Ini diperbaiki.
  • Aneh jika salah satu petunjuknya adalah NULL. Ini diperbaiki.
  • Sungguh aneh bahwa hasil dari fungsi ini tidak digunakan. Ini diperbaiki.
  • Dan sebagainya.

Apakah semuanya baik-baik saja? Tidak. Di sini kita dihadapkan dengan "ekor panjang". Secara singkat, inti dari "ekor panjang" adalah sebagai berikut. Tidak praktis menjual di toko buku hanya Top50 buku paling populer dan saat ini dibaca. Ya, setiap buku seperti itu akan diperoleh, katakanlah, 100 kali lebih sering daripada buku yang tidak ada dalam daftar ini. Namun, sebagian besar pendapatan akan berasal dari buku-buku lain, yang, seperti yang mereka katakan, temukan pembacanya. Misalnya, toko online Amazon.com menghasilkan lebih dari setengah laba dari apa yang melebihi 130 ribu "item paling populer."

Ada beberapa fitur yang populer dan beberapa di antaranya. Ada yang tidak populer, tetapi ada banyak dari mereka. Misalnya, masih ada varietas fungsi perbandingan string seperti itu:

  • g_ascii_strncasecmp - 35.695
  • lstrcmpiA - 27.512
  • _wcsicmp_l - 5,737
  • _strnicmp_l - 5,848
  • _mbscmp_l - 2,458
  • dll.

Seperti yang Anda lihat, mereka lebih jarang digunakan, tetapi Anda masih bisa membuat kesalahan yang sama saat menggunakannya. Ada terlalu sedikit contoh untuk mengidentifikasi pola. Namun, seseorang tidak dapat mengabaikan fungsi-fungsi ini. Secara terpisah, mereka jarang digunakan, tetapi dalam jumlah penggunaannya, banyak kode telah ditulis yang berguna untuk memeriksa. Di sinilah "ekor panjang" memanifestasikan dirinya.

Di PVS-Studio, kami membubuhi keterangan fungsi secara manual. Misalnya, untuk C dan C ++, sekitar 7200 fungsi saat ini dilabeli. Penandaan tunduk pada:

  • Winapi
  • C library standar
  • perpustakaan templat standar (STL),
  • glibc (Perpustakaan GNU C)
  • Qt
  • Mfc
  • zlib
  • libpng
  • Openssl
  • dll.

Di satu sisi, ini sepertinya jalan buntu. Tidak mungkin untuk membubuhi keterangan segalanya. Di sisi lain, ini berfungsi.

Sekarang pertanyaannya. Apa manfaat ML? Keuntungan yang signifikan tidak terlihat, tetapi kesulitan terlihat.

Kita dapat mengatakan bahwa algoritma yang dibangun di atas ML sendiri akan mengungkapkan pola dengan fungsi yang sering digunakan dan mereka tidak harus ditandai. Ya itu benar. Namun, tidak ada masalah secara independen menandai fungsi populer seperti strcmp atau malloc .

Dengan ekor panjang, masalah dimulai. Anda bisa berlatih dengan membuat contoh-contoh sintetis. Namun, di sini kita kembali ke bagian artikel di mana kami menganggap bahwa lebih mudah dan lebih cepat untuk menulis diagnostik klasik, daripada menghasilkan banyak contoh.

Ambil contoh fungsi seperti _fread_nolock . Tentu saja, ini lebih jarang digunakan daripada rasa takut . Tetapi ketika menggunakannya, Anda bisa membuat semua kesalahan yang sama. Misalnya, buffer harus cukup besar. Ukuran ini seharusnya tidak kurang dari hasil mengalikan argumen kedua dan ketiga. Yaitu, saya ingin mendeteksi kode yang salah:

 int buffer[10]; size_t n = _fread_nolock(buffer, size_of(int), 100, stream); 

Berikut ini penjelasan fungsi ini dalam PVS-Studio:

 C_"size_t _fread_nolock" "(void * _DstBuf, size_t _ElementSize, size_t _Count, FILE * _File);" ADD(HAVE_STATE | RET_SKIP | F_MODIFY_PTR_1, nullptr, nullptr, "_fread_nolock", POINTER_1, BYTE_COUNT, COUNT, POINTER_2). Add_Read(from_2_3, to_return, buf_1). Add_DataSafetyStatusRelations(0, 3); 

Sekilas, anotasi semacam itu mungkin terlihat rumit, tetapi, pada kenyataannya, ketika Anda mulai menulisnya, itu menjadi sederhana. Plus, perlu diingat bahwa ini hanya kode tulis. Menulis dan lupa. Anotasi jarang berubah.

Sekarang mari kita bicara tentang fungsi ini dalam hal ML. GitHub tidak akan membantu kami. Ada sekitar 15.000 referensi untuk fungsi ini. Bahkan ada kode yang kurang masuk akal. Bagian penting dari hasil pencarian serupa:

 #define fread_unlocked _fread_nolock 

Apa saja pilihannya?

  1. Jangan lakukan apa pun. Ini adalah jalan menuju ke mana-mana.
  2. Latih penganalisa dengan menulis ratusan contoh untuk fungsi ini saja, sehingga penganalisa memahami hubungan antara ukuran buffer dan argumen lainnya. Ya, Anda dapat melakukan ini, tetapi secara ekonomi tidak rasional. Ini adalah jalan menuju ke mana-mana.
  3. , , . , . ML :). .

Seperti yang Anda lihat, ML dan ekor panjang fungsi yang jarang digunakan tidak digabungkan.

Di sini, orang-orang yang terkait dengan tema ML keberatan dengan kami dan mengatakan bahwa kami tidak mempertimbangkan opsi ketika analis akan mempelajari fungsi dan menarik kesimpulan tentang apa yang mereka lakukan. Di sini, tampaknya, entah kita tidak memahami para ahli, atau mereka tidak mengerti kita.

Fungsi tubuh mungkin tidak diketahui. Misalnya, itu mungkin fungsi terkait WinAPI. Jika ini adalah fungsi yang jarang digunakan, lalu bagaimana penganalisa memahami apa fungsinya? Kita dapat berfantasi bahwa selama pelatihan analis akan menggunakan Google itu sendiri, menemukan deskripsi fungsi, membaca dan memahaminya . Selain itu, perlu untuk mengambil kesimpulan tingkat tinggi dari dokumentasi. Dalam deskripsi _fread_nolocktidak ada yang dikatakan tentang hubungan antara ukuran buffer, argumen kedua dan ketiga. Perbandingan ini harus disimpulkan oleh kecerdasan buatan secara independen, berdasarkan pada pemahaman prinsip-prinsip umum pemrograman dan bagaimana bahasa C ++ bekerja. Tampaknya dengan serius semua ini akan mungkin untuk dipikirkan dalam 20 tahun ke depan.

Badan fungsi mungkin tersedia, tetapi mungkin tidak ada artinya dari ini. Pertimbangkan fungsi seperti memmove . Ini sering diimplementasikan seperti ini:

 void *memmove (void *dest, const void *src, size_t len) { return __builtin___memmove_chk(dest, src, len, __builtin_object_size(dest, 0)); } 

Dan apakah __builtin___memmove_chk ? Ini adalah fungsi intrinsik yang diimplementasikan oleh kompiler itu sendiri. Fungsi ini tidak memiliki kode sumber.

Atau memmove mungkin terlihat seperti ini: versi pertama yang muncul di assembler . Anda dapat mengajarkan penganalisa untuk memahami varian assembler yang berbeda, tetapi ada sesuatu yang tidak benar.

Ok, terkadang fungsi tubuh benar-benar terkenal. Selain itu, fungsi tubuh dalam kode pengguna juga dikenal. Tampaknya dalam hal ini, ML mendapatkan keuntungan luar biasa dengan membaca dan memahami apa yang semua fungsi ini lakukan.

Namun, dalam kasus ini juga, kami penuh pesimisme. Tugas ini terlalu rumit. Sulit bagi seseorang. Ingat betapa sulitnya bagi Anda untuk memahami kode yang tidak Anda tulis. Jika ini sulit bagi manusia, mengapa tugas ini mudah untuk AI? Sebenarnya, AI memiliki masalah besar dalam memahami konsep tingkat tinggi. Jika kita berbicara tentang memahami kode, maka kita tidak dapat melakukannya tanpa kemampuan untuk mengabstraksi dari detail implementasi dan mempertimbangkan algoritma pada level tinggi. Tampaknya di sini lagi selama 20 tahun Anda dapat menunda diskusi.

Nuansa lain.

Ada poin lain yang juga harus diperhitungkan, tapi yang kita pikirkan tidak hati-hati. Dan artikel itu sudah diseret. Karena itu, kami secara singkat menuliskan beberapa nuansa lagi, membiarkannya kepada pembaca untuk dipikirkan.
  • . , , . , - . . C++ auto_ptr . unique_ptr .
  • . , C C++ , . , . , . , long Windows 32/64 32 . Linux 32/64 . , . -. , , . , ( ). , .
  • . ML, , , . Yaitu , — , , . , . , , — , . . , / , , , . . : " PVS-Studio: ". , , .


Kami tidak menyangkal prospek pembelajaran mesin, termasuk dalam tugas analisis kode statis. Ada potensi untuk menggunakan ML dalam masalah pencarian kesalahan ketik, saat memfilter pesan palsu, dalam mencari pola kesalahan baru (belum dijelaskan di mana pun), dan sebagainya. Namun, kami sama sekali tidak berbagi optimisme dengan artikel-artikel tentang ML dalam masalah analisis kode yang jenuh.

Dalam artikel tersebut, kami menjelaskan beberapa masalah yang harus kami selesaikan jika menggunakan ML sebagai basis. Nuansa yang dijelaskan sebagian besar meniadakan manfaat dari pendekatan baru, apalagi, pendekatan klasik lama untuk penerapan analisis lebih menguntungkan dan lebih layak secara ekonomi.

Menariknya, artikel dari penganut metodologi ML tidak menyebutkan perangkap ini. Namun, ini tidak mengejutkan. Tema ML terlalu hype sekarang dan aneh untuk diharapkan dari para apologisnya penilaian seimbang penerapannya dalam tugas-tugas analisis kode statis.

Dari sudut pandang kami, pembelajaran mesin akan menempati ceruk dalam teknologi yang digunakan dalam analisis statis, bersama dengan analisis aliran kontrol, perhitungan simbolik, dan sebagainya.

Metodologi analisis statis mungkin mendapat manfaat dari penerapan ML, tetapi jangan melebih-lebihkan kemampuan teknologi ini.

PS


Karena artikel tersebut pada umumnya kritis, seseorang mungkin berpikir bahwa kita takut akan hal baru dan bagaimana Luddit berperang melawan ML, takut kehilangan pasar untuk alat analisis statis.

Unicorn Luddite


Tidak, kami tidak takut. Kami hanya tidak melihat alasan untuk mengeluarkan uang untuk pendekatan yang tidak efisien dalam pengembangan alat analisa kode PVS-Studio. Dalam satu atau lain bentuk, kami akan mengadopsi ML. Selain itu, beberapa diagnostik sudah mengandung elemen algoritma belajar mandiri. Namun, kami pasti akan sangat konservatif dan hanya mengambil apa yang jelas akan memiliki efek yang lebih besar daripada pendekatan klasik berdasarkan loop dan if-ah :). Bagaimanapun, kita perlu membuat alat yang efektif, dan tidak menghasilkan dana :).

Artikel itu ditulis dengan alasan semakin banyak pertanyaan diajukan pada topik yang dibahas, dan saya ingin memiliki artikel jawaban, meletakkan segala sesuatu di tempatnya.

Terima kasih atas perhatian anda Dan kami menawarkan untuk berkenalan dengan artikel " Alasan untuk memperkenalkan analisa kode statis PVS-Studio ke dalam proses pengembangan ."



Jika Anda ingin berbagi artikel ini dengan audiens yang berbahasa Inggris, silakan gunakan tautan ke terjemahan: Andrey Karpov, Victoria Khanieva. Pembelajaran Mesin dalam Analisis Statis Kode Sumber Program .

Source: https://habr.com/ru/post/id484208/


All Articles