Celestia: Petualangan Bugs di Luar Angkasa

Gambar 1

Celestia adalah simulator ruang tiga dimensi. Simulasi ruang memungkinkan penjelajahan alam semesta kita dalam tiga dimensi. Celestia tersedia di Windows, Linux dan macOS. Proyek ini sangat kecil dan PVS-Studio mendeteksi beberapa cacat di dalamnya. Terlepas dari kenyataan ini, kami ingin memperhatikannya, karena ini adalah proyek pendidikan yang populer dan akan berguna untuk meningkatkannya. Omong-omong, program ini digunakan dalam film populer, seri, dan program untuk menunjukkan ruang. Kenyataan ini, pada gilirannya, meningkatkan persyaratan terhadap kualitas kode.

Pendahuluan


Situs web resmi proyek Celestia memberikan deskripsi terperinci. Kode sumber tersedia di GitHub . Alat analisis memeriksa 166 file .cpp, tidak termasuk perpustakaan dan tes. Proyek ini kecil, tetapi cacat yang ditemukan patut diperhatikan.

Untuk melakukan analisis kode sumber kami menggunakan penganalisa kode statis PVS-Studio . Baik Celestia dan PVS-Studio adalah cross-platform. Kami menganalisis proyek pada platform Windows. Sangat mudah untuk membangun proyek dengan mendapatkan dependensi menggunakan Vcpkg - manajer perpustakaan Microsoft. Menurut ulasan, ini lebih rendah dari kapasitas Conan , tetapi program ini juga cukup nyaman untuk digunakan.

Hasil analisis


Peringatan 1

V501 Ada sub-ekspresi yang identik ke kiri dan ke kanan operator '<': b.nAttributes <b.nAttributes cmodfix.cpp 378

