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"
Pada halaman berikutnya, kami akan diminta untuk mengautentikasi dengan akun GitHub atau Bitbucket. Pilih GitHub dan buka halaman otorisasi aplikasi CircleCI.
Kami mengotorisasi aplikasi (tombol hijau "Otorisasi circleci") dan mengarahkan kami ke halaman pembuka "Selamat datang di CircleCI!"
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.
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.
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.
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:
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:
Kami mengunggah file ke repositori dan CircleCI akan secara otomatis memulai perakitan proyek.
Setelah tugas selesai, file dengan hasil analisa dapat diunduh pada tab βArtefakβ.
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;
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),
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);
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",
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;
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 .