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