bool operator<(const Mesh::VertexDescription& a, const Mesh::VertexDescription& b) { if (a.stride < b.stride) return true; if (b.stride < a.stride) return false; if (a.nAttributes < b.nAttributes) // <= return true; if (b.nAttributes < b.nAttributes) // <= return false; for (uint32_t i = 0; i < a.nAttributes; i++) { if (a.attributes[i] < b.attributes[i]) return true; else if (b.attributes[i] < a.attributes[i]) return false; } return false; } 

Betapa mudahnya membuat kesalahan saat menyalin kode. Kami menulis tentang itu di setiap ulasan. Rupanya, hanya analisis kode statis yang dapat membantu dalam situasi ini.

Pemrogram menyalin ekspresi bersyarat dan tidak sepenuhnya mengeditnya. Versi yang benar kemungkinan besar sebagai berikut:

 if (a.nAttributes < b.nAttributes) return true; if (b.nAttributes < a.nAttributes) return false; 

Sebuah penelitian menarik tentang topik ini: " Kejahatan dalam Fungsi Perbandingan ".

Peringatan 2

V575 Fungsi 'memset' memproses elemen '0'. Periksa argumen ketiga. winmain.cpp 2235

 static void BuildScriptsMenu(HMENU menuBar, const fs::path& scriptsDir) { .... MENUITEMINFO info; memset(&info, sizeof(info), 0); info.cbSize = sizeof(info); info.fMask = MIIM_SUBMENU; .... } 

Penulis kode mencampur argumen kedua dan ketiga dari fungsi memset . Alih-alih mengisi struktur dengan nol, ia mengatakan untuk mengisi 0 byte memori.

Peringatan 3

V595 Pointer 'tujuan' digunakan sebelum diverifikasi terhadap nullptr. Periksa baris: 48, 50. wintourguide.cpp 48

 BOOL APIENTRY TourGuideProc(....) { .... const DestinationList* destinations = guide->appCore->getDestinations(); Destination* dest = (*destinations)[0]; guide->selectedDest = dest; if (hwnd != NULL && destinations != NULL) { .... } .... } 

Pointer tujuan mendapat dereferensi dua baris sebelum dibandingkan dengan NULL . Kode semacam itu berpotensi menyebabkan kesalahan.

Peringatan 4

Kelas V702 harus selalu diturunkan dari std :: exception (dan yang sama) sebagai 'publik' (tidak ada kata kunci yang ditentukan, jadi kompiler secara default ke 'private'). fs.h 21

 class filesystem_error : std::system_error { public: filesystem_error(std::error_code ec, const char* msg) : std::system_error(ec, msg) { } }; // filesystem_error 

Penganalisis telah mendeteksi kelas yang diwarisi dari kelas std :: exception melalui pengubah pribadi (diatur secara default). Warisan seperti itu berbahaya karena std :: exception exception tidak akan ditangkap karena pewarisan non-publik. Akibatnya, penangan pengecualian berperilaku tidak seperti yang dimaksudkan.

Peringatan 5

V713 Pointer 's' digunakan dalam ekspresi logis sebelum diverifikasi terhadap nullptr dalam ekspresi logis yang sama. winmain.cpp 3031

 static char* skipUntilQuote(char* s) { while (*s != '"' && s != '\0') s++; return s; } 

Dalam satu bagian dari ekspresi kondisional, pemrogram lupa untuk mengubah pointer s . Ternyata perbandingan pointer, bukan nilainya. Dan itu tidak masuk akal dalam situasi ini.

Peringatan 6

V773 Fungsi itu keluar tanpa melepaskan pointer 'vertexShader'. Kebocoran memori dimungkinkan. modelviewwidget.cpp 1517

 GLShaderProgram* ModelViewWidget::createShader(const ShaderKey& shaderKey) { .... auto* glShader = new GLShaderProgram(); auto* vertexShader = new GLVertexShader(); if (!vertexShader->compile(vertexShaderSource.toStdString())) { qWarning("Vertex shader error: %s", vertexShader->log().c_str()); std::cerr << vertexShaderSource.toStdString() << std::endl; delete glShader; return nullptr; } .... } 

Memori dilepaskan oleh pointer glShader tetapi tidak dihapus oleh pointer vertexShader saat keluar dari fungsi.

Fragmen serupa di bawah ini:
  • V773 Fungsi itu keluar tanpa melepaskan pointer 'fragmentShader'. Kebocoran memori dimungkinkan. modelviewwidget.cpp 1526

Peringatan 7

Ekspresi V547 '! InputFilename.empty ()' selalu benar. makexindex.cpp 128

 int main(int argc, char* argv[]) { if (!parseCommandLine(argc, argv) || inputFilename.empty()) { Usage(); return 1; } istream* inputFile = &cin; if (!inputFilename.empty()) { inputFile = new ifstream(inputFilename, ios::in); if (!inputFile->good()) { cerr << "Error opening input file " << inputFilename << '\n'; return 1; } } .... } 

Pemeriksaan berulang atas keberadaan nama file. Ini bukan bug, tetapi karena fakta bahwa variabel inputFilename sudah diperiksa di awal fungsi, pemeriksaan di bawah ini dapat dihapus, membuat kode lebih kompak.

Peringatan 8

V556 Nilai dari berbagai tipe enum dibandingkan: switch (ENUM_TYPE_A) {case ENUM_TYPE_B: ...}. render.cpp 7457

 enum LabelAlignment { AlignCenter, AlignLeft, AlignRight }; enum LabelVerticalAlignment { VerticalAlignCenter, VerticalAlignBottom, VerticalAlignTop, }; struct Annotation { .... LabelVerticalAlignment valign : 3; .... }; void Renderer::renderAnnotations(....) { .... switch (annotations[i].valign) { case AlignCenter: vOffset = -font[fs]->getHeight() / 2; break; case VerticalAlignTop: vOffset = -font[fs]->getHeight(); break; case VerticalAlignBottom: vOffset = 0; break; } .... } 

Nilai enumerasi digabungkan dalam operator sakelar. Karena itu, enumerasi tipe yang berbeda dibandingkan dalam satu fragmen: LabelVerticalAlignment dan AlignCenter .

Peringatan 9

V581 Ekspresi kondisional dari pernyataan 'jika' yang terletak berdampingan adalah identik. Periksa baris: 2844, 2850. shadermanager.cpp 2850

 GLVertexShader* ShaderManager::buildParticleVertexShader(const ShaderProperties& props) { .... if (props.texUsage & ShaderProperties::PointSprite) { source << "uniform float pointScale;\n"; source << "attribute float pointSize;\n"; } if (props.texUsage & ShaderProperties::PointSprite) { source << DeclareVarying("pointFade", Shader_Float); } .... } 

Penganalisa telah mendeteksi dua ekspresi kondisional yang identik dalam satu baris. Entah kesalahan telah dibuat atau dua kondisi dapat digabungkan menjadi satu, dan dengan demikian membuat kode lebih sederhana.

Peringatan 10

V668 Tidak ada gunanya menguji pointer 'dp' terhadap null, karena memori dialokasikan menggunakan operator 'baru'. Pengecualian akan dihasilkan jika terjadi kesalahan alokasi memori. windatepicker.cpp 625

 static LRESULT DatePickerCreate(HWND hwnd, CREATESTRUCT& cs) { DatePicker* dp = new DatePicker(hwnd, cs); if (dp == NULL) return -1; .... } 

Nilai pointer yang dikembalikan oleh operator baru dibandingkan dengan nol. Jika operator tidak dapat mengalokasikan memori, maka menurut standar C ++, pengecualian std :: bad_alloc () dilemparkan. Maka cek untuk nol tidak ada gunanya.

Tiga cek serupa lainnya:

  • V668 Tidak ada gunanya menguji pointer 'mode' terhadap null, karena memori dialokasikan menggunakan operator 'baru'. Pengecualian akan dihasilkan jika terjadi kesalahan alokasi memori. winmain.cpp 2967
  • V668 Tidak ada gunanya menguji pointer 'dropTarget' terhadap null, karena memori dialokasikan menggunakan operator 'baru'. Pengecualian akan dihasilkan jika terjadi kesalahan alokasi memori. winmain.cpp 3272
  • V668 Tidak ada gunanya menguji pointer 'appCore' terhadap nol, karena memori dialokasikan menggunakan operator 'baru'. Pengecualian akan dihasilkan jika terjadi kesalahan alokasi memori. winmain.cpp 3352

Peringatan 11

V624 Konstanta 3.14159265 sedang digunakan. Nilai yang dihasilkan mungkin tidak akurat. Pertimbangkan untuk menggunakan konstanta M_PI dari <math.h>. 3dstocmod.cpp 62

 int main(int argc, char* argv[]) { .... Model* newModel = GenerateModelNormals(*model, float(smoothAngle * 3.14159265 / 180.0), weldVertices, weldTolerance); .... } 

Diagnostik bersifat opsional tetapi dalam hal ini lebih baik menggunakan konstanta siap pakai untuk nomor Pi dari perpustakaan standar.

Kesimpulan


Baru-baru ini proyek ini telah dikembangkan oleh para penggemar, tetapi masih populer dan diminati dalam program pelatihan. Ada ribuan addons dengan objek ruang berbeda di Internet. Celestia digunakan dalam film " The Day After Tomorrow " dan serial dokumenter " Through the Wormhole with Morgan Freeman ".

Kami senang bahwa dengan memeriksa berbagai proyek dengan kode sumber terbuka, kami tidak hanya mempromosikan metodologi analisis kode statis, tetapi juga berkontribusi pada pengembangan proyek sumber terbuka. Omong-omong, Anda juga dapat menggunakan penganalisa PVS-Studio tidak hanya untuk menguji proyek Anda sendiri, tetapi juga proyek pihak ketiga sebagai penggila. Untuk melakukan ini, Anda dapat menggunakan salah satu opsi lisensi gratis.

Gunakan penganalisa kode statis, buat proyek Anda lebih andal dan lebih baik!

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


All Articles