Pembelajaran Mesin dalam Analisis Statis Kode Sumber Program

Program Pembelajaran Mesin dalam Analisis Statis Kode Sumber

Pembelajaran mesin telah tertanam kuat dalam berbagai bidang manusia, dari pengenalan ucapan hingga diagnosa medis. Popularitas pendekatan ini begitu besar sehingga orang mencoba menggunakannya di mana pun mereka bisa. Beberapa upaya untuk menggantikan pendekatan klasik dengan jaringan saraf muncul tidak berhasil. Kali ini kami akan mempertimbangkan pembelajaran mesin dalam hal menciptakan 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 bug dalam kode sumber perangkat lunak. Jawaban singkatnya adalah ya, tetapi sampai batas tertentu. Kami percaya bahwa dengan pembelajaran mesin, ada banyak jebakan yang mengintai dalam tugas analisis kode. Di bagian kedua artikel, kami akan menceritakan tentang mereka. Mari kita mulai dengan ulasan tentang solusi dan ide baru.

Pendekatan baru


Saat ini ada banyak analisa statis berdasarkan atau menggunakan pembelajaran mesin, termasuk pembelajaran mendalam dan NLP untuk deteksi kesalahan. Tidak hanya penggemar menggandakan potensi pembelajaran mesin, tetapi juga perusahaan besar, misalnya, Facebook, Amazon, atau Mozilla. Beberapa proyek tidak sepenuhnya merupakan analisa statis, karena mereka hanya menemukan beberapa kesalahan dalam komit.

Menariknya, hampir semuanya diposisikan sebagai produk game changer yang akan membuat terobosan dalam proses pengembangan karena kecerdasan buatan.



Mari kita lihat 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 untuk kode perangkat lunak Java, JavaScript, TypeScript, dan Python yang menampilkan pembelajaran mesin sebagai komponen. Menurut Boris Paskalev, lebih dari 250.000 aturan sudah ada. Alat ini belajar dari perubahan, yang dibuat oleh pengembang dalam kode sumber proyek sumber terbuka (jutaan repositori). Perusahaan itu sendiri mengatakan bahwa proyek mereka adalah semacam Grammarly untuk pengembang.



Bahkan, penganalisa ini membandingkan solusi Anda dengan basis proyeknya dan menawarkan solusi terbaik yang diinginkan dari pengalaman pengembang lain.

Pada bulan Mei 2018, pengembang mengatakan bahwa dukungan C ++ sedang dalam perjalanan, tetapi sejauh ini, bahasa ini tidak didukung. Meskipun, seperti yang dinyatakan di situs, dukungan bahasa baru dapat ditambahkan dalam hitungan minggu karena fakta bahwa bahasa hanya bergantung pada satu tahap, yang parsing.





Serangkaian posting tentang metode dasar analisa juga tersedia di situs.

Infer


Facebook cukup bersemangat dalam upayanya untuk memperkenalkan pendekatan komprehensif baru dalam produk-produknya. Pembelajaran mesin juga tidak berhenti di sela-sela. Pada 2013, mereka membeli startup yang mengembangkan analisa statis berdasarkan pembelajaran mesin. Dan pada 2015, kode sumber proyek menjadi terbuka .

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

Saat ini, Infer dapat menemukan kesalahan terkait dengan null pointer dereference dan kebocoran memori. Infer didasarkan pada logika Hoare, logika pemisahan dan penculikan, serta teori interpretasi abstrak. Penggunaan pendekatan ini memungkinkan penganalisa untuk memecah program menjadi potongan-potongan dan menganalisa secara mandiri.

Anda dapat mencoba menggunakan Infer pada proyek Anda, tetapi pengembang memperingatkan bahwa sementara dengan proyek Facebook itu menghasilkan sekitar 80% dari peringatan yang berguna, sejumlah kecil positif palsu tidak dijamin pada proyek lain. Berikut adalah beberapa kesalahan yang tidak dapat dideteksi oleh Infer sejauh ini, tetapi pengembang sedang berupaya mengimplementasikan peringatan ini:

  • indeks array di luar batas;
  • pengecualian pengecoran tipe;
  • kebocoran data yang tidak diverifikasi;
  • kondisi balapan.

Sapfix


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



Dalam beberapa kasus, SapFix mengembalikan semua perubahan atau bagiannya. Dalam kasus lain, ia mencoba untuk menyelesaikan masalah dengan membuat tambalan dari set pola perbaikannya. Set ini dibentuk dari pola perbaikan yang dikumpulkan oleh programmer sendiri dari set perbaikan yang sudah dibuat. Jika pola seperti itu tidak memperbaiki kesalahan, SapFix mencoba menyesuaikannya dengan situasi dengan membuat modifikasi kecil di pohon sintaksis abstrak hingga solusi potensial ditemukan.

Tetapi satu solusi potensial tidak cukup, jadi SapFix mengumpulkan beberapa solusi 'berdasarkan beberapa poin: apakah ada kesalahan kompilasi, apakah itu crash, apakah itu memperkenalkan crash baru. Setelah pengeditan sepenuhnya diuji, tambalan ditinjau oleh seorang programmer, yang akan memutuskan yang mana dari pengeditan terbaik yang memecahkan masalah.

Embold


Embold adalah platform start-up untuk analisis statis kode sumber perangkat lunak yang disebut Gamma sebelum penggantian nama. Alat analisa statis bekerja berdasarkan diagnosa alat itu sendiri, serta menggunakan alat analisa bawaan, seperti Cppcheck, SpotBugs, SQL Check dan lainnya.



Selain diagnosa sendiri, platform ini berfokus pada infografis yang jelas tentang pemuatan basis kode dan tampilan yang mudah dari kesalahan yang ditemukan, serta mencari kemungkinan 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 suatu sistem.



Salah satu keunggulan utama adalah sistem cerdas yang menawarkan solusi dan pengeditan, yang, selain diagnostik konvensional, memeriksa pengeditan berdasarkan informasi tentang perubahan sebelumnya.



Dengan NLP, Embold memecah kode dan mencari interkoneksi dan dependensi antara fungsi dan metode, menghemat waktu refactoring.



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

Sumber {d}


