Ini adalah bagian baru dari seri artikel kami tentang menggunakan analisa statis PVS-Studio dengan sistem cloud CI. Hari ini kita akan melihat layanan lain, CircleCI. Kami akan menggunakan aplikasi pemutar media Kodi sebagai proyek uji dan melihat apakah kami dapat menemukan bug yang menarik dalam kode sumbernya.
Catatan Artikel sebelumnya tentang mengintegrasikan PVS-Studio dengan sistem cloud CI:
Sebelum mengatur lingkungan kerja dan memeriksa laporan analisis, saya ingin mengatakan beberapa kata tentang perangkat lunak yang akan kita gunakan dan periksa.
CircleCI adalah layanan cloud CI untuk pembuatan, pengujian, dan penyebaran perangkat lunak otomatis. Ini mendukung pembangunan proyek baik dalam wadah dan pada mesin virtual pada Windows, Linux, dan macOS.
Kodi adalah aplikasi media player lintas platform gratis dan open-source. Ini memungkinkan pengguna untuk memutar dan melihat sebagian besar media streaming, seperti video, musik, podcast, dan video dari Internet, serta semua file media digital umum dari media penyimpanan lokal dan jaringan. Ini mendukung penggunaan tema dan skin dan ekstensi fungsionalitas melalui plugin. Kodi tersedia untuk Windows, Linux, macOS, dan Android.
PVS-Studio adalah penganalisa statis untuk mendeteksi bug dan kerentanan potensial dalam kode sumber aplikasi yang ditulis dalam C, C ++, C #, dan Java. Penganalisa berjalan pada Windows, Linux, dan macOS.
Menyiapkan
Pertama kita harus pergi ke halaman utama 
CircleCI dan klik "Daftar"
Pada halaman berikutnya, kami ditawari untuk mengotorisasi dengan akun GitHub atau Bitbucket. Kami memilih GitHub dan membuka halaman otorisasi CircleCI.
Setelah mengotorisasi aplikasi (dengan mengklik tombol hijau "Otorisasi circleci"), kami diarahkan ke "Selamat Datang di CircleCI!" halaman:
Di sini kita dapat menentukan segera proyek mana yang ingin kita bangun CircleCI. Kami mencentang repositori kami dan klik "Ikuti".
Setelah menambahkan repositori, CircleCI akan secara otomatis memulai proses build, tetapi karena kami belum memiliki file konfigurasi di repositori kami, pekerjaan build akan dibatalkan dengan pesan kesalahan.
Sebelum menambahkan file konfigurasi, kita perlu menambahkan beberapa variabel yang berisi data lisensi analyzer. Untuk melakukan itu, kita klik "Pengaturan" di sidebar kiri, pilih "Proyek" di bagian "ORGANISASI", dan klik tombol roda gigi di sebelah kanan nama proyek kami. Jendela pengaturan akan muncul.
Kita pergi ke halaman "Variabel Lingkungan". Di sini kita membuat dua variabel, 
PVS_USERNAME dan 
PVS_KEY , yang berisi kunci lisensi nama pengguna dan penganalisa.
Saat memulai build, CircleCI membaca konfigurasi pekerjaan dari file yang disimpan dalam repositori di .circleci / config.yml. Mari kita tambahkan.
Pertama, kita perlu menentukan gambar mesin virtual yang akan dijalankan oleh alat analisa. Daftar lengkap gambar tersedia di 
sini .
version: 2 jobs: build: machine: image: ubuntu-1604:201903-01 
Selanjutnya, kami menambahkan 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 
Menambahkan repositori PVS-Studio dan menginstal analyzer:
 - run: wget -q -O - https: 
Kemudian kami sedang membangun dependensi:
 - run: sudo make -C tools/depends/target/flatbuffers PREFIX=/usr/local 
Setelah itu, kami membuat Makefiles di direktori build:
 - run: mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Debug .. 
Langkah selanjutnya adalah menyiapkan dan memulai analisis proyek.
Pertama kita membuat file lisensi penganalisa. Perintah lain akan mulai melacak pembangunan proyek oleh kompiler.
Perintah selanjutnya mengikuti penelusuran menjalankan analisis seperti itu. Jika Anda menggunakan versi demo PVS-Studio, luncurkan dengan parameter:
--disableLicenseExpirationCheck .
Perintah terakhir mengubah file laporan analyzer menjadi 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 tes selesai, kami menyimpan laporan:
 - run: mkdir PVS_Result && cp PVS-Studio.* ./PVS_Result/ - store_artifacts: path: ./PVS_Result 
