SARIF SDK dan Kesalahannya

Gambar 2

Hari ini kami memiliki proyek Microsoft lain yang berkualitas tinggi untuk diperiksa, yang dengan gagah hati kami akan mencoba mencari kesalahan dengan PVS-Studio. SARIF, akronim untuk Format Interchange Analisis Static, yang merupakan standar (format file), dirancang untuk berinteraksi dan berbagi hasil analisis statis dengan alat lain: IDE, verifikasi kode kompleks dan alat analisis (misalnya SonarQube), sistem integrasi berkelanjutan, dll. SARIF SDK, masing-masing, berisi alat pengembang .NET untuk mendukung SARIF serta file tambahan.

SARIF berasal dari Microsoft dan sekarang merupakan standar yang dikembangkan oleh OASIS (konsorsium nirlaba yang berhubungan dengan standar terbuka). SARIF dimaksudkan untuk memberikan tidak hanya hasil analisa, tetapi juga metadata tentang alat, serta data tentang bagaimana alat itu diluncurkan, label waktu, dan sebagainya. Untuk informasi lebih lanjut, kunjungi situs web OASIS . Kode sumber SARIF SDK dapat diunduh dari repositori di GiHub . Beranda proyek tersedia melalui tautan .

Tentang proyek


Proyek SARIF SDK ternyata berukuran kecil: 799 file .cs (sekitar 98.000 baris kode tidak kosong). Proyek ini berisi tes yang selalu saya kecualikan dari cek. Dengan demikian, bagian dari kode yang kami minati adalah 642 file .cs (sekitar 79.000 baris kode tidak kosong). Itu tentu saja tidak cukup. Di sisi positifnya, pemeriksaan dan analisisnya mudah dan cepat, antara ini dan kemudian, yang saya coba renungkan pada gambar di awal. Meskipun demikian, saya berhasil melacak beberapa kasus luar biasa. Mari kita 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 diinisialisasi dengan nilai bidang lain, yang belum menerima nilai. Akibatnya, Default akan menerima nilai nol secara default untuk tipe string . Kemungkinan besar, kesalahan itu tetap tidak diperhatikan, karena bidang Default tidak digunakan di mana pun. Tetapi berbagai hal dapat berubah, dan kemudian pengembang akan menghadapi hasil yang tidak semestinya atau program macet.

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); .... } 

Penulis kode tidak menggunakan parameter logicalLocationToIndexMap dengan cara apa pun, tetapi menulis nilai yang berbeda di dalamnya. Anehnya, nilai sebelumnya persis sama dengan kamus kosong, dibuat dalam kode pemanggil:

 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. Baik saat membuat objek Alat dan saat menulis nilai di properti Alat , tidak ada pekerjaan tambahan yang diperlukan. Karena itu, pengalihan bau amis.

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 nilai variabel loc adalah null , akan dilakukan upaya untuk mengembalikan nilai dari bagian kanan ?? operator, menghasilkan akses dengan 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; .... } 

Pengembang menggunakan opsi akses tidak aman dan aman dengan referensi formatString yang berpotensi nol di dua cabang paralel dari persyaratan: Operator.

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 analis mengeluarkan dua peringatan tentang kemungkinan akses dengan referensi messageText nol. Itu terlihat agak tidak mengancam, tapi itu masih 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)) { .... } .... } 

Analisator curiga bahwa NullReferenceException dimungkinkan ketika bekerja dengan referensi fileDataVersionOne.Uri . Mari kita lihat dari mana variabel ini berasal dan cari tahu apakah alat analisisnya benar. Untuk melakukan ini, mari kita melihat dekat tubuh dari 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 dari kelas FileDataVersionOne , properti Uri mungkin menerima nilai nol . Ini adalah contoh yang bagus analisis aliran data dan mekanisme analisis antarproedural yang bekerja bersama.

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(); } // <= } .... } 

Ada kesalahan ketik dalam fragmen ini. Jelas bahwa _jsonTextWriter! = Null harus dalam kondisi blok kedua. Sepotong kode ini membahayakan karena, kemungkinan besar, itu tidak macet, karena _jsonTextWriter menjadi nonnull . Selain itu, alirannya 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(....); } .... } 

Acara ditangani dengan tidak aman. Ini adalah bug tidak kritis yang dapat dengan mudah diperbaiki, misalnya, dengan mengikuti tip Visual Studio. Inilah penggantian yang disarankan oleh IDE:

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

Hanya perlu beberapa detik untuk memperbaikinya, tetapi penganalisa tidak akan lagi mengeluh tentang hal itu dan IDE tidak akan 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 berpikir bahwa referensi v1Location mungkin nol dan menambahkan cek yang sesuai. Sedangkan di atas kita dapat melihat bahwa referensi ini ditangani tanpa pemeriksaan. Refactoring lalai? Anda tidak pernah tahu.

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; } 

Seperti biasa, inilah kasus sebaliknya. Pertama referensi v1StackFrame diperiksa untuk null , dan kemudian cek hilang. Tetapi kasus ini memiliki peringatan penting: variabel v1StackFrame dan stackFrame terkait secara logis. Lihat, jika v1StackFrame adalah null , objek StackFrame tidak akan dibuat, sedangkan stackFrame akan tetap nol. Diikuti oleh crash program karena panggilan stackFrame.Location , karena tidak ada pemeriksaan di sini. Jadi bahkan tidak akan sampai pada penggunaan v1StackFrame yang berbahaya, ditunjukkan oleh penganalisa. Kode ini hanya berfungsi jika Anda meneruskan nilai v1StackFrame nonnull ke metode CreateStackFrame . Saya menduga bahwa kode penelepon entah bagaimana mengendalikannya. Panggilan CreateStackFrame terlihat seperti ini:

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

CreateStackFrame digunakan sebagai pemilih. Referensi yang lulus tidak dicentang untuk nol di sini. Mungkin, ketika mengisi koleksi Frames itu (penulisan referensi nol) dikontrol, tapi saya tidak pergi untuk menggali terlalu dalam. Kesimpulannya sudah jelas - kode membutuhkan perhatian penulis.

Kesimpulan


Seperti yang Anda lihat, artikelnya tidak lama tetapi saya harap Anda menikmati bacaan ringan ini :) Untuk berjaga-jaga, Anda selalu dapat mengunduh penganalisa kami untuk mencari kesalahan dalam proyek Anda atau seseorang sendiri.

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

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

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


All Articles