Sumber {d} adalah alat yang paling terbuka dalam hal cara pelaksanaannya dibandingkan dengan analisis yang telah kami ulas. Ini juga merupakan solusi kode sumber terbuka . Di situs web mereka, sebagai ganti alamat email Anda, Anda bisa mendapatkan selebaran produk yang menjelaskan teknologi yang mereka gunakan. Selain itu, situs web memberikan tautan ke database publikasi yang terkait dengan penggunaan pembelajaran mesin untuk analisis kode, serta repositori dengan dataset untuk pembelajaran berbasis kode. Produk itu sendiri adalah seluruh platform untuk menganalisis kode sumber dan produk perangkat lunak, dan tidak berfokus pada pengembang, melainkan pada manajer. Di antara kemampuannya adalah perhitungan ukuran utang teknis, hambatan dalam proses pengembangan dan statistik global lainnya pada proyek.



Pendekatan mereka terhadap analisis kode melalui pembelajaran mesin didasarkan pada Hipotesis Alamiah, sebagaimana diuraikan dalam artikel " Tentang Kealamian Perangkat Lunak ".

"Bahasa pemrograman, secara teori, adalah kompleks, fleksibel dan kuat, tetapi program yang orang-orang sungguhan sebenarnya kebanyakan sederhana dan agak berulang, dan dengan demikian mereka memiliki sifat statistik yang dapat diprediksi berguna yang dapat ditangkap dalam model bahasa statistik dan dimanfaatkan untuk rekayasa perangkat lunak tugas. "

Berdasarkan hipotesis ini, semakin besar basis kode, semakin besar sifat statistiknya, dan semakin akurat metrik yang dicapai melalui pembelajaran.

Untuk menganalisis kode dalam sumber {d}, layanan Babelfish digunakan, yang dapat mem-parsing file kode dalam salah satu bahasa yang tersedia, dapatkan pohon sintaksis abstrak dan mengubahnya menjadi pohon sintaksis universal.



Namun, sumber {d} tidak mencari kesalahan dalam kode. Berdasarkan pohon menggunakan ML pada seluruh proyek, sumber {d} mendeteksi pemformatan kode, gaya yang diterapkan dalam proyek dan dalam komit. Jika kode baru tidak sesuai dengan gaya kode proyek, itu membuat beberapa pengeditan.





Pembelajaran berfokus pada beberapa elemen dasar: spasi, tabulasi, jeda baris, dll.



Baca lebih lanjut tentang ini dalam publikasi mereka: " STYLE-ANALYZER: memperbaiki ketidakkonsistenan gaya kode dengan algoritme tanpa pengawasan yang dapat ditafsirkan ".

Secara keseluruhan, sumber {d} adalah platform luas untuk mengumpulkan statistik beragam tentang kode sumber dan proses pengembangan proyek: mulai dari perhitungan efisiensi pengembang hingga biaya waktu untuk tinjauan kode.

Komitmen pintar


Clever-Commit adalah penganalisa yang dibuat oleh Mozilla bekerja sama dengan Ubisoft. Ini didasarkan pada studi CLEVER (Menggabungkan Tingkat Pencegahan Bug dan Teknik Resolusi) oleh Ubisoft dan produk anak-anak, Asisten Komit, yang mendeteksi komitmen mencurigakan yang kemungkinan mengandung kesalahan. Karena CLEVER didasarkan pada perbandingan kode, CLEVER dapat menunjuk pada kode berbahaya dan memberikan saran untuk kemungkinan pengeditan. Menurut deskripsi, dalam 60-70% kasus, Pintar-Komit menemukan tempat-tempat bermasalah dan menawarkan suntingan yang benar dengan probabilitas yang sama. Secara umum, ada sedikit informasi tentang proyek ini dan tentang kesalahan yang dapat ditemukan.

CodeGuru


Baru-baru ini CodeGuru, yang merupakan produk dari Amazon, telah sejalan dengan analis yang menggunakan pembelajaran mesin. Ini adalah layanan pembelajaran mesin yang memungkinkan Anda menemukan kesalahan dalam kode, serta mengidentifikasi area yang mahal di dalamnya. Analisis ini hanya tersedia untuk kode Java sejauh ini, tetapi penulis berjanji untuk mendukung bahasa lain di masa depan. Meskipun diumumkan baru-baru ini, Andy Jassy, โ€‹โ€‹CEO AWS (Amazon Web Services) mengatakan telah lama digunakan di Amazon.

Situs web itu mengatakan bahwa CodeGuru sedang belajar di pangkalan kode Amazon, dan juga lebih dari 10.000 proyek sumber terbuka.

Pada dasarnya, layanan ini dibagi menjadi dua bagian: Peninjau KodeGuru, diajarkan menggunakan pencarian aturan asosiatif dan mencari kesalahan dalam kode, dan CodeGuru Profiler, memantau kinerja aplikasi.



Secara umum, tidak banyak informasi yang tersedia tentang proyek ini. Seperti yang dinyatakan situs web, Reviewer menganalisis basis kode Amazon dan mencari permintaan tarik, yang berisi panggilan AWS API untuk mempelajari cara menangkap penyimpangan dari "praktik terbaik". Selanjutnya, ia melihat perubahan yang dibuat dan membandingkannya dengan data dari dokumentasi, yang dianalisis secara bersamaan. Hasilnya adalah model "praktik terbaik".

Dikatakan juga bahwa rekomendasi untuk kode pengguna cenderung membaik setelah menerima umpan balik.

Daftar kesalahan yang direspon Reviewer cukup kabur, karena tidak ada dokumentasi kesalahan spesifik yang telah dipublikasikan:

  • Praktik Terbaik AWS
  • Konkurensi
  • Kebocoran sumber daya
  • Kebocoran informasi rahasia
  • Umum "praktik terbaik" pengkodean

Skeptisisme kami


