
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 { ....
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 (....) {
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: