PVS-Studio pergi ke awan: CircleCI

Gambar 2

Kami melanjutkan serangkaian artikel tentang penggunaan analisa statis PVS-Studio dalam sistem cloud CI. Hari ini kami sedang mempertimbangkan layanan lain - CircleCI. Kali ini, pemutar media Kodi akan bertindak sebagai proyek untuk analisis, dalam kode sumber yang akan kami coba temukan tempat-tempat menarik.

Catatan Artikel lain tentang pengintegrasian PVS-Studio ke dalam sistem cloud CI dapat ditemukan di sini:


Sebelum kita melanjutkan langsung ke pengaturan dan analisis peringatan penganalisa, izinkan kami mengatakan beberapa kata tentang perangkat lunak yang digunakan dan dianalisis.

CircleCI adalah layanan CI berbasis cloud untuk mengotomatisasi perakitan, pengujian, dan penerbitan perangkat lunak. Ini mendukung perakitan proyek baik dalam wadah dan di mesin virtual yang menjalankan Windows, Linux dan macOS.

Kodi adalah media player lintas platform gratis, open source. Memungkinkan Anda memutar file audio dan video yang terletak di komputer di rumah dan di jaringan lokal atau Internet. Ini mendukung tema dan fungsionalitas dengan menginstal plugin. Tersedia untuk Windows, Linux, macOS dan Android.

PVS-Studio adalah penganalisa kode statis untuk mencari kesalahan dan potensi kerentanan dalam kode yang ditulis dalam C, C ++, C # dan Java. Bekerja di bawah Windows, Linux dan macOS.

Kustomisasi


Buka halaman beranda CircleCI dan klik tombol "Daftar"

Gambar 1

Pada halaman berikutnya, kami akan diminta untuk mengautentikasi dengan akun GitHub atau Bitbucket. Pilih GitHub dan buka halaman otorisasi aplikasi CircleCI.

Gambar 3

Kami mengotorisasi aplikasi (tombol hijau "Otorisasi circleci") dan mengarahkan kami ke halaman pembuka "Selamat datang di CircleCI!"

Gambar 4

Di halaman ini, kita dapat segera mengkonfigurasi proyek mana yang akan dirakit di CircleCI. Kami menandai repositori kami dan klik "Ikuti".

Setelah menambahkan repositori, CircleCI akan secara otomatis memulai build, tetapi sejak belum ada file konfigurasi di repositori - tugas build akan gagal.

Gambar 5

Sebelum menambahkan file konfigurasi, kami akan menambahkan ke variabel proyek yang berisi data lisensi untuk penganalisa. Untuk melakukan ini, klik pada "Pengaturan" di panel kiri, lalu di grup "ORGANISASI" pilih item "Proyek" dan klik pada roda gigi di sebelah kanan proyek yang kita butuhkan. Jendela pengaturan akan terbuka.

Gambar 6

Kami tertarik pada bagian "Variabel Lingkungan". Kami masuk ke dalamnya dan membuat variabel PVS_USERNAME dan PVS_KEY yang berisi nama pengguna dan kunci lisensi untuk penganalisa.

Gambar 7

Ketika memulai membangun proyek, CircleCI membaca konfigurasi tugas dari file di repositori di sepanjang path .circleci / config.yml. Tambahkan itu.

Pertama, kami menunjukkan gambar mesin virtual di mana penganalisa akan dimulai. Daftar lengkap gambar tersedia di sini .

version: 2 jobs: build: machine: image: ubuntu-1604:201903-01 

Selanjutnya, tambahkan repositori yang diperlukan untuk apt dan menginstal dependensi proyek:

 steps: - checkout - run: sudo -- sh -c " add-apt-repository -y ppa:team-xbmc/xbmc-ppa-build-depends && add-apt-repository -y ppa:wsnipex/vaapi && add-apt-repository -y ppa:pulse-eight/libcec && apt-get update" - run: sudo apt-get install -y automake autopoint build-essential cmake curl default-jre gawk gdb gdc gettext git-core gperf libasound2-dev libass-dev libbluray-dev libbz2-dev libcap-dev libcdio-dev libcec4-dev libcrossguid-dev libcurl3 libcurl4-openssl-dev libdbus-1-dev libegl1-mesa-dev libfmt3-dev libfontconfig-dev libfreetype6-dev libfribidi-dev libfstrcmp-dev libgif-dev libgl1-mesa-dev libglu1-mesa-dev libiso9660-dev libjpeg-dev liblcms2-dev libltdl-dev liblzo2-dev libmicrohttpd-dev libmysqlclient-dev libnfs-dev libpcre3-dev libplist-dev libpng-dev libpulse-dev libsmbclient-dev libsqlite3-dev libssl-dev libtag1-dev libtinyxml-dev libtool libudev-dev libusb-dev libva-dev libvdpau-dev libxml2-dev libxmu-dev libxrandr-dev libxrender-dev libxslt1-dev libxt-dev mesa-utils nasm pmount python-dev python-imaging python-sqlite rapidjson-dev swig unzip uuid-dev yasm zip zlib1g-dev wget 