Sekarang mari kita pertimbangkan pencarian kesalahan dari sudut pandang tim kami, yang telah mengembangkan analisa statis selama bertahun-tahun. Kami melihat sejumlah masalah tingkat tinggi dalam penerapan metode pembelajaran, yang ingin kami bahas. Untuk mulai dengan, kami akan membagi semua pendekatan ML menjadi dua jenis:

  1. Yang mengajarkan penganalisa statis secara manual untuk mencari berbagai masalah, menggunakan contoh kode sintetik dan nyata;
  2. Mereka yang mengajarkan algoritma pada sejumlah besar kode sumber terbuka dan riwayat revisi (GitHub), setelah itu penganalisa akan mulai mendeteksi bug dan bahkan menawarkan pengeditan.

Kami akan berbicara tentang setiap arah secara terpisah, karena mereka memiliki kelemahan yang berbeda. Setelah itu, saya pikir, pembaca akan mengerti mengapa kita tidak menyangkal kemungkinan pembelajaran mesin, tetapi masih tidak berbagi antusiasme.

Catatan Kami melihat dari perspektif mengembangkan alat analisis tujuan umum statis universal. Kami fokus pada pengembangan alat analisis, yang dapat digunakan oleh tim mana pun, bukan yang berfokus pada basis kode tertentu.

Pengajaran Manual dari Penganalisis Statis


Katakanlah kita ingin menggunakan ML untuk mulai mencari jenis cacat 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 mengajarkan penganalisa untuk mencari kesalahan tersebut. Selain itu, Anda dapat menambahkan contoh nyata bug yang sudah ditemukan ke tes. Nah, pertanyaannya adalah di mana menemukan contoh seperti itu. Ok, anggap saja itu mungkin. Misalnya, kami memiliki sejumlah contoh kesalahan seperti itu: V501 , V3001 , V6001 .

Jadi mungkinkah untuk mengidentifikasi cacat dalam kode dengan menggunakan algoritma ML? Ya, benar. Masalahnya adalah - mengapa kita membutuhkannya?

Lihat, untuk mengajar analis, kita perlu melakukan banyak upaya untuk mempersiapkan contoh-contoh untuk pengajaran. Pilihan lain adalah untuk menandai kode aplikasi nyata, menunjukkan fragmen di mana penganalisa harus mengeluarkan peringatan. Bagaimanapun, banyak pekerjaan yang perlu dilakukan, karena harus ada ribuan contoh untuk belajar. Atau puluhan ribu.

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

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


Mari kita lihat potensi implementasi diagnostik sederhana 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("Oh boy! Holy cow!", left, 501, Level_1, "CWE-571"); } } 

Dan itu dia! Anda tidak memerlukan basis contoh untuk ML!

Di masa depan, diagnostik harus belajar untuk memperhitungkan sejumlah pengecualian dan mengeluarkan peringatan untuk (A [0] == A [1-1]). Seperti yang kita tahu, ini bisa diprogram dengan mudah. Sebaliknya, dalam hal ini, segalanya akan menjadi buruk dengan dasar contoh.

Perhatikan bahwa dalam kedua kasus kita akan memerlukan sistem pengujian, dokumentasi, dan sebagainya. Adapun kontribusi tenaga kerja dalam menciptakan diagnostik baru, pendekatan klasik, di mana aturan diprogram secara kaku dalam kode, memimpin.

Oke, sudah waktunya untuk aturan lain. Misalnya, di mana hasil dari beberapa fungsi harus digunakan. Tidak ada gunanya memanggil mereka dan tidak menggunakan hasilnya. Berikut beberapa fungsi tersebut:

  • malloc
  • memcmp
  • string :: kosong

Inilah yang dilakukan diagnostik PVS-Studio V530 .

Jadi yang kami inginkan adalah mendeteksi panggilan ke fungsi tersebut, yang hasilnya tidak digunakan. Untuk melakukan ini, Anda dapat menghasilkan banyak tes. Dan kami pikir semuanya akan bekerja dengan baik. Tetapi sekali lagi tidak jelas mengapa itu dibutuhkan.

Implementasi diagnostik V530 dengan semua pengecualian mengambil 258 baris kode di penganalisa PVS-Studio, 64 di antaranya adalah komentar. Ada juga tabel dengan anotasi fungsi, yang mencatat bahwa hasilnya harus digunakan. Jauh lebih mudah untuk mengisi tabel ini daripada membuat contoh-contoh sintetis.

Hal-hal akan menjadi lebih buruk dengan diagnostik yang menggunakan analisis aliran data. Misalnya, penganalisis PVS-Studio dapat melacak nilai pointer, yang memungkinkan Anda 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; // <= .... } 

Contohnya diambil dari artikel " Chromium: Memory Leaks ". Jika kondisi (pkey.n0inv == 0) benar, fungsi keluar tanpa membebaskan buffer, penunjuk yang disimpan dalam variabel n .

Dari sudut pandang PVS-Studio, tidak ada yang rumit di sini. Penganalisis telah mempelajari fungsi BnNew dan ingat bahwa itu mengembalikan pointer ke blok memori yang dialokasikan. Di fungsi lain, ia memperhatikan bahwa buffer mungkin tidak bebas dan pointer ke itu hilang pada saat keluar dari fungsi.

Ini adalah algoritma umum dari nilai pelacakan yang berfungsi. Tidak masalah bagaimana kode ditulis. Tidak masalah apa pun yang ada dalam fungsi yang tidak berhubungan dengan kerja pointer. Algoritma bersifat universal dan diagnostik V773 menemukan banyak kesalahan dalam berbagai proyek. Lihat betapa berbedanya fragmen kode dengan kesalahan yang terdeteksi!

Kami bukan ahli dalam ML, tetapi kami memiliki perasaan bahwa masalah besar ada di sudut sini. Ada banyak cara Anda dapat menulis kode dengan kebocoran memori. Bahkan jika mesin belajar dengan baik bagaimana melacak nilai-nilai variabel, perlu dipahami bahwa ada panggilan ke fungsi juga.

Kami menduga itu akan membutuhkan begitu banyak contoh untuk belajar sehingga tugas menjadi tidak dapat diraih. Kami tidak mengatakan itu tidak realistis. Kami ragu bahwa biaya pembuatan penganalisis akan terbayar.

