SARIF SDK dan kesalahannya

Gambar 2


Hari ini, kami sedang menguji proyek Microsoft lainnya yang berkualitas tinggi, di mana kami masih berusaha mencari kesalahan dengan menggunakan PVS-Studio. SARIF adalah singkatan dari Format Interchange Hasil Analisis Statis. Ini adalah standar (format file) yang dimaksudkan untuk interaksi dan pertukaran hasil analisis statis dengan alat lain: IDE, verifikasi kode komprehensif dan alat analisis (misalnya, SonarQube), sistem integrasi berkelanjutan dll. SARIF SDK, masing-masing, berisi alat pengembang .NET untuk mendukung SARIF, serta file pendukung.
SARIF berasal dari Microsoft dan sekarang merupakan standar yang dikembangkan oleh OASIS (konsorsium nirlaba yang bergerak di bidang standar terbuka). SARIF dirancang untuk mengirimkan tidak hanya hasil analisa, tetapi juga metadata tentang alat, serta data tentang bagaimana alat itu diluncurkan, stempel waktu, dan sebagainya. Rincian lebih lanjut tentang standar ini dapat ditemukan di situs web OASIS . Kode sumber SARIF SDK dapat diunduh dari repositori di GiHub . Halaman beranda proyek tersedia di sini .

Tentang proyek


Proyek SARIF SDK kecil: 799 file .cs (sekitar 98.000 baris kode tidak kosong). Proyek ini berisi tes, yang selalu saya kecualikan dari verifikasi. Dengan demikian, bagian dari kode yang menarik bagi kami adalah 642 file .cs (sekitar 79.000 baris kode tidak kosong). Ini tentu saja tidak cukup. Tetapi verifikasi dan analisisnya cepat dan mudah, di antara hal-hal yang saya coba renungkan dalam gambar di awal artikel. Namun demikian, beberapa kesalahan menarik ditemukan. Mari lihat mereka.

Kesalahan


V3070 [CWE-457] Variabel 'Biner' yang tidak diinisialisasi digunakan ketika menginisialisasi variabel 'Default'. MimeType.cs 90

public static class MimeType { .... /// <summary>The MIME type to use when no better MIME type is known.</summary> public static readonly string Default = Binary; .... /// <summary>The MIME type for binaries.</summary> public static readonly string Binary = "application/octet-stream"; .... } 

Bidang Default diinisialisasi dengan nilai bidang lain yang belum menerima nilai. Akibatnya, Default akan menerima nilai default untuk tipe string - null. Mungkin, kesalahan tidak diperhatikan, karena bidang Default tidak digunakan di mana pun. Tapi semuanya bisa berubah, dan kemudian pengembang akan menemui hasil yang tidak terduga atau crash program.

V3061 Parameter 'logicalLocationToIndexMap' selalu ditulis ulang dalam tubuh metode sebelum digunakan. PrereleaseCompatibilityTransformer.cs 1963

 private static JArray ConvertLogicalLocationsDictionaryToArray( .... Dictionary<LogicalLocation, int> logicalLocationToIndexMap, ....) { .... logicalLocationToIndexMap = new Dictionary<LogicalLocation, int>(LogicalLocation.ValueComparer); .... } 

Parameter logicalLocationToIndexMap tidak digunakan dengan cara apa pun, menulis nilai lain untuk itu. Anehnya, nilai lama adalah kamus kosong yang sama persis yang dibuat dalam metode panggilan:

 private static bool ApplyChangesFromTC25ThroughTC30(....) { .... Dictionary<LogicalLocation, int> logicalLocationToIndexMap = null; .... logicalLocationToIndexMap = new Dictionary<LogicalLocation, int>(LogicalLocation.ValueComparer); run["logicalLocations"] = ConvertLogicalLocationsDictionaryToArray( ...., logicalLocationToIndexMap, ....); } 

Kode aneh dan mencurigakan.

V3008 [CWE-563] Variabel 'run.Tool' diberi nilai dua kali berturut-turut. Mungkin ini sebuah kesalahan. Periksa baris: 116, 114. ExportRulesMetadataCommandBase.cs 116