Tambahkan repositori PVS-Studio dan instal analisa:

 - run: wget -q -O - https://files.viva64.com/etc/pubkey.txt | sudo apt-key add - && sudo wget -O /etc/apt/sources.list.d/viva64.list https://files.viva64.com/etc/viva64.list - run: sudo -- sh -c "apt-get update && apt-get install pvs-studio -y" 

Mari kumpulkan dependensi proyek:

 - run: sudo make -C tools/depends/target/flatbuffers PREFIX=/usr/local 

Hasilkan Makefiles di direktori assembly:

 - run: mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Debug .. 

Langkah selanjutnya adalah mengonfigurasi dan menjalankan analisis statis proyek kami.

Pertama, buat file dengan lisensi analyzer. Perintah kedua memulai kompilasi jejak perakitan proyek.

Setelah penelusuran, kami langsung memulai analisis statis. Saat menggunakan lisensi percobaan, penganalisis harus dimulai dengan parameter:

--disableLicenseExpirationCheck .

Perintah terakhir mengonversi file dengan hasil analisis ke laporan html:

 - run: pvs-studio-analyzer credentials -o PVS.lic ${PVS_USER} ${PVS_KEY} - run: pvs-studio-analyzer trace -- make -j2 -C build/ - run: pvs-studio-analyzer analyze -j2 -l PVS.lic -o PVS-Studio.log --disableLicenseExpirationCheck - run: plog-converter -t html -o PVS-Studio.html PVS-Studio.log 

Setelah menyelesaikan tes, simpan laporan penganalisa:

 - run: mkdir PVS_Result && cp PVS-Studio.* ./PVS_Result/ - store_artifacts: path: ./PVS_Result 

Teks lengkap file .circleci / config.yml:

 version: 2.1 jobs: build: machine: image: ubuntu-1604:201903-01 steps: - checkout - run: sudo -- sh -c " add-apt-repository -y ppa:team-xbmc/xbmc-ppa-build-depends && add-apt-repository -y ppa:wsnipex/vaapi && add-apt-repository -y ppa:pulse-eight/libcec && apt-get update" - run: sudo apt-get install -y automake autopoint build-essential cmake curl default-jre gawk gdb gdc gettext git-core gperf libasound2-dev libass-dev libbluray-dev libbz2-dev libcap-dev libcdio-dev libcec4-dev libcrossguid-dev libcurl3 libcurl4-openssl-dev libdbus-1-dev libegl1-mesa-dev libfmt3-dev libfontconfig-dev libfreetype6-dev libfribidi-dev libfstrcmp-dev libgif-dev libgl1-mesa-dev libglu1-mesa-dev libiso9660-dev libjpeg-dev liblcms2-dev libltdl-dev liblzo2-dev libmicrohttpd-dev libmysqlclient-dev libnfs-dev libpcre3-dev libplist-dev libpng-dev libpulse-dev libsmbclient-dev libsqlite3-dev libssl-dev libtag1-dev libtinyxml-dev libtool libudev-dev libusb-dev libva-dev libvdpau-dev libxml2-dev libxmu-dev libxrandr-dev libxrender-dev libxslt1-dev libxt-dev mesa-utils nasm pmount python-dev python-imaging python-sqlite rapidjson-dev swig unzip uuid-dev yasm zip zlib1g-dev wget - run: wget -q -O - https://files.viva64.com/etc/pubkey.txt | sudo apt-key add – && sudo wget -O /etc/apt/sources.list.d/viva64.list https://files.viva64.com/etc/viva64.list - run: sudo -- sh -c "apt-get update && apt-get install pvs-studio -y" - run: sudo make -C tools/depends/target/flatbuffers PREFIX=/usr/local - run: mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Debug .. - run: pvs-studio-analyzer credentials -o PVS.lic ${PVS_USER} ${PVS_KEY} - run: pvs-studio-analyzer trace -- make -j2 -C build/ - run: pvs-studio-analyzer analyze -j2 -l PVS.lic -o PVS-Studio.log --disableLicenseExpirationCheck - run: plog-converter -t html -o PVS-Studio.html PVS-Studio.log - run: mkdir PVS_Result && cp PVS-Studio.* ./PVS_Result/ - store_artifacts: path: ./PVS_Result 