Analogi Yang muncul di benak saya adalah analogi dengan kalkulator, di mana alih-alih diagnostik, kita harus memprogram tindakan aritmatika. Kami yakin Anda dapat mengajarkan kalkulator berbasis ML untuk merangkum angka dengan baik dengan mengumpankannya hasil operasi 1 + 1 = 2, 1 + 2 = 3, 2 + 1 = 3, 100 + 200 = 300 dan seterusnya . Seperti yang Anda pahami, kelayakan pengembangan kalkulator semacam itu adalah pertanyaan besar (kecuali jika dialokasikan hibah :). Kalkulator yang lebih sederhana, lebih cepat, lebih akurat, dan andal dapat ditulis menggunakan operasi "+" sederhana dalam kode.

Kesimpulan Nah, cara ini akan berhasil. Tetapi menggunakannya, menurut kami, tidak masuk akal secara praktis. Pengembangan akan lebih memakan waktu, tetapi hasilnya - kurang dapat diandalkan dan akurat, terutama ketika datang untuk mengimplementasikan diagnostik yang kompleks berdasarkan analisis aliran data.

Belajar tentang Sejumlah Besar Kode Sumber Terbuka


Oke, kami sudah menyortir dengan contoh-contoh sintetis manual, tetapi ada juga GitHub. Anda dapat melacak riwayat komit dan menyimpulkan pola mengubah / memperbaiki kode. Maka Anda dapat menunjuk tidak hanya pada fragmen kode yang mencurigakan, tetapi bahkan menyarankan cara untuk memperbaiki kode.

Jika Anda berhenti pada level detail ini, semuanya terlihat bagus. Iblis, seperti biasa, ada dalam perinciannya. Jadi mari kita bicara benar tentang perincian ini.

Nuansa pertama. Sumber data.

Suntingan GitHub cukup acak dan beragam. Orang-orang sering malas membuat komitmen atom dan melakukan beberapa pengeditan dalam kode pada saat bersamaan. Anda tahu bagaimana itu terjadi: Anda akan memperbaiki bug, dan pada saat yang sama sedikit memperbaiki itu ("Dan di sini saya akan menambahkan penanganan kasus seperti itu ..."). Bahkan seseorang kemudian dapat dipahami, apakah ini tetap terkait satu sama lain, atau tidak.

Tantangannya adalah bagaimana membedakan kesalahan aktual dari menambahkan fungsionalitas baru atau sesuatu yang lain. Anda bisa, tentu saja, mendapatkan 1000 orang yang secara manual akan menandai komit. Orang-orang harus menunjukkan: di sini kesalahan diperbaiki, di sini refactoring, di sini ada beberapa fungsi baru, di sini persyaratannya telah berubah dan sebagainya.

Apakah markup seperti itu mungkin? Ya! Tetapi perhatikan seberapa cepat spoofing terjadi. Alih-alih "algoritma belajar sendiri berdasarkan GitHub" kita sudah membahas cara membuat teka-teki ratusan orang untuk waktu yang lama. Pekerjaan dan biaya pembuatan alat meningkat secara dramatis.

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

Jadi, kita bahkan belum belajar, dan sudah ada nuansa :).

Nuansa kedua. Keterlambatan dalam pengembangan.

Analisis yang akan belajar berdasarkan pada platform seperti itu, seperti GitHub akan selalu mengalami sindrom seperti itu, sebagai "keterlambatan keterbelakangan mental." Ini karena bahasa pemrograman berubah seiring waktu.

Sejak C # 8.0 telah ada jenis Referensi Nullable, membantu untuk melawan Null Reference Exception (NRE). Di JDK 12, operator switch baru ( JEP 325 ) muncul. Dalam C ++ 17, ada kemungkinan untuk melakukan konstruksi kondisional waktu kompilasi ( constexpr if ). Dan sebagainya.

Bahasa pemrograman berkembang. Selain itu, yang, seperti C ++, berkembang sangat cepat. Konstruksi baru muncul, fungsi standar baru ditambahkan dan seterusnya. Seiring dengan fitur-fitur baru, ada pola kesalahan baru yang kami juga ingin mengidentifikasi dengan analisis kode statis.

Pada titik ini, metode ML menghadapi masalah: pola kesalahan sudah jelas, kami ingin mendeteksinya, tetapi tidak ada basis kode untuk belajar.

Mari kita lihat masalah ini menggunakan contoh tertentu. Berbasis rentang untuk loop muncul di C ++ 11. Anda dapat menulis kode berikut, melintasi semua elemen dalam wadah:

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

Loop baru telah membawa pola kesalahan baru dengannya. Jika kita mengubah wadah di dalam loop, ini akan menyebabkan pembatalan iterator "bayangan".

Mari kita lihat 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 push_back , __begin dan __end iterators dapat dibatalkan, jika memori dipindahkan di dalam vektor. Hasilnya adalah perilaku program yang tidak ditentukan.

Oleh karena itu, pola kesalahan telah lama diketahui dan dijelaskan dalam literatur. Penganalisa PVS-Studio mendiagnosisnya dengan diagnostik V789 dan telah menemukan kesalahan nyata dalam proyek sumber terbuka.

Seberapa cepat GitHub mendapatkan kode baru yang cukup untuk memperhatikan pola ini? Pertanyaan bagus ... Penting untuk diingat bahwa jika ada rentang berbasis untuk loop, itu tidak berarti bahwa semua programmer akan segera mulai menggunakannya sekaligus. Mungkin bertahun-tahun sebelum ada banyak kode menggunakan loop baru. Selain itu, banyak kesalahan harus dibuat, dan kemudian mereka harus diperbaiki sehingga algoritme dapat melihat pola dalam suntingan.

Berapa tahun yang dibutuhkan? Lima? Sepuluh?

Sepuluh terlalu banyak, atau hanya prediksi pesimistis? Jauh dari itu. Pada saat artikel itu ditulis, sudah delapan tahun sejak rentang berbasis untuk loop muncul di C ++ 11. Namun sejauh ini dalam database kami hanya ada tiga kasus kesalahan seperti itu. Tiga kesalahan itu tidak banyak dan tidak sedikit. Seseorang seharusnya tidak menarik kesimpulan apa pun dari nomor ini. Yang utama adalah untuk mengkonfirmasi bahwa pola kesalahan seperti itu nyata dan masuk akal untuk mendeteksinya.

