Memeriksa bungkus OpenCvSharp melalui OpenCV dengan PVS-Studio

Gambar 2

OpenCV adalah perpustakaan algoritma penglihatan komputer, pemrosesan gambar dan algoritma numerik untuk keperluan umum dengan open source, yang akrab bagi banyak pengembang C ++. Selain C ++, OpenCV juga sedang dikembangkan untuk Python, Java, Ruby, Matlab, Lua, dan bahasa lainnya. Karena di antara bahasa-bahasa ini tidak ada utama saya, C #, saya memutuskan untuk memperhatikan OpenCvSharp - perpustakaan pembungkus di bawah C # dan memeriksa proyek ini. Apa yang datang dari ini dapat ditemukan di artikel ini.

Pendahuluan


Sebelumnya, sebelum bergabung dengan PVS-Studio, saya terlibat dalam robotika di pameran. Tugas saya termasuk perbaikan paling mendasar (jika kerusakan besar terjadi, maka robot diberikan kepada orang lain), serta pengembangan berbagai macam perangkat lunak dan utilitas.

Gambar 1

Lelah dan baru saja tiba di kota baru, saya bersama dengan robot KIKI yang baru saja dibongkar.

Berbicara tentang pengembangan. Itu sangat lucu. Setiap kali sebuah ide muncul kepada seseorang dari tim, apa lagi yang akan mengejutkan para tamu pameran, kami membawa masalah ini untuk diskusi umum dan, jika ide itu bagus, kami membawanya untuk implementasi. Suatu ketika, ide datang kepada kami untuk membuat makhluk yang merespons wajah manusia dengan ucapan selamat datang.

Setelah mencari pustaka untuk kebutuhan saya di Internet, saya menemukan situs web OpenCV, pustaka algoritma penglihatan komputer. Saya segera kecewa - OpenCV diimplementasikan dalam C ++. Pengetahuan saya tentang manfaat yang saya dapatkan dari kuliah jelas tidak cukup. Oleh karena itu, dengan googling singkat, saya menemukan OpenCvSharp - pembungkus perpustakaan ini di bawah C #, bahasa utama saya. Enam bulan telah berlalu sejak itu, program ini telah ditulis dan digunakan untuk waktu yang lama, dan saya memutuskan untuk masuk ke balik OpenCvSharp dan memeriksa kode sumbernya menggunakan penganalisa statis PVS-Studio.

Proyek yang Diaudit


OpenCvSharp adalah pembungkus OpenCV untuk menggunakan pustaka dalam proyek C #. Pustaka OpenCV, omong-omong, kami juga memeriksa . Keuntungan dari OpenCvSharp termasuk koleksi besar sampel kode, lintas-platform (dapat bekerja pada platform apa pun yang didukung Mono) dan kemudahan instalasi.

Wrapper adalah proyek kecil dan berisi sekitar 112.200 baris kode C #. Dari jumlah tersebut, 1,2% adalah komentar, yang, bagaimanapun, entah bagaimana kecilnya mencurigakan. Tetapi untuk proyek sekecil itu ada banyak kesalahan. Dalam artikel itu, saya menulis lebih dari 20 kesalahan, tetapi ada beberapa kesalahan yang tidak begitu menarik atau jelas.

Penganalisa kode PVS-Studio


PVS-Studio adalah alat untuk mendeteksi kesalahan dan kerentanan potensial dalam kode sumber program yang ditulis dalam C, C ++, C # dan Java. Berjalan di Windows, Linux, dan macOS. Selain kode, kesalahan, dan kesalahan ketik yang tidak dapat dicapai, PVS-Studio dapat mendeteksi kerentanan potensial, seperti disebutkan di atas. Oleh karena itu, dapat dianggap sebagai alat untuk pengujian keamanan aplikasi statis (Static Application Security Testing, SAST).

Potongan kode yang menarik perhatian saat memeriksa laporan penganalisa


Metode WriteableBitmapConverter menarik perhatian segera dengan empat jenis peringatan PVS-Studio yang sama:

  • V3005 Variabel 'optimumChannels [PixelFormats.Indexed1]' ditugaskan untuk dirinya sendiri. WriteableBitmapConverter.cs 22
  • V3005 Variabel 'optimumChannels [PixelFormats.Indexed8]' ditugaskan untuk dirinya sendiri. WriteableBitmapConverter.cs 23
  • V3005 Variabel 'optimumTypes [PixelFormats.Indexed1]' ditugaskan untuk dirinya sendiri. WriteableBitmapConverter.cs 50
  • V3005 Variabel 'optimumTypes [PixelFormats.Indexed8]' ditugaskan untuk dirinya sendiri. WriteableBitmapConverter.cs 51