 public partial class Run { .... public Tool Tool { get; set; } .... } public partial class Tool : .... { .... public Tool() { } .... } private void OutputSarifRulesMetada(....) { .... var run = new Run(); run.Tool = new Tool(); run.Tool = Tool.CreateFromAssemblyData(....); // <= .... } 

Properti run.Tool diberi nilai dua kali. Seperti saat membuat objek Alat , dan saat menulis ke properti Alat , tidak ada pekerjaan tambahan yang dilakukan. Karena itu, penugasan kembali terlihat mencurigakan.

V3042 [CWE-476] Kemungkinan NullReferenceException. '?.' dan '.' operator digunakan untuk mengakses anggota objek 'loc' WhereComparer.cs 152

 private static Uri ArtifactUri(ArtifactLocation loc, Run run) { return loc?.Uri ?? loc.Resolve(run)?.Uri; } 

Jika variabel loc ternyata nol , upaya akan dilakukan untuk mengembalikan nilai dari sisi kanan operator, yang akan menghasilkan akses oleh referensi nol.

V3042 [CWE-476] Kemungkinan NullReferenceException. '?.' dan '.' operator digunakan untuk mengakses anggota objek 'formatString' InsertOptionalDataVisitor.cs 194

 public override Message VisitMessage(Message node) { .... node.Text = node.Arguments?.Count > 0 ? string.Format(...., formatString.Text, ....) : formatString?.Text; .... } 

Dalam dua cabang paralel dari pernyataan bersyarat ?: Mereka menggunakan opsi akses yang tidak aman dan aman menggunakan referensi formatString yang berpotensi nol.

V3042 [CWE-476] Kemungkinan NullReferenceException. '?.' dan '.' operator digunakan untuk mengakses anggota objek 'messageText' FortifyFprConverter.cs 1210

V3042 [CWE-476] Kemungkinan NullReferenceException. '?.' dan '.' operator digunakan untuk mengakses anggota objek 'messageText' FortifyFprConverter.cs 1216