Sekarang bandingkan angka ini, misalnya, dengan pola kesalahan ini: pointer akan didereferensi sebelum cek . Secara total, kami telah mengidentifikasi 1.716 kasus seperti itu ketika memeriksa proyek sumber terbuka.

Mungkin kita seharusnya tidak mencari kesalahan dalam jangkauan untuk loop sama sekali? Tidak. Hanya saja pemrogram bersifat inersia, dan operator ini menjadi sangat lambat populer. Secara bertahap, akan ada lebih banyak kode dengan itu dan kesalahan, masing-masing.

Ini mungkin terjadi hanya 10-15 tahun setelah C ++ 11 muncul. Ini mengarah pada pertanyaan filosofis. Misalkan kita sudah tahu pola kesalahan, kita hanya akan menunggu selama bertahun-tahun sampai kita memiliki banyak kesalahan dalam proyek open source. Akankah begitu?

Jika "ya", aman untuk mendiagnosis "keterlambatan perkembangan mental" untuk semua analisis berbasis ML.

Jika "tidak", apa yang harus kita lakukan? Tidak ada contoh. Menulisnya secara manual? Tetapi dengan cara ini, kita kembali ke bab sebelumnya, di mana kita telah memberikan deskripsi rinci tentang opsi ketika orang akan menulis satu paket contoh untuk dipelajari.

Ini bisa dilakukan, tetapi pertanyaan tentang kemanfaatan muncul kembali. Implementasi diagnostik V789 dengan semua pengecualian dalam analisa PVS-Studio hanya membutuhkan 118 baris kode, yang 13 baris merupakan komentar. Artinya, ini adalah diagnostik yang sangat sederhana, yang dapat dengan mudah diprogram dengan cara klasik.

Situasinya akan mirip 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 menggambarkan setiap diagnostik. Tanpa itu, akan sangat sulit atau tidak mungkin untuk menggunakan alat analisa. Dalam dokumentasi PVS-Studio, kami memiliki deskripsi setiap diagnostik, yang memberikan contoh kode yang salah dan cara memperbaikinya. Kami juga memberikan tautan ke CWE , di mana orang dapat membaca deskripsi masalah alternatif. Dan tetap saja, kadang-kadang pengguna tidak memahami sesuatu, dan mereka menanyakan pertanyaan klarifikasi kepada kami.

Dalam kasus analisis statis berbasis ML, masalah dokumentasi entah bagaimana ditutup. Diasumsikan bahwa penganalisa hanya akan menunjuk ke suatu tempat yang tampaknya mencurigakan dan bahkan mungkin menyarankan cara memperbaikinya. Keputusan untuk mengedit atau tidak tergantung pada orang tersebut. Di situlah masalahnya dimulai ... Tidak mudah untuk membuat keputusan tanpa bisa membaca, yang membuat penganalisa tampak curiga terhadap tempat tertentu dalam kode.

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

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

Dan menyarankan agar kami menggantinya dengan:

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

Segera jelas bahwa programmer membuat kesalahan ketik dan menambahkan 1 di tempat yang salah. Akibatnya, lebih sedikit memori yang akan dialokasikan daripada yang diperlukan.

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

Bayangkan bahwa alat analisa "diam-diam" menunjuk ke kode ini:

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

Dan menyarankan agar kita mengubah tipe char dari nilai pengembalian untuk int:

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

Tidak ada dokumentasi untuk peringatan itu. Tampaknya, tidak akan ada teks dalam pesan peringatan juga, jika kita berbicara tentang penganalisa yang sepenuhnya independen.

Apa yang harus kita lakukan? Apa bedanya? Apakah layak melakukan penggantian seperti itu?

Sebenarnya, saya bisa mengambil risiko dan setuju untuk memperbaiki kode. Meskipun setuju untuk memperbaiki tanpa memahaminya adalah praktik yang kasar ... :) Anda dapat melihat deskripsi fungsi memcmp dan mengetahui bahwa fungsi tersebut benar-benar mengembalikan nilai seperti int : 0, lebih dari nol dan kurang dari nol. Tetapi mungkin masih belum jelas mengapa melakukan pengeditan, jika kodenya sudah berfungsi dengan baik.

Sekarang, jika Anda tidak tahu apa itu pengeditan, lihat deskripsi diagnostik V642 . Segera menjadi jelas bahwa ini adalah bug nyata. Selain itu, dapat menyebabkan kerentanan.

Mungkin, contohnya tampak tidak meyakinkan. Bagaimanapun, penganalisa menyarankan 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 sebuah objek. Ini serialisasi. Kemudian keadaan objek berubah, dan serialisasi ulang. Itu terlihat baik. Sekarang bayangkan, tiba-tiba, alat analisa tidak menyukai kode dan ingin menggantinya dengan yang berikut:

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

Alih-alih mengubah objek dan menulis ulang, objek baru dibuat dan akan diserialisasi.

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

Anda akan mengatakan itu tidak jelas. Memang, itu tidak bisa dipahami. Dan akan selalu begitu. Bekerja dengan penganalisa "sunyi" seperti itu akan menjadi studi tanpa akhir dalam upaya untuk memahami mengapa penganalisa tidak menyukai apa pun.

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

Kami berharap kami berhasil menjelaskan pentingnya dokumentasi. Inilah pertanyaannya. Bagaimana dokumentasi untuk penganalisa berbasis ML muncul?

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

Dalam kasus ML, prosesnya terbalik. Ya, penganalisa dapat melihat anomali dalam kode dan menunjukkannya. Tetapi ia tidak tahu apa-apa tentang esensi cacat itu. Itu tidak mengerti dan tidak akan memberi tahu Anda mengapa Anda tidak dapat menulis kode seperti itu. Ini adalah abstraksi tingkat tinggi. Dengan cara ini, penganalisa juga harus belajar membaca dan memahami dokumentasi untuk fungsi.

Seperti yang saya katakan, karena masalah dokumentasi dihindari dalam artikel tentang pembelajaran mesin, kami tidak siap untuk membahasnya lebih lanjut. Hanya nuansa besar lain yang sudah kami ucapkan.

