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. :)