Berikut 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: 
Setelah file ini diunggah ke repositori, CircleCI akan secara otomatis memulai pembuatan.
Setelah pekerjaan selesai, file dengan hasil analisis dapat diunduh pada tab "Artefak".
Hasil analisis
OK, sekarang mari kita lihat beberapa output peringatan oleh penganalisa.
Peringatan PVS-Studio: 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"); .... } 
Pemformatan kode menyarankan logika eksekusi berikut:
- jika arttypes adalah null pointer, metode ini mengembalikan;
- jika arttypes adalah pointer non-null, vektor artworkMap akan dihapus dan beberapa tindakan kemudian dilakukan.
Tapi yang hilang ';' karakter istirahat semuanya, dan logika eksekusi yang sebenarnya adalah sebagai berikut:
- jika arttypes adalah pointer nol, vektor artworkMap akan dihapus dan metode kembali;
- jika arttypes adalah pointer non-null, program mengeksekusi tindakan apa pun yang terjadi selanjutnya tetapi vektor artworkMap tidak bisa dihapus.
Singkatnya, situasi ini memang terlihat seperti bug. Lagi pula, Anda hampir tidak mengharapkan orang untuk menulis ekspresi seperti 
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 yang ditandai dengan 
// <= . Variabel 
lastsector diberi nilai 0 dan kemudian digunakan sebagai ekspresi kondisional dalam dua pernyataan 
if . Karena nilai tidak berubah baik dalam loop atau antara penugasan, kontrol tidak akan pernah masuk ke cabang 
lain dari kedua pernyataan 
if .
Namun, itu juga bisa berarti bahwa para pengembang belum mengimplementasikan fungsi yang dimaksudkan (perhatikan komentar 
todo ).
Ngomong-ngomong, seperti yang mungkin Anda perhatikan, cuplikan ini memicu tiga peringatan sekaligus. Tetapi bahkan banyak peringatan untuk sepotong kode tidak akan terlihat cukup meyakinkan bagi beberapa pengguna, dan mereka akan tetap percaya bahwa penganalisa itu salah ... Aspek ini dibahas secara rinci dalam sebuah pos oleh salah satu rekan tim saya: " 
Satu 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(); } 
Pemeriksaan 
values.size ()! = 2 berlebihan di sini karena ekspresi kondisional ini akan selalu bernilai 
false . Memang, jika eksekusi memasuki salah satu cabang 
case dari pernyataan 
switch , dua elemen akan ditambahkan ke vektor, dan karena awalnya kosong, ukurannya secara alami akan menjadi sama dengan 2; jika tidak (yaitu jika cabang 
default dijalankan), metode akan kembali.
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 ke nilai 
INT_MAX dan kemudian digunakan sebagai operan dari operator ternary dalam perbandingan 
prio == INT_MAX , meskipun nilainya tidak berubah setelah inisialisasi. Ini berarti 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: pointer yang dikembalikan oleh fungsi 
malloc digunakan lebih lanjut dalam fungsi 
memcpy tanpa diperiksa untuk 
NULL terlebih dahulu.
Beberapa orang mungkin berpendapat bahwa 
malloc tidak akan pernah mengembalikan null pointer, dan jika ya, akan lebih baik bagi aplikasi untuk crash. Ini adalah subjek diskusi terpisah, tetapi apa pun pendapat Anda, saya sarankan membaca posting ini oleh rekan satu tim saya: " 
Mengapa penting untuk memeriksa apa fungsi malloc dikembalikan ".
Jika mau, Anda dapat menyesuaikan penganalisa sehingga tidak menganggap 
malloc dapat mengembalikan null pointer - ini akan mencegahnya mengeluarkan jenis peringatan ini. Rincian lebih lanjut dapat ditemukan 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); .... } 
Contoh ini mirip dengan yang sebelumnya. Pointer yang dikembalikan oleh fungsi 
malloc disimpan ke variabel 
entri , dan variabel ini kemudian digunakan tanpa cek nol sebelumnya ( 
entri-> 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(); } 
Nilai pointer 
progressHandler dikembalikan oleh operator 
baru . Tetapi tidak ada operator 
hapus untuk penunjuk ini. Ini berarti 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 membatasi 
ukuran vektor 
m_vecPlayerConfigs ke rentang tertentu dengan meminta metode kembali jika kondisi pemeriksaan ukuran benar. Akibatnya, ketika eksekusi mencapai pernyataan 
pengembalian terakhir, 
ukuran vektor 
m_vecPlayerConfigs akan berada dalam kisaran yang ditentukan, [1; idx]. Tetapi beberapa baris kemudian, program ini mengindeks vektor di 
idx : 
m_vecPlayerConfigs [idx] -> m_bPlaysVideo . Ini berarti bahwa jika 
idx sama dengan ukuran vektor, kami akan mengindeks di luar rentang yang valid.
Mari kita buat artikel ini dengan beberapa contoh dari 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",  
Para pengembang memiliki asumsi yang salah tentang prioritas operasi. Apa yang dilemparkan ke 
const char * bukan hasil yang dikembalikan oleh operator ternary ( 
berlangganan? "S": "Uns" ) tetapi variabel 
berlangganan . Ini terlihat aneh, paling tidak.
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 karakter spasi adalah 0x20, dan kode karakter tab adalah 0x09. Oleh karena itu, subekspresi 
c == '\ t' akan selalu bernilai 
false karena case ini sudah dicakup oleh pemeriksaan 
c <'' (yang, jika benar, akan menyebabkan fungsi kembali).
Kesimpulan
Seperti yang ditunjukkan artikel ini, kami berhasil membuat analisis oleh PVS-Studio pada sistem CI lain (CircleCI). Saya mengundang Anda untuk 
mengunduh dan mencoba penganalisa pada proyek Anda sendiri. Jika Anda memiliki pertanyaan tentang pengaturan atau penggunaan PVS-Studio, jangan ragu untuk 
menghubungi kami - kami akan dengan senang hati membantu.
Dan, tentu saja, kami berharap Anda mendapatkan kode bugless. :)