Catatan Anda bisa berpendapat bahwa dokumentasi itu opsional. Penganalisa dapat merujuk pada banyak contoh perbaikan pada GitHub dan orang yang melihat melalui komit dan komentar kepada mereka, akan mengerti apa itu. Ya, benar. Tapi idenya tidak terlihat menarik. Di sini penganalisa adalah orang jahat, yang lebih suka teka-teki seorang programmer daripada membantunya.

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 lain tidak memiliki basis kode sumber yang cukup besar untuk memberikan pembelajaran yang efektif.

Mari kita lihat ini menggunakan contoh nyata. Pertama, mari kita pergi ke GitHub dan mencari repositori untuk bahasa Java yang populer.

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

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

Hasil: bahasa: โ€œ1C Enterpriseโ€: 551 hasil repositori yang tersedia

Mungkin analisa tidak diperlukan untuk bahasa ini? Tidak, mereka Ada kebutuhan praktis untuk menganalisis program-program semacam itu dan sudah ada analisis yang tepat. Misalnya, ada Plugin SonarQube 1C (BSL), yang diproduksi oleh perusahaan " Silver Bullet ".

Saya pikir tidak ada penjelasan khusus yang diperlukan mengapa pendekatan ML akan sulit untuk bahasa khusus.

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

Artikel tentang analisis kode statis berbasis ML kebanyakan tentang bahasa-bahasa seperti Java, JavaScript, dan Python. Ini dijelaskan oleh popularitas mereka yang ekstrem. Adapun C dan C ++, mereka agak diabaikan, meskipun Anda tidak dapat menyebutnya tidak populer.

Kami menyarankan bahwa ini bukan tentang popularitas / prospek yang menjanjikan, tetapi ini tentang masalah dengan bahasa C dan C ++. Dan sekarang kita akan membahas satu masalah yang tidak nyaman.

File c / cpp abstrak bisa sangat sulit untuk dikompilasi. Setidaknya Anda tidak dapat memuat proyek dari GitHub, pilih file cpp acak dan cukup kompilasi. Sekarang kami akan menjelaskan apa hubungannya semua ini dengan ML.

Jadi kami ingin mengajarkan alat analisa. Kami mengunduh proyek dari GitHub. Kami tahu tambalan dan menganggapnya memperbaiki bug. Kami ingin hasil edit ini menjadi salah satu contoh untuk belajar. Dengan kata lain, kami memiliki file .cpp sebelum dan sesudah diedit.

Di situlah masalahnya dimulai. Tidak cukup hanya mempelajari perbaikannya. Konteks penuh juga diperlukan. Anda perlu mengetahui deklarasi kelas yang digunakan, Anda perlu tahu prototipe fungsi yang digunakan, Anda perlu tahu bagaimana makro berkembang dan seterusnya. Dan untuk melakukan ini, Anda perlu melakukan preprocessing file lengkap.

Mari kita lihat contohnya. Pada awalnya, kode itu terlihat seperti ini:

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

Itu diperbaiki dengan cara ini:

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

Haruskah penganalisa mulai belajar untuk menyarankan (x == "y") pengganti forstrcmp (x, "y")?

Anda tidak dapat menjawab pertanyaan itu 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 pointer biasa. Jika kita tidak memperhitungkan tipe variabel, penganalisa mungkin belajar mengeluarkan peringatan baik dan buruk (untuk kasus dengan std :: string ).

Deklarasi kelas biasanya terletak di file header. Di sini sedang menghadapi kebutuhan untuk melakukan preprocessing untuk memiliki semua informasi yang diperlukan. Ini sangat penting untuk C dan C ++.

Jika seseorang mengatakan bahwa itu mungkin dilakukan tanpa preprocessing, ia adalah seorang penipu, atau hanya tidak terbiasa dengan bahasa C atau C ++.

Untuk mengumpulkan semua informasi yang diperlukan, Anda perlu preprocessing yang benar. Untuk melakukan ini, Anda perlu tahu di mana dan file header apa yang terletak, makro mana yang ditetapkan selama proses pembuatan. Anda juga perlu tahu bagaimana file cpp tertentu dikompilasi.

Itu masalahnya. Seseorang tidak hanya mengkompilasi file (atau, lebih tepatnya, menentukan kunci untuk kompiler sehingga menghasilkan file preprocess). Kita perlu mencari tahu bagaimana file ini dikompilasi. Informasi ini ada dalam skrip build, tetapi pertanyaannya adalah bagaimana cara mendapatkannya dari sana. Secara umum, tugasnya rumit.



Selain itu, banyak proyek di GitHub berantakan. Jika Anda mengambil proyek abstrak dari sana, Anda sering harus mengotak-atik untuk mengkompilasinya. Suatu hari Anda kekurangan perpustakaan dan Anda perlu mencari dan mengunduhnya secara manual. Di hari lain, semacam sistem pembangunan yang ditulis sendiri digunakan, yang harus ditangani. Itu bisa apa saja. Terkadang proyek yang diunduh hanya menolak untuk dibuat dan perlu diubah. Anda tidak bisa hanya mengambil dan secara otomatis mendapatkan representasi preprocessed (.i) untuk file .cpp. Ini bisa rumit bahkan ketika melakukannya secara manual.

Kita dapat mengatakan, well, masalah dengan proyek-proyek non-bangunan dapat dipahami, tetapi tidak penting. Mari kita hanya bekerja dengan proyek yang bisa dibangun. Masih ada tugas preprocessing file tertentu. Belum lagi kasus ketika kita berurusan dengan beberapa kompiler khusus, misalnya, untuk sistem embedded.

Bagaimanapun, masalah yang dijelaskan tidak dapat diatasi. Namun, semua ini sangat sulit dan padat karya. Dalam kasus C dan C ++, kode sumber yang terletak di GitHub tidak melakukan apa-apa. Ada banyak pekerjaan yang harus dilakukan untuk mempelajari cara menjalankan kompiler secara otomatis.

Catatan Jika pembaca masih belum mendapatkan kedalaman masalahnya, kami mengundang Anda untuk mengambil bagian dalam percobaan berikut. Ambil sepuluh proyek acak berukuran sedang dari GitHub dan cobalah untuk mengompilasinya dan kemudian dapatkan versi pracroses untuk file .cpp. Setelah itu, pertanyaan tentang sulitnya tugas ini akan hilang :).

