Mengikuti Jejak Kalkulator: SpeedCrunch

Gambar 4

Inilah kami, terus mengeksplorasi kode kalkulator! Hari ini kita akan melihat proyek bernama SpeedCrunch, kalkulator gratis terpopuler kedua.

Pendahuluan


SpeedCrunch adalah kalkulator ilmiah presisi tinggi yang menampilkan antarmuka pengguna yang digerakkan oleh keyboard. Ini adalah perangkat lunak bebas dan sumber terbuka, dilisensikan di bawah GPL dan berjalan di Windows, Linux, dan macOS.

Kode sumber tersedia di BitBucket . Saya agak kecewa dengan dokumentasi pembangunan, yang bisa lebih detail. Dikatakan bahwa Anda perlu β€œQt 5.2 atau yang lebih baru” untuk membangun proyek, tetapi sebenarnya dibutuhkan beberapa paket spesifik, yang tidak mudah diketahui dari log CMake. Ngomong-ngomong, ini dianggap praktik yang baik saat ini untuk memasukkan Dockerfile ke dalam proyek untuk membuatnya lebih mudah bagi pengguna untuk mengatur lingkungan pengembangan.

Berikut ini output dari utilitas Cloc yang menunjukkan bagaimana SpeedCrunch membandingkan dengan kalkulator lain:

Gambar 2

Ulasan bug untuk proyek-proyek lain:


Analisis dilakukan dengan analisa statis PVS-Studio . Ini adalah paket solusi untuk kontrol kualitas perangkat lunak dan deteksi bug dan kerentanan potensial. PVS-Studio mendukung C, C ++, C #, dan Java dan berjalan pada Windows, Linux, dan macOS.

Logika aneh dalam satu lingkaran


V560 Bagian dari ekspresi kondisional selalu benar:! RuleFound. evaluator.cpp 1410