Kami mengunggah file ke repositori dan CircleCI akan secara otomatis memulai perakitan proyek.

Gambar 12

Setelah tugas selesai, file dengan hasil analisa dapat diunduh pada tab β€œArtefak”.

Gambar 11

Hasil analisis


Nah, sekarang mari kita lihat beberapa peringatan yang dikeluarkan oleh penganalisa selama bekerja.

PVS-Studio Warning : V504 Sangat mungkin bahwa titik koma ';' hilang setelah kata kunci 'kembali'. AdvancedSettings.cpp: 1476

 void CAdvancedSettings::SetExtraArtwork(const TiXmlElement* arttypes, std::vector<std::string>& artworkMap) { if (!arttypes) return artworkMap.clear(); const TiXmlNode* arttype = arttypes->FirstChild("arttype"); .... } 

Dilihat oleh pemformatan kode, logika eksekusi berikut diasumsikan:

  • jika arttypes adalah null pointer, selesaikan eksekusi metode;
  • jika arttypes adalah pointer non-nol, kosongkan vektor artworkMap, dan kemudian lakukan beberapa tindakan lainnya.

Namun, karakter yang hilang ';' melakukan penyesuaian; sebagai hasilnya, logika eksekusi tidak sesuai dengan pemformatan sama sekali. Akibatnya, itu menjadi sebagai berikut:

  • jika arttypes adalah pointer nol, vektor artworkMap dihapus dan metode keluar;
  • jika arttypes adalah pointer non-nol, tindakan lebih lanjut dilakukan, tetapi vektor artworkMap tidak dibersihkan.

Secara umum, sangat tidak mungkin bahwa tidak ada kesalahan. Dan hampir tidak ada orang yang akan menulis ekspresi dalam semangat return artworkMap.clear (); :)

Peringatan PVS-Studio :

  • Ekspresi 'lastsector' V547 selalu salah. udf25.cpp: 636
  • Ekspresi 'lastsector' V547 selalu salah. udf25.cpp: 644
  • V571 Cek berulang. Kondisi 'if (lastsector)' sudah diverifikasi di baris 636. udf25.cpp: 644

 int udf25::UDFGetAVDP( struct avdp_t *avdp) { .... uint32_t lastsector; .... lastsector = 0; // <= .... for(;;) { .... if( lastsector ) { // <= V547 lbnum = lastsector; terminate = 1; } else { //! @todo Find last sector of the disc (this is optional). if( lastsector ) // <= V547 lbnum = lastsector - 256; else return 0; } } .... } 

Perhatikan tempat-tempat yang ditandai dengan // <= . Nilai 0 ditulis ke variabel lastsector , dan kemudian digunakan dua kali sebagai ekspresi kondisional dari pernyataan if . Karena nilai variabel tidak berubah dalam loop atau di antara penugasan ini, maka cabang dari keduanya jika pernyataan tidak akan dieksekusi.

Mungkin benar bahwa fungsi yang diperlukan belum diimplementasikan (perhatikan todo ).

Omong-omong, seperti yang Anda lihat, penganalisa segera mengeluarkan 3 peringatan untuk kode ini. Namun, kadang-kadang, bahkan beberapa peringatan saja tidak cukup, dan pengguna tetap percaya bahwa penganalisisnya keliru ... Seorang kolega menulis lebih banyak tentang ini di artikel " Suatu hari dari dukungan pengguna PVS-Studio " :).

Peringatan PVS-Studio : Ekspresi V547 'values.size ()! = 2' selalu salah. GUIControlSettings.cpp: 1174

 bool CGUIControlRangeSetting::OnClick() { .... std::vector<CVariant> values; SettingConstPtr listDefintion = settingList->GetDefinition(); switch (listDefintion->GetType()) { case SettingType::Integer: values.push_back(m_pSlider-> GetIntValue(CGUISliderControl::RangeSelectorLower)); values.push_back(m_pSlider-> GetIntValue(CGUISliderControl::RangeSelectorUpper)); break; case SettingType::Number: values.push_back(m_pSlider-> GetFloatValue(CGUISliderControl::RangeSelectorLower)); values.push_back(m_pSlider-> GetFloatValue(CGUISliderControl::RangeSelectorUpper)); break; default: return false; } if (values.size() != 2) return false; SetValid(CSettingUtils::SetList(settingList, values)); return IsValid(); } 