Mungkin ada masalah serupa dengan bahasa lain, tetapi mereka sangat jelas dalam C dan C ++.

Nuansa keenam. Harga menghilangkan positif palsu.

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

Sekarang kita akan kembali ke diagnostik V789 yang sebelumnya dianggap, mendeteksi perubahan kontainer di dalam rentang berbasis untuk loop. Katakanlah kita tidak cukup berhati-hati saat menulisnya, dan klien melaporkan positif palsu. Dia menulis bahwa penganalisa tidak memperhitungkan skenario ketika loop berakhir setelah wadah diubah, dan karena itu tidak ada masalah. Kemudian ia memberikan contoh kode berikut di mana penganalisis memberikan false positive:

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

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

Kelemahan ini juga dapat dikoreksi ketika penganalisa dibangun di atas algoritma pembelajaran. Yang pasti, itu bisa diajarkan dengan mengumpulkan puluhan atau ratusan contoh kode yang harus dianggap benar.

Sekali lagi, pertanyaannya bukan pada kelayakan, tetapi dalam pendekatan praktis. Kami menduga bahwa berperang melawan positif palsu spesifik, yang mengganggu klien, jauh lebih mahal dalam kasus ML. Artinya, dukungan pelanggan dalam hal menghilangkan kesalahan positif akan membutuhkan lebih banyak uang.

Nuansa ketujuh. Fitur yang jarang digunakan dan ekor panjang.

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

Jika kita berbicara tentang fungsi-fungsi seperti itu dari bahasa C, seperti strcmp , maka sebenarnya 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 string dibandingkan dengan dirinya sendiri. Itu diperbaiki.
  • Aneh jika salah satu petunjuknya NULL. Itu diperbaiki.
  • Sungguh aneh bahwa hasil dari fungsi ini tidak digunakan. Itu diperbaiki.
  • Dan sebagainya.

Bukankah itu keren? Tidak. Di sini kita menghadapi masalah "ekor panjang". Secara singkat titik "ekor panjang" sebagai berikut. Tidak praktis menjual hanya 50 buku paling populer dan paling banyak dibaca di toko buku. Ya, setiap buku seperti itu akan dibeli, katakanlah, 100 kali lebih sering daripada buku yang tidak ada dalam daftar ini. Namun, sebagian besar hasilnya akan terdiri dari buku-buku lain yang, seperti kata mereka, menemukan pembaca mereka. Misalnya, toko online Amazon.com menerima lebih dari setengah keuntungan dari apa yang ada di luar dari 130.000 "item paling populer".

Ada fungsi yang populer dan ada beberapa di antaranya. Ada yang tidak populer, tetapi ada banyak dari mereka. Misalnya, ada variasi fungsi perbandingan string berikut:

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

Seperti yang Anda lihat, mereka lebih jarang digunakan, tetapi ketika Anda menggunakannya, Anda dapat membuat kesalahan yang sama. Ada terlalu sedikit contoh untuk mengidentifikasi pola. Namun, fungsi-fungsi ini tidak dapat diabaikan. Secara individual, mereka jarang digunakan, tetapi banyak kode ditulis dengan penggunaannya, yang lebih baik diperiksa. Di situlah "ekor panjang" menunjukkan dirinya.

Di PVS-Studio, kami membubuhi keterangan fitur secara manual. Misalnya, saat ini sekitar 7.200 fungsi telah dianotasi untuk C dan C ++. Inilah yang kami tandai:

  • Winapi
  • Perpustakaan C Standar,
  • Perpustakaan Template Standar (STL),
  • glibc (Perpustakaan GNU C)
  • Qt
  • Mfc
  • zlib
  • libpng
  • Openssl
  • dan lainnya.

Di satu sisi, sepertinya jalan buntu. Anda tidak dapat membubuhi keterangan segalanya. Di sisi lain, ini berfungsi.

Sekarang inilah pertanyaannya. Apa manfaat yang bisa dimiliki ML? Keuntungan yang signifikan tidak begitu jelas, tetapi Anda dapat melihat kerumitannya.

Anda dapat berargumen bahwa algoritma yang dibangun di atas ML sendiri akan menemukan pola dengan fungsi yang sering digunakan dan mereka tidak perlu dijelaskan. Ya itu benar. Namun, tidak ada masalah untuk membubuhi keterangan secara independen fungsi populer seperti strcmp atau malloc .

Meskipun demikian, buntut panjang menyebabkan masalah. Anda dapat mengajar dengan membuat contoh-contoh sintetis. Namun, di sini kita kembali ke bagian artikel, di mana kami mengatakan bahwa lebih mudah dan lebih cepat untuk menulis diagnostik klasik, daripada menghasilkan banyak contoh.

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

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

Berikut penjelasan fungsi ini di 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, penjelasan seperti itu mungkin terlihat sulit, tetapi pada kenyataannya, ketika Anda mulai menulisnya, itu menjadi sederhana. Plus, itu hanya kode tulis. Menulis dan lupa. Anotasi jarang berubah.

Sekarang mari kita bicara tentang fungsi ini dari sudut pandang ML. GitHub tidak akan membantu kami. Ada sekitar 15.000 menyebutkan fungsi ini. Bahkan ada kode yang kurang bagus. Bagian penting dari hasil pencarian adalah sebagai berikut:

 #define fread_unlocked _fread_nolock 

Apa saja pilihannya?
  1. Jangan lakukan apa pun. Ini cara ke mana-mana.
  2. Bayangkan saja, ajarkan penganalisa dengan menulis ratusan contoh hanya untuk satu fungsi sehingga penganalisa memahami interkoneksi antara argumen penyangga dan argumen lainnya. Ya, Anda bisa melakukan itu, tetapi secara ekonomi tidak rasional. Ini jalan buntu.
  3. Anda dapat menemukan cara yang mirip dengan kami ketika anotasi ke fungsi akan diatur secara manual. Itu cara yang baik dan masuk akal. Itu hanya ML, yang tidak ada hubungannya dengan itu :). Ini adalah kemunduran ke cara klasik menulis analisis statis.