 private void AddMessagesToResult(Result result) { .... string messageText = (rule.ShortDescription ?? rule.FullDescription)?.Text; .... if (....) { // Replace the token with an embedded hyperlink. messageText = messageText.Replace(....); } else { // Replace the token with plain text. messageText = messageText.Replace(....); } .... } 

Di sini, penganalisa segera mengeluarkan dua peringatan tentang kemungkinan akses melalui messageText null reference. Itu terlihat sepele, tetapi kesalahan dari ini tidak berhenti menjadi kesalahan.

V3080 [CWE-476] Kemungkinan null dereference. Pertimbangkan untuk memeriksa 'fileDataVersionOne.Uri'. SarifCurrentToVersionOneVisitor.cs 1030

 private IDictionary<string, FileDataVersionOne> CreateFileDataVersionOneDictionary() { .... FileDataVersionOne fileDataVersionOne = CreateFileDataVersionOne(v2File); if (fileDataVersionOne.Uri.OriginalString.Equals(key)) { .... } .... } 

Penganalisa curiga bahwa NullReferenceException dimungkinkan ketika bekerja dengan tautan fileDataVersionOne.Uri . Mari kita lihat dari mana variabel ini berasal, dan apakah alat analisisnya benar. Untuk melakukan ini, periksa tubuh metode CreateFileDataVersionOne :

 private FileDataVersionOne CreateFileDataVersionOne(Artifact v2FileData) { FileDataVersionOne fileData = null; if (v2FileData != null) { .... fileData = new FileDataVersionOne { .... Uri = v2FileData.Location?.Uri, .... }; .... } return fileData; } public partial class FileDataVersionOne { .... public Uri Uri { get; set; } .... } 

Memang, saat membuat objek FileDataVersionOne kelas , properti Uri dapat diatur ke nol . Contoh yang baik dari kolaborasi analisis aliran data dan mekanisme analisis antarproedural.

V3080 [CWE-476] Kemungkinan null dereference. Pertimbangkan untuk memeriksa '_jsonTextWriter'. SarifLogger.cs 242

 public virtual void Dispose() { .... if (_closeWriterOnDispose) { if (_textWriter != null) { _textWriter.Dispose(); } if (_jsonTextWriter == null) { _jsonTextWriter.Close(); } // <= } .... } 

Ini salah ketik. Jelas, kondisi blok if kedua harus _jsonTextWriter! = Null . Bahayanya kode ini adalah kemungkinan besar tidak macet, karena _jsonTextWriter tidak sepenuhnya nol . Tetapi pada saat yang sama, aliran tetap terbuka.

V3083 [CWE-367] Doa yang tidak aman dari acara 'RuleRead', NullReferenceException dimungkinkan. Pertimbangkan menugaskan acara ke variabel lokal sebelum menjalankannya. FxCopConverter.cs 897

 private void ReadRule(....) { .... if (RuleRead != null) { RuleRead(....); } .... } 

Mereka tidak bekerja dengan acara tersebut dengan aman. Kesalahan non-kritis yang dapat dengan mudah diperbaiki, misalnya, mengikuti permintaan Visual Studio. Berikut adalah penawaran IDE pengganti:

 private void ReadRule(....) { .... RuleRead?.Invoke(....); .... } 

Ini berfungsi selama beberapa detik, tetapi alat analisa tidak lagi bersumpah, dan IDE tidak menyoroti kode. Kesalahan serupa lainnya:

  • V3083 [CWE-367] Doa yang tidak aman dari acara 'ResultRead', NullReferenceException dimungkinkan. Pertimbangkan menugaskan acara ke variabel lokal sebelum menjalankannya. FxCopConverter.cs 813

V3095 [CWE-476] Objek 'v1Location' digunakan sebelum diverifikasi terhadap null. Periksa baris: 333, 335. SarifVersionOneToCurrentVisitor.cs 333

 internal Location CreateLocation(LocationVersionOne v1Location) { .... string key = v1Location.LogicalLocationKey ?? v1Location.FullyQualifiedLogicalName; if (v1Location != null) { .... } .... } 

Penulis kode menyarankan bahwa tautan v1Location mungkin nol, jadi ia menambahkan tanda centang yang sesuai. Pada saat yang sama, dalam kode sedikit lebih tinggi, karena alasan tertentu ia berfungsi dengan tautan ini tanpa pemeriksaan. Kurang perhatian saat refactoring? Sulit dikatakan.

V3125 [CWE-476] Objek 'v1StackFrame' digunakan setelah diverifikasi terhadap nol. Periksa baris: 1182, 1171. SarifVersionOneToCurrentVisitor.cs 1182

 internal StackFrame CreateStackFrame(StackFrameVersionOne v1StackFrame) { StackFrame stackFrame = null; if (v1StackFrame != null) { stackFrame = new StackFrame { .... }; } stackFrame.Location = CreateLocation(v1StackFrame.FullyQualifiedLogicalName, v1StackFrame.LogicalLocationKey, ....); return stackFrame; } 

Secara tradisional, yang terjadi adalah sebaliknya. Pertama, tautan v1StackFrame diperiksa nol , dan kemudian mereka lupa untuk memeriksanya. Tetapi ada satu peringatan penting: variabel v1StackFrame dan stackFrame terkait secara logis. Lihat, jika v1StackFrame adalah = null , maka objek StackFrame tidak akan dibuat, dan stackFrame akan tetap = null . Dalam hal ini, program akan menerima panggilan ke stackFrame.Location , karena tidak ada pemeriksaan di sini. Artinya, penggunaan berbahaya v1StackFrame , yang ditunjukkan oleh penganalisa, bahkan tidak akan mencapai titik. Kode ini hanya berfungsi jika nilai v1StackFrame bukan nol dilewatkan ke metode CreateStackFrame . Saya menduga bahwa ini entah bagaimana dikendalikan dalam kode panggilan. Panggilan CreateStackFrame terlihat seperti ini:

 Frames = v1Stack.Frames?.Select(CreateStackFrame).ToList() 

CreateStackFrame digunakan sebagai pemilih. Tidak ada verifikasi referensi kesetaraan nol yang disahkan. Mungkin di suatu tempat ketika mengisi koleksi Frames ini (merekam nol referensi) dikontrol, tapi saya tidak menggali sejauh itu. Kesimpulannya sudah jelas - kode membutuhkan perhatian penulis.

Kesimpulan


Artikel itu ternyata kecil, tetapi tidak ada yang bosan :) saya mengingatkan Anda bahwa Anda selalu dapat mengunduh penganalisis kami untuk mencari kesalahan di proyek Anda sendiri atau orang lain.

Dan akhirnya, sebuah pengumuman kecil: artikel saya berikutnya adalah tentang kesalahan paling menarik yang saya dan kolega saya temukan di proyek C # pada tahun 2019. Ikuti blog kami. Sampai ketemu lagi!

Untuk mempelajari tentang posting blog baru, Anda dapat berlangganan saluran berikut:




Jika Anda ingin berbagi artikel ini dengan audiens yang berbahasa Inggris, silakan gunakan tautan ke terjemahan: Sergey Khrenov. SARIF SDK dan Kesalahannya .

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


All Articles