Dalam kasus ini, memeriksa values.size ()! = 2 berlebihan, karena hasil dari ekspresi kondisional akan selalu salah . Bahkan, jika eksekusi masuk ke salah satu cabang case dari pernyataan switch , 2 elemen akan ditambahkan ke vektor, dan karena itu kosong, maka ukurannya akan menjadi sama dengan dua; jika tidak (saat menjalankan cabang default ), metode akan keluar.

Peringatan PVS-Studio : Ekspresi V547 'prio == 0x7fffffff' selalu benar. DBusReserve.cpp: 57

 bool CDBusReserve::AcquireDevice(const std::string& device) { .... int prio = INT_MAX; .... res = dbus_bus_request_name( m_conn, service.c_str(), DBUS_NAME_FLAG_DO_NOT_QUEUE | (prio == INT_MAX ? 0 : DBUS_NAME_FLAG_ALLOW_REPLACEMENT), // <= error); .... } 

Variabel prio diinisialisasi dengan nilai INT_MAX , setelah itu juga digunakan dalam operator ternary dalam perbandingan prio == INT_MAX . Namun, antara tempat inisialisasi dan penggunaan, nilainya tidak berubah, oleh karena itu, nilai ekspresi prio == INT_MAX benar , dan operator ternary akan selalu mengembalikan 0.

Peringatan PVS-Studio :

  • V575 Potensi penunjuk nol dilewatkan ke fungsi 'memcpy'. Periksa argumen pertama. Periksa baris: 39, 38. DVDOverlayImage.h: 39
  • V575 Potensi penunjuk nol dilewatkan ke fungsi 'memcpy'. Periksa argumen pertama. Periksa baris: 44, 43. DVDOverlayImage.h: 44

 CDVDOverlayImage(const CDVDOverlayImage& src) : CDVDOverlay(src) { Data = (uint8_t*)malloc(src.linesize * src.height); memcpy(data, src.data, src.linesize * src.height); // <= if(src.palette) { palette = (uint32_t*)malloc(src.palette_colors * 4); memcpy(palette, src.palette, src.palette_colors * 4); // <= } .... } 

Kedua peringatan memiliki pola yang sama - penunjuk yang diperoleh sebagai hasil memanggil fungsi malloc digunakan lebih lanjut dalam fungsi memcpy tanpa terlebih dahulu memeriksa NULL .

Seseorang mungkin ingin menolak peringatan ini dalam kunci berikut: malloc tidak akan pernah mengembalikan null pointer, dan jika ya, maka biarkan aplikasi jatuh lebih baik. Ini adalah topik untuk diskusi panjang, tetapi dengan satu atau lain cara saya mengusulkan untuk membaca catatan rekan saya - " Mengapa penting untuk memeriksa bahwa fungsi malloc kembali ".

Jika diinginkan, Anda dapat mengonfigurasikan penganalisa agar berperilaku sedemikian rupa sehingga tidak menganggap malloc dapat mengembalikan pointer nol - maka tidak akan ada peringatan seperti itu. Baca lebih lanjut tentang ini di sini .

Peringatan PVS-Studio : V522 Mungkin ada referensi 'entri' penunjuk nol potensial. Periksa baris: 985, 981. emu_msvcrt.cpp: 985

 struct dirent *dll_readdir(DIR *dirp) { .... struct dirent *entry = NULL; entry = (dirent*) malloc(sizeof(*entry)); if (dirData->curr_index < dirData->items.Size() + 2) { if (dirData->curr_index == 0) strncpy(entry->d_name, ".\0", 2); .... } 

Situasinya mirip dengan yang dijelaskan di atas. Pointer yang diperoleh sebagai hasil pemanggilan malloc ditulis ke variabel entri , setelah itu digunakan tanpa memeriksa NULL ( entry-> d_name ).

Peringatan PVS-Studio : V773 Ruang lingkup visibilitas dari pointer 'progressHandler' keluar tanpa melepaskan memori. Kebocoran memori dimungkinkan. PVRGUIChannelIconUpdater.cpp: 94

 void CPVRGUIChannelIconUpdater::SearchAndUpdateMissingChannelIcons() const { .... CPVRGUIProgressHandler* progressHandler = new CPVRGUIProgressHandler(g_localizeStrings.Get(19286)); for (const auto& group : m_groups) { const std::vector<PVRChannelGroupMember> members = group->GetMembers(); int channelIndex = 0; for (const auto& member : members) { progressHandler->UpdateProgress(member.channel->ChannelName(), channelIndex++, members.size()); .... } progressHandler->DestroyProgress(); } 

Pointer progressHandler berisi nilai yang diperoleh dengan memanggil operator baru . Namun, operator delete tidak dipanggil untuk penunjuk ini, yang menyebabkan kebocoran memori.

Peringatan PVS-Studio : V557 Array overrun dimungkinkan. Indeks 'idx' menunjuk di luar batas array. PlayerCoreFactory.cpp: 240

 std::vector<CPlayerCoreConfig *> m_vecPlayerConfigs; bool CPlayerCoreFactory::PlaysVideo(const std::string& player) const { CSingleLock lock(m_section); size_t idx = GetPlayerIndex(player); if (m_vecPlayerConfigs.empty() || idx > m_vecPlayerConfigs.size()) return false; return m_vecPlayerConfigs[idx]->m_bPlaysVideo; } 

Pernyataan if memberlakukan batasan pada ukuran vektor m_vecPlayerConfigs karena ekspresi kondisional dan keluar dari metode jika itu benar. Akibatnya, jika eksekusi kode mencapai pernyataan pengembalian terakhir, ukuran vektor m_vecPlayerConfigs berada dalam kisaran yang ditentukan: [1; idx]. Namun, beberapa baris di bawah ini adalah panggilan idx : m_vecPlayerConfigs [idx] -> m_bPlaysVideo . Akibatnya, jika idx sama dengan ukuran vektor, aplikasi akan melampaui rentang yang dapat diterima.

Dan akhirnya, lihat beberapa peringatan pada kode perpustakaan Platinum .

Peringatan PVS-Studio : V542 Pertimbangkan untuk memeriksa pemeran tipe ganjil: 'bool' ke 'char *'. PltCtrlPoint.cpp: 1617

 NPT_Result PLT_CtrlPoint::ProcessSubscribeResponse(...) { .... bool subscription = (request.GetMethod().ToUppercase() == "SUBSCRIBE"); .... NPT_String prefix = NPT_String::Format(" PLT_CtrlPoint::ProcessSubscribeResponse %ubscribe for service \"%s\" (result = %d, status code = %d)", (const char*)subscription?"S":"Uns", // <= (const char*)service->GetServiceID(), res, response?response->GetStatusCode():0); .... } 

Dalam hal ini, prioritas operasi menjadi bingung. Konstanta * bukan hasil dari komputasi operator ternary ( berlangganan? "S": "Uns" ), tetapi berlangganan variabel. Setidaknya itu terlihat aneh.

Peringatan PVS-Studio : V560 Bagian dari ekspresi kondisional selalu salah: c == '\ t'. NptUtils.cpp: 863

 NPT_Result NPT_ParseMimeParameters(....) { .... case NPT_MIME_PARAMETER_PARSER_STATE_NEED_EQUALS: if (c < ' ') return NPT_ERROR_INVALID_SYNTAX; // END or CTLs are invalid if (c == ' ' || c == '\t') continue; // ignore leading whitespace .... } 

Kode spasi 0x20, kode tab 0x09. Oleh karena itu, subekspresi c == '\ t' akan selalu salah , karena case ini sudah dicakup oleh ekspresi c <'' (jika benar, fungsi akan keluar).

Kesimpulan


Seperti yang dapat Anda lihat dari artikel ini, pada sistem CI berikutnya (CircleCI), kami berhasil mengonfigurasi verifikasi proyek menggunakan PVS-Studio. Saya sarankan Anda mengunduh dan mencoba penganalisa pada proyek Anda. Jika Anda memiliki pertanyaan tentang mengonfigurasikan atau menggunakan analisis, jangan ragu untuk menulis kepada kami , kami akan dengan senang hati membantu.

Dan, tentu saja, kode kecerobohan untukmu, teman-teman. :)



Jika Anda ingin berbagi artikel ini dengan audiens yang berbahasa Inggris, silakan gunakan tautan ke terjemahan: Sergey Vasiliev, Ilya Gainulin. PVS-Studio di Awan: CircleCI .

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


All Articles