static WriteableBitmapConverter() { optimumChannels = new Dictionary <PixelFormat, int>(); optimumChannels[PixelFormats.Indexed1] = // <= optimumChannels[PixelFormats.Indexed8] = // <= optimumChannels[PixelFormats.Gray2] = optimumChannels[PixelFormats.Gray4] = optimumChannels[PixelFormats.Gray8] = optimumChannels[PixelFormats.Gray16] = optimumChannels[PixelFormats.Gray32Float] = optimumChannels[PixelFormats.Indexed1] = // <= optimumChannels[PixelFormats.Indexed2] = optimumChannels[PixelFormats.Indexed4] = optimumChannels[PixelFormats.Indexed8] = // <= .... optimumTypes = new Dictionary <PixelFormat, MatType>(); optimumTypes[PixelFormats.Indexed1] = // <= optimumTypes[PixelFormats.Indexed8] = // <= optimumTypes[PixelFormats.Gray2] = optimumTypes[PixelFormats.Gray4] = optimumTypes[PixelFormats.Gray8] = optimumTypes[PixelFormats.Indexed1] = // <= optimumTypes[PixelFormats.Indexed2] = optimumTypes[PixelFormats.Indexed4] = optimumTypes[PixelFormats.Indexed8] = // <= optimumTypes[PixelFormats.BlackWhite] = .... } .... public static class PixelFormats { .... public static PixelFormat Indexed8 { get; } .... public static PixelFormat Indexed1 { get; } .... } 

Kelas PixelFormats didefinisikan dalam namespace System.Windows.Media dan merupakan kumpulan berbagai format piksel. Penganalisa menarik perhatian pada fakta bahwa metode WriteableBitmapConverter menugaskan kembali nilai ke optimalChannels [PixelFormats.Indexed1] dan optimumChannels [PixelFormats.Indexed8] elemen , yang tidak masuk akal praktis. Tidak jelas apakah ini salah ketik sederhana atau sesuatu yang lain dimaksudkan. Omong-omong, bagian kode ini dengan jelas menunjukkan manfaat analisis statis. Ketika Anda melihat banyak garis dengan tipe yang sama di depan mata Anda, mata Anda mulai "kabur", dan perhatian Anda menghilang - tidak mengherankan jika bahkan setelah kode meninjau, kesalahan ketik merayap ke dalam program. Dan penganalisa statis tidak memiliki masalah dengan perhatian dan tidak perlu istirahat, oleh karena itu lebih mudah baginya untuk menemukan kesalahan seperti itu.

Gambar 5

Rasakan kekuatan dan kekuatan analisis statis.

Peringatan PVS-Studio : V3021 Ada dua pernyataan 'jika' dengan ekspresi kondisional yang identik. Pernyataan 'jika' pertama berisi pengembalian metode. Ini berarti bahwa pernyataan 'jika' yang kedua adalah InputArray.cs 394 yang tidak masuk akal

 private static MatType EstimateType(Type t) { .... if (t == typeof(Vec2b)) return MatType.CV_8UC2; if (t == typeof(Vec3b)) return MatType.CV_8UC3; if (t == typeof(Vec4b)) return MatType.CV_8UC4; if (t == typeof(Vec6b)) return MatType.CV_8UC(6); if (t == typeof(Vec2s)) // <= return MatType.CV_16SC2; .... if (t == typeof(Vec2s)) // <= return MatType.CV_32SC2; .... } 

Kesalahan ini sedikit seperti yang sebelumnya. Programmer membuat situasi di mana kondisi yang sama diperiksa dua kali. Dalam hal ini, ini tidak masuk akal - maka cabang dari "digandakan" jika pernyataan tidak akan dieksekusi, karena:

  • jika ekspresi kondisional pertama benar, metode akan keluar;
  • jika kondisi pertama salah, yang kedua juga akan salah, karena variabel yang diuji - t - tidak berubah di antara ekspresi kondisional.

Pengembang harus memeriksa ulang bagian kode ini. Sangat mungkin bahwa di tempat variabel kedua Vec2s harus beberapa yang lain.

PVS-Studio Warning : V3010 Nilai balik fungsi 'ToString' harus digunakan. ImgProcTest.cs 80

 public static RectanglesIntersectTypes RotatedRectangleIntersection(RotatedRect rect1, RotatedRect rect2, out Point2f[] intersectingRegion) { using (var intersectingRegionVec = new VectorOfPoint2f()) { int ret = NativeMethods .imgproc_rotatedRectangleIntersection_vector( rect1, rect2, intersectingRegionVec.CvPtr); intersectingRegion = intersectingRegionVec.ToArray(); return (RectanglesIntersectTypes) ret; } } public void RotatedRectangleIntersectionVector() { var rr1 = new RotatedRect(new Point2f(100, 100), new Size2f(100, 100), 45); var rr2 = new RotatedRect(new Point2f(130, 100), new Size2f(100, 100), 0); Cv2.RotatedRectangleIntersection(rr1, rr2, out var intersectingRegion); .... intersectingRegion.ToString(); } 

Metode RotatedRectangleIntersection mengembalikan array elemen Point2f melalui parameter intersectingRegion . Setelah program mengisi intersectingRegion dengan nilai - nilai, metode ToString () dipanggil pada array ini . Dengan elemen-elemen array, tidak ada perubahan yang terjadi dari ini dan tidak ada pekerjaan yang berguna dilakukan di baris terakhir, oleh karena itu ada alasan untuk mencurigai pengembang bahwa ia hanya lupa untuk menghapusnya.

Peringatan PVS-Studio :

  • V3021 Ada dua pernyataan 'jika' dengan ekspresi kondisional yang identik. Pernyataan 'jika' pertama berisi pengembalian metode. Ini berarti bahwa pernyataan kedua 'jika' tidak masuk akal Cv2_calib3d.cs 1370
  • Ekspresi V3022 'objectPoints == null' selalu salah. Cv2_calib3d.cs 1372

 public static double CalibrateCamera(....) { if (objectPoints == null) throw new ArgumentNullException(nameof(objectPoints)); if (objectPoints == null) throw new ArgumentNullException(nameof(objectPoints)); .... } 

Dalam kasus ini, sebuah fragmen kode digandakan, karena itu muncul dua peringatan. Peringatan pertama menunjukkan bahwa kedua pernyataan tersebut memiliki ekspresi kondisional yang sama. Jika ungkapan ini benar, metode akan keluar dari cabang kemudian dari pernyataan if pertama. Karena alasan ini, kondisi kedua akan selalu salah, seperti yang ditunjukkan oleh peringatan berikut. Rupanya, teks itu disalin, tetapi lupa untuk memperbaiki.

Gambar 6

Lucu Salin-Tempel.

Peringatan penganalisa serupa lainnya:

  • V3021 Ada dua pernyataan 'jika' dengan ekspresi kondisional yang identik. Pernyataan 'jika' pertama berisi pengembalian metode. Ini berarti bahwa pernyataan kedua 'jika' tidak masuk akal Cv2_calib3d.cs 1444
  • Ekspresi V3022 'objectPoints == null' selalu salah. Cv2_calib3d.cs 1446

Peringatan PVS-Studio : Ekspresi V3022 'label == MarkerValue' selalu salah. Labeller.cs 135

 internal static class Labeller { .... private const int MarkerValue = -1; public static int Perform(Mat img, CvBlobs blobs) { .... int label = 0; int lastLabel = 0; CvBlob lastBlob = null; for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { if (imgIn[x + y * step] == 0) continue; bool labeled = labels[y, x] != 0; if (....) { labeled = true; // Label contour. label++; if (label == MarkerValue) // <= throw new Exception(); .... } .... } .... } } } 

Di bagian kode ini, variabel label nol dibuat. Ketika suatu kondisi terpenuhi, seseorang dapat ditambahkan ke variabel ini. Dalam hal ini, dalam kode, nilai label variabel tidak berubah ke bawah. Pada baris yang ditandai dengan panah, variabel ini dibandingkan dengan konstanta sama dengan -1, yang tidak masuk akal secara praktis.

PVS-Studio Warning : V3038 Argumen dilewatkan ke metode beberapa kali. Ada kemungkinan bahwa argumen lain harus disahkan. Cv2_photo.cs 124

 public static void FastNlMeansDenoisingMulti(....) { .... NativeMethods.photo_fastNlMeansDenoisingMulti( srcImgPtrs, srcImgPtrs.Length, dst.CvPtr, imgToDenoiseIndex, templateWindowSize, h, templateWindowSize, searchWindowSize); .... } 

Untuk memahami apa artinya penganalisa, mari kita lihat parameter dari metode photo_fastNlMeansDenoisingMulti :

 public static extern void photo_fastNlMeansDenoisingMulti( IntPtr[] srcImgs, int srcImgsLength, IntPtr dst, int imgToDenoiseIndex, int temporalWindowSize, float h, int templateWindowSize, int searchWindowSize) 

Sederhanakan bahkan lebih untuk membuatnya sangat jelas. Bandingkan garis-garis ini:

 NativeMethods.photo_fastNlMeansDenoisingMulti( .... templateWindowSize, .... templateWindowSize, ....); public static extern void photo_fastNlMeansDenoisingMulti( .... int temporalWindowSize, .... int templateWindowSize, ....) 

Penganalisa menarik perhatian pada fakta bahwa pengembang menggunakan variabel templatWindowSize dua kali, meskipun kemungkinan besar, temporalWindowSize harus menggantikan variabel pertama yang disebutkan. Juga mencurigakan bahwa nilai temporalWindowSize dalam metode photo_fastNlMeansDenoisingMulti tidak digunakan sama sekali. Mungkin ini dilakukan dengan sengaja, tetapi sebagai gantinya pengembang perlu melihat kode ini lebih dekat, apakah ada kesalahan yang terjadi di sana?

Peringatan penganalisa serupa:

  • V3038 Argumen diteruskan ke metode beberapa kali. Ada kemungkinan bahwa argumen lain harus disahkan. Cv2_photo.cs 149
  • V3038 Argumen diteruskan ke metode beberapa kali. Ada kemungkinan bahwa argumen lain harus disahkan. Cv2_photo.cs 180
  • V3038 Argumen diteruskan ke metode beberapa kali. Ada kemungkinan bahwa argumen lain harus disahkan. Cv2_photo.cs 205

Kesalahan berikut akan sedikit mirip dengan yang sebelumnya.

Peringatan PVS-Studio : V3066 Kemungkinan urutan argumen yang salah diteruskan ke metode 'calib3d_Rodrigues_MatToVec': 'matrixM.CvPtr' dan 'vectorM.CvPtr'. Cv2_calib3d.cs 86

 public static void Rodrigues(double[,] matrix, out double[] vector, out double[,] jacobian) { .... using (var jacobianM = new Mat<double>()) { NativeMethods.calib3d_Rodrigues_MatToVec (matrixM.CvPtr, vectorM.CvPtr, jacobianM.CvPtr); .... } } 

Mari kita lihat parameter calib3d_Rodrigues_MatToVec

 public static extern void calib3d_Rodrigues_MatToVec( IntPtr vector, IntPtr matrix, IntPtr jacobian) 

Mungkin ketika memanggil metode calib3d_Rodrigues_MatToVec , argumen matrixM.CvPtr dan vectorM.CvPtr dicampuradukkan. Pengembang harus melihat lebih dekat pada kode ini. Ada kemungkinan kesalahan menyusup ke dalamnya yang mengganggu perhitungan yang benar.

Peringatan PVS-Studio : V3063 Bagian dari ekspresi kondisional selalu salah jika dievaluasi: data == null. Mat.cs 3539

 private void CheckArgumentsForConvert(....) { .... if (data == null) throw new ArgumentNullException(nameof(data)); MatType t = Type(); if (data == null || (data.Length * dataDimension) // <= (data.Length * dataDimension) % t.Channels != 0) .... } 

Alat analisis menunjukkan bahwa data pemeriksaan kedua == null tidak akan pernah benar , karena jika dalam kondisi data pertama adalah nol , pengecualian akan dilemparkan, dan eksekusi program tidak akan mencapai pemeriksaan kedua.

Gambar 7

Saya mengerti bahwa Anda sudah lelah, tetapi sangat sedikit yang tersisa.

Peringatan PVS-Studio : V3127 Ditemukan dua fragmen kode serupa. Mungkin, ini adalah kesalahan ketik dan variabel 'jendela' harus digunakan alih-alih 'src2' Cv2_imgproc.cs 1547

 public static Point2d PhaseCorrelateRes(....) { if (src1 == null) throw new ArgumentNullException(nameof(src1)); if (src2 == null) throw new ArgumentNullException(nameof(src2)); if (window == null) throw new ArgumentNullException(nameof(src2)); // <= .... } 

Dan kemudian penganalisa menemukan kesalahan ketik. Di bagian kode ini, nilai variabel diperiksa untuk nol , dan jika kondisi ini terpenuhi, pengecualian dilemparkan untuk masing-masing variabel. Namun, variabel jendela tidak begitu sederhana. Jika variabel ini nol , maka pengecualian juga dihasilkan untuk itu, tetapi teks dari pengecualian ini dieja salah. Jendela variabel itu sendiri tidak muncul dalam teks pengecualian ini, melainkan, src2 ditunjukkan di sana . Rupanya, kondisi terakhir harus seperti ini:

 if (window == null) throw new ArgumentNullException(nameof(window)); 

Peringatan PVS-Studio : V3142 Kode yang tidak dapat dideteksi terdeteksi. Mungkin saja ada kesalahan. MatOfT.cs 873

Sekarang, untuk perubahan, mari kita lihat kasus ketika alat analisa ini benar-benar benar ketika melaporkan kode yang tidak dapat dijangkau, tetapi tidak ada kesalahan. Ini adalah kasus ketika dapat dikatakan bahwa penganalisa menghasilkan peringatan yang benar dan salah pada saat yang sama.

 public new Mat<TElem> SubMat(params Range[] ranges) { Mat result = base.SubMat(ranges); return Wrap(result); } 

Penganalisa mengklaim bahwa pernyataan pengembalian tidak dapat dijangkau di sini. Untuk memeriksanya, lihat isi dari metode SubMat .

 public Mat SubMat(params Range[] ranges) { throw new NotImplementedException(); /* if (ranges == null) throw new ArgumentNullException(); ThrowIfDisposed(); CvSlice[] slices = new CvSlice[ranges.Length]; for (int i = 0; i < ranges.Length; i++) { slices[i] = ranges[i]; } IntPtr retPtr = NativeMethods.core_Mat_subMat1(ptr, ranges.Length, ranges); Mat retVal = new Mat(retPtr); return retVal;*/ } 

Seperti yang Anda lihat, fungsi belum ditambahkan dan selalu melempar pengecualian. Dan penganalisa itu benar ketika melaporkan kode yang tidak terjangkau. Tapi ini tidak bisa disebut kesalahan nyata.

Tiga kesalahan berikut yang ditemukan oleh penganalisa adalah dari jenis yang sama, tetapi mereka sangat keren sehingga saya tidak dapat membantu menuliskan semuanya.

Peringatan PVS-Studio : Ekspresi V3022 'String.IsNullOrEmpty ("winName")' selalu salah. Cv2_highgui.cs 46

 public static void DestroyWindow(string winName) { if (String.IsNullOrEmpty("winName")) .... } 

Peringatan PVS-Studio : Ekspresi V3022 'string.IsNullOrEmpty ("fileName")' selalu salah. FrameSource.cs 37

 public static FrameSource CreateFrameSource_Video(string fileName) { if (string.IsNullOrEmpty("fileName")) .... } 

Peringatan PVS-Studio : Ekspresi V3022 'string.IsNullOrEmpty ("fileName")' selalu salah. FrameSource.cs 53

 public static FrameSource CreateFrameSource_Video_CUDA(string fileName) { if (string.IsNullOrEmpty("fileName")) .... } 

Kadang-kadang di balik peringatan penganalisis V3022 (ungkapan selalu benar / salah) ada hal-hal aneh atau lucu. Dalam ketiga kasus, situasi yang sama diamati. Kode metode memiliki parameter tipe string , yang nilainya harus diperiksa. Namun, itu bukan nilai dari variabel yang diperiksa, tetapi string literal dengan namanya, yaitu. nama dikutip sia-sia.

Gambar 18

Rupanya, pengembang disegel sekali dan, menggunakan copy-paste, menyebarkan kesalahan ini dengan kode.

Kesimpulan


Pengembang OpenCvSharp telah melakukan pekerjaan penting dan hebat. Dan saya, sebagai pengguna perpustakaan ini, sangat berterima kasih kepada mereka. Terima kasih

Namun, berada di tim PVS-Studio dan melihat kode, saya harus mengakui bahwa masalah kualitasnya belum cukup berhasil. Kemungkinan besar, penganalisa kode statis tidak digunakan secara teratur dalam proyek ini. Dan banyak kesalahan diperbaiki dengan metode yang lebih mahal (pengujian, menurut ulasan pengguna, misalnya). Dan beberapa umumnya tetap hidup untuk waktu yang lama dalam kode, dan kami hanya menemukannya. Gagasan ini disajikan secara lebih rinci dalam catatan singkat tentang topik filosofi menggunakan metodologi analisis statis.

Karena proyek ini terbuka dan berlokasi di GitHub, pengembangnya memiliki kesempatan untuk mengambil keuntungan dari opsi lisensi gratis PVS-Studio dan mulai menerapkan analisis secara teratur.

Terima kasih atas perhatian anda Unduh dan uji proyek Anda menggunakan versi uji coba PVS-Studio.



Jika Anda ingin berbagi artikel ini dengan audiens yang berbahasa Inggris, silakan gunakan tautan ke terjemahan: Ekaterina Nikiforova. Memeriksa OpenCvSharp Wrapper untuk OpenCV dengan PVS-Studio .

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


All Articles