Seperti yang Anda lihat, ML dan buntut panjang fitur yang jarang digunakan tidak cocok.

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

Badan fungsi mungkin tidak diketahui. Misalnya, itu bisa menjadi fungsi terkait WinAPI. Jika ini adalah fungsi yang jarang digunakan, bagaimana penganalisa akan mengerti apa yang dilakukannya? Kita dapat berfantasi bahwa penganalisa akan menggunakan Google itu sendiri, menemukan deskripsi fungsi, membaca dan memahaminya . Selain itu, harus menarik kesimpulan tingkat tinggi dari dokumentasi. Deskripsi _fread_nolock tidak memberi tahu apa pun tentang interkoneksi antara buffer, argumen kedua dan ketiga. Perbandingan ini harus disimpulkan dengan kecerdasan buatan sendiri, berdasarkan pada pemahaman prinsip-prinsip umum pemrograman dan bagaimana bahasa C ++ bekerja. Saya pikir kita harus memikirkan semua ini dengan serius dalam 20 tahun.

Badan fungsi mungkin tersedia, tetapi mungkin tidak ada gunanya dari ini. Mari kita lihat suatu fungsi, seperti memmove . Ini sering diimplementasikan dalam sesuatu seperti ini:

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

Apa itu __builtin___memmove_chk ? Ini adalah fungsi intrinsik yang sudah diimplementasikan oleh kompiler. Fungsi ini tidak memiliki kode sumber.

Atau memmove mungkin terlihat seperti ini: versi perakitan pertama . Anda dapat mengajarkan penganalisa untuk memahami berbagai opsi perakitan, tetapi pendekatan tersebut tampaknya salah.

Oke, terkadang fungsi tubuh benar-benar dikenal. Selain itu, kita juga tahu banyak fungsi dalam kode pengguna. Tampaknya dalam hal ini ML mendapat keuntungan besar dengan membaca dan memahami apa yang semua fungsi ini lakukan.

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

Nuansa-nuansa lain

Ada poin-poin lain yang juga harus diperhitungkan, tetapi kami belum membahasnya secara mendalam. Ngomong-ngomong, artikelnya ternyata cukup panjang. Karena itu, kami akan secara singkat mendaftarkan beberapa nuansa lain, meninggalkannya untuk refleksi pembaca.

  • Outdated recommendations. As mentioned, languages change, and recommendations for their use change, respectively. If the analyzer learns on old source code, it might start issuing outdated recommendations at some point. Example. Formerly, C++ programmers have been recommended using auto_ptr instead of half-done pointers. This smart pointer is now considered obsolete and it is recommended that you use unique_ptr .
  • Data models. At the very least, C and C++ languages have such a thing as a data model . This means that data types have different number of bits across platforms. If you don't take this into account, you can incorrectly teach the analyzer. For example, in Windows 32/64 the long type always has 32 bits. But in Linux, its size will vary and take 32/64 bits depending on the platform's number of bits. Without taking all this into account, the analyzer can learn to miscalculate the size of the types and structures it forms. But the types also align in different ways. All this, of course, can be taken into account. You can teach the analyzer to know about the size of the types, their alignment and mark the projects (indicate how they are building). However, all this is an additional complexity, which is not mentioned in the research articles.
  • Behavioral unambiguousness. Since we're talking about ML, the analysis result is more likely to have probabilistic nature. That is, sometimes the erroneous pattern will be recognized, and sometimes not, depending on how the code is written. From our experience, we know that the user is extremely irritated by the ambiguity of the analyzer's behavior. He wants to know exactly which pattern will be considered erroneous and which will not, and why. In the case of the classical analyzer developing approach, this problem is poorly expressed. Only sometimes we need to explain our clients why there is a/there is no analyzer warning and how the algorithm works, what exceptions are handled in it. Algorithms are clear and everything can always be easily explained. An example of this kind of communication: " False Positives in PVS-Studio: How Deep the Rabbit Hole Goes ". It's not clear how the described problem will be solved in the analyzers built on ML.

Conclusions


Kami tidak menyangkal prospek arahan ML, termasuk penerapannya dalam hal analisis kode statis. ML dapat berpotensi digunakan dalam tugas menemukan kesalahan ketik, saat memfilter positif palsu, saat mencari pola kesalahan baru (belum dijelaskan) dan sebagainya. Namun, kami tidak membagikan optimisme yang menembus artikel yang ditujukan untuk ML dalam hal analisis kode.

Dalam artikel ini, kami telah menguraikan beberapa masalah yang harus dikerjakan seseorang jika ia akan menggunakan ML. Nuansa yang dijelaskan sebagian besar meniadakan manfaat dari pendekatan baru. Selain itu, pendekatan klasik lama dari implementasi analisis lebih menguntungkan dan lebih ekonomis.

Menariknya, artikel penganut metodologi ML tidak menyebutkan perangkap ini. Yah, tidak ada yang baru. ML memprovokasi hype tertentu dan mungkin kita seharusnya tidak mengharapkan penilaian seimbang dari para apologisnya mengenai penerapan ML dalam tugas analisis kode statis.

Dari sudut pandang kami, pembelajaran mesin akan mengisi ceruk dalam teknologi, digunakan dalam analisis statis bersama dengan analisis aliran kontrol, eksekusi simbolis dan lain-lain.

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

PS


Karena artikel ini umumnya kritis, beberapa orang mungkin berpikir bahwa kita takut pada yang baru dan ketika Luddites berbalik melawan ML karena takut kehilangan pasar untuk alat analisis statis.

Luddites


Tidak, kami tidak takut. Kami hanya tidak melihat gunanya menghabiskan 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, dibangun di atas loop dan seandainya :). Lagi pula, kita perlu membuat alat yang efektif, bukan bekerja dari hibah :).

Artikel ini ditulis dengan alasan bahwa semakin banyak pertanyaan diajukan pada topik dan kami ingin memiliki artikel ekspositoris yang meletakkan segala sesuatu di tempatnya.

Terima kasih atas perhatian anda Kami mengundang Anda untuk membaca artikel "Mengapa Anda Harus Memilih Penganalisis Statis PVS-Studio untuk Diintegrasikan ke Dalam Proses Pengembangan Anda . "

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


All Articles