void Evaluator::compile(const Tokens& tokens) { .... while (!syntaxStack.hasError()) { bool ruleFound = false; // <= // Rule for function last argument: id (arg) -> arg. if (!ruleFound && syntaxStack.itemCount() >= 4) { // <= Token par2 = syntaxStack.top(); Token arg = syntaxStack.top(1); Token par1 = syntaxStack.top(2); Token id = syntaxStack.top(3); if (par2.asOperator() == Token::AssociationEnd && arg.isOperand() && par1.asOperator() == Token::AssociationStart && id.isIdentifier()) { ruleFound = true; // <= syntaxStack.reduce(4, MAX_PRECEDENCE); m_codes.append(Opcode(Opcode::Function, argCount)); #ifdef EVALUATOR_DEBUG dbg << "\tRule for function last argument " << argCount << " \n"; #endif argCount = argStack.empty() ? 0 : argStack.pop(); } } .... } .... } 

Perhatikan variabel ruleFound : diset ke false di setiap iterasi. Di dalam tubuh loop, meskipun, variabel itu disetel ke true pada kondisi tertentu, tetapi itu akan diatur kembali ke false pada iterasi berikutnya. Variabel ruleFound seharusnya dideklarasikan sebelum loop.

Perbandingan yang mencurigakan


V560 Bagian dari ekspresi kondisional selalu benar: m_scrollDirection! = 0. resultdisplay.cpp 242

 void ResultDisplay::fullContentScrollEvent() { QScrollBar* bar = verticalScrollBar(); int value = bar->value(); bool shouldStop = (m_scrollDirection == -1 && value <= 0) || (m_scrollDirection == 1 && value >= bar->maximum()); if (shouldStop && m_scrollDirection != 0) { // <= stopActiveScrollingAnimation(); return; } scrollLines(m_scrollDirection * 10); } 

Jika nilai shouldStop variabel benar , maka variabel m_scrollDirection akan mengambil salah satu dari dua nilai: -1 atau 1. Oleh karena itu, nilainya pasti akan berbeda dari nol dalam pernyataan bersyarat berikutnya, yang merupakan apa yang diingatkan oleh penganalisa.

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

 void EditorCompletion::showCompletion(const QStringList& choices) { .... for (int i = 0; i < choices.count(); ++i) { QStringList pair = choices.at(i).split(':'); QTreeWidgetItem* item = new QTreeWidgetItem(m_popup, pair); if (item && m_editor->layoutDirection() == Qt::RightToLeft) item->setTextAlignment(0, Qt::AlignRight); .... } .... } 

Memori untuk objek tipe QTreeWidgetItem dialokasikan menggunakan operator baru . Ini berarti bahwa kegagalan alokasi memori akan menyebabkan melempar pengecualian std :: bad_alloc () . Oleh karena itu, memeriksa penunjuk item mubazir dan dapat dihapus.

Potensi NULL Dereference


V595 Pointer 'ioparams' digunakan sebelum diverifikasi terhadap nullptr. Periksa baris: 969, 983. floatio.c 969

 int cattokens(....) { .... if (printexp) { if (expbase < 2) expbase = ioparams->expbase; // <= .... } dot = '.'; expbegin = "("; expend = ")"; if (ioparams != NULL) // <= { dot = ioparams->dot; expbegin = ioparams->expbegin; expend = ioparams->expend; } .... } 

Pointer ioparams ditereferensi sebelum cek. Sepertinya ada beberapa kesalahan di sini. Karena dereference didahului oleh sejumlah kondisi, bug tidak akan sering muncul, tetapi itu akan memiliki efek drastis ketika itu terjadi.

Pembagian dengan nol


V609 Bagilah dengan nol. Kisaran penyebut [0..4]. floatconvert.c 266

 static int lgbase( signed char base) { switch(base) { case 2: return 1; case 8: return 3; case 16: return 4; } return 0; // <= } static void _setlongintdesc( p_ext_seq_desc n, t_longint* l, signed char base) { int lg; n->seq.base = base; lg = lgbase(base); // <= n->seq.digits = (_bitlength(l) + lg - 1) / lg; // <= n->seq.leadingSignDigits = 0; n->seq.trailing0 = _lastnonzerobit(l) / lg; // <= n->seq.param = l; n->getdigit = _getlongintdigit; } 

Fungsi lgbase dapat mengembalikan nol, yang kemudian dapat digunakan sebagai pembagi. Fungsi ini dapat berpotensi dipanggil dengan nilai apa pun, tidak hanya 2, 8, atau 16.

Perilaku tidak terdefinisi


V610 Perilaku tidak terdefinisi. Periksa operator shift '<<'. Operan kiri '(~ 0)' negatif. floatlogic.c 64

 static char _signextend( t_longint* longint) { unsigned mask; signed char sign; sign = _signof(longint); mask = (~0) << SIGNBIT; // <= if (sign < 0) longint->value[MAXIDX] |= mask; else longint->value[MAXIDX] &= ~mask; return sign; } 

Karena hasil pembalik nol disimpan ke int yang ditandatangani, nilai yang dihasilkan akan menjadi angka negatif, yang kemudian digeser. Menggeser-kiri nilai negatif adalah perilaku yang tidak terdefinisi.

Berikut daftar lengkap semua kasus tersebut:

  • V610 Perilaku tidak terdefinisi. Periksa operator shift '<<'. Operan kiri '(- 1)' negatif. floatnum.c 289
  • V610 Perilaku tidak terdefinisi. Periksa operator shift '<<'. Operan kiri '(- 1)' negatif. floatnum.c 325
  • V610 Perilaku tidak terdefinisi. Periksa operator shift '<<'. Operan kiri '(- 1)' negatif. floatnum.c 344
  • V610 Perilaku tidak terdefinisi. Periksa operator shift '<<'. Operan kiri '(- 1)' negatif. floatnum.c 351

Tag HTML tidak tertutup


V735 Mungkin HTML yang salah. Tag penutup "</body>" ditemukan, sedangkan tag "</div>" diharapkan. book.cpp 127

 static QString makeAlgebraLogBaseConversionPage() { return BEGIN INDEX_LINK TITLE(Book::tr("Logarithmic Base Conversion")) FORMULA(y = log(x) / log(a), log<sub>a</sub>x = log(x) / log(a)) END; } 

Seperti yang sering terjadi dengan kode C / C ++, mempelajari sumber tidak banyak membantu dalam mencari tahu, jadi kita akan melihat pada kode preprocessed sebagai gantinya:

Gambar 3



Penganalisis telah mendeteksi tag div tidak tertutup. File ini mengandung banyak cuplikan dalam HTML, dan pengembang harus memeriksa kode itu juga.

Berikut adalah beberapa kasus mencurigakan lainnya yang ditemukan oleh PVS-Studio:

  • V735 Mungkin HTML yang salah. Tag penutup "</td>" ditemukan, sedangkan tag "</sub>" diharapkan. book.cpp 344
  • V735 Mungkin HTML yang salah. Tag penutup "</td>" ditemukan, sedangkan tag "</sub>" diharapkan. book.cpp 347

Operator penugasan


V794 Operator penugasan harus dilindungi dari kasus 'this == & other'. kuantitas.cpp 373

 Quantity& Quantity::operator=(const Quantity& other) { m_numericValue = other.m_numericValue; m_dimension = other.m_dimension; m_format = other.m_format; stripUnits(); if(other.hasUnit()) { m_unit = new CNumber(*other.m_unit); m_unitName = other.m_unitName; } cleanDimension(); return *this; } 

Dianjurkan agar Anda memeriksa situasi di mana objek ditugaskan untuk dirinya sendiri dengan membandingkan pointer. Dengan kata lain, tambahkan dua baris berikut ke bagian awal badan fungsi:

 if (this == &other) return *this; 

Sebagai pengingat


V601 Nilai 'salah' secara implisit dilemparkan ke tipe integer. cmath.cpp 318

 /** * Returns -1, 0, 1 if n1 is less than, equal to, or more than n2. * Only valid for real numbers, since complex ones are not an ordered field. */ int CNumber::compare(const CNumber& other) const { if (isReal() && other.isReal()) return real.compare(other.real); else return false; // FIXME: Return something better. } 

Kadang-kadang Anda mengatakan dalam komentar bahwa mungkin beberapa peringatan dipicu oleh kode yang tidak lengkap. Ya, itu terjadi sesekali, tetapi kami secara khusus menunjukkan kasus-kasus seperti itu.

Kesimpulan


Kami telah memeriksa kode tiga program kalkulator - Windows Calculator, Qalculate!, Dan SpeedCrunch - dan tidak akan berhenti. Jangan ragu untuk menyarankan proyek yang Anda ingin kami periksa karena peringkat perangkat lunak tidak selalu mencerminkan keadaan sebenarnya.

Selamat mengunduh PVS-Studio dan cobalah "Kalkulator" Anda sendiri. :-)

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


All Articles