
Teknologi komputer modern, solusi teknis dan perangkat lunak - semua ini sangat memudahkan dan mempercepat implementasi berbagai penelitian ilmiah. Seringkali, simulasi komputer adalah satu-satunya cara untuk menguji banyak teori. Perangkat lunak ilmiah memiliki karakteristiknya sendiri. Sebagai contoh, perangkat lunak seperti itu sering mengalami pengujian yang sangat teliti, tetapi tidak terdokumentasi dengan baik. Namun, perangkat lunak ditulis oleh orang-orang, dan orang membuat kesalahan. Kesalahan dalam program ilmiah dapat menimbulkan keraguan pada keseluruhan penelitian. Artikel ini akan mencantumkan lusinan masalah yang ditemukan dalam kode paket perangkat lunak NCBI Genome Workbench.
Pendahuluan
NCBI Genome Workbench menawarkan kepada para peneliti berbagai alat untuk mempelajari dan menganalisis data genetik. Pengguna dapat meneliti dan membandingkan data dari beberapa sumber, termasuk database NCBI (Pusat Nasional untuk Informasi Bioteknologi) atau data pribadi mereka sendiri.
Seperti disebutkan sebelumnya, perangkat lunak ilmiah biasanya tercakup dengan baik oleh unit test. Saat memeriksa proyek ini, 85 direktori dengan file uji dikeluarkan dari analisis. Ini sekitar seribu file. Mungkin ini disebabkan oleh persyaratan untuk menguji berbagai algoritma kompleks yang ditemukan untuk berbagai penelitian. Tetapi kualitas sisa kode (bukan yang diuji) tidak pada tingkat setinggi yang kita inginkan. Namun, seperti dalam setiap proyek di mana mereka belum mengurus memperkenalkan alat analisis kode statis :).
Data untuk tinjauan kode (atau bahkan penelitian) disediakan oleh penganalisa kode statis untuk C / C ++ / C # / Java -
PVS-Studio .
Hanya dua angka yang merusak proyek Anda
Berdasarkan database kesalahan kami, yang saat ini berjumlah lebih dari 12 ribu contoh yang dipilih, kami memperhatikan dan menjelaskan pola spesifik untuk penulisan kode yang mengarah pada banyak kesalahan. Sebagai contoh, kami melakukan studi berikut:
- Efek dari baris terakhir ;
- Fungsi paling berbahaya di dunia C / C ++ ;
- Ekspresi logis dalam C / C ++. Betapa salahnya para profesional ;
- Jahat hidup dalam fungsi perbandingan .
Proyek ini menandai awal dari deskripsi pola baru. Kita berbicara tentang angka
1 dan
2 dalam nama variabel, misalnya,
file1 dan
file2 , dll. Sangat mudah untuk membingungkan dua variabel tersebut. Ini adalah kasus kesalahan ketik khusus dalam kode, tetapi satu kesalahan seperti itu menghasilkan keinginan untuk bekerja dengan variabel-variabel dengan nama yang sama, yang hanya berbeda dengan angka 1 dan 2 pada akhir nama.
Ke depan sedikit, saya akan mengatakan bahwa semua studi yang tercantum di atas dikonfirmasi dalam kode proyek ini: D.
Pertimbangkan contoh pertama dari proyek Genome Workbench:
V501 Ada sub-ekspresi identik '(! Loc1.IsInt () &&! Loc1.IsWhole ())' di sebelah kiri dan di sebelah kanan '||' operator. nw_aligner.cpp 480
CRef<CSeq_align> CNWAligner::Run(CScope &scope, const CSeq_loc &loc1, const CSeq_loc &loc2, bool trim_end_gaps) { if ((!loc1.IsInt() && !loc1.IsWhole()) || (!loc1.IsInt() && !loc1.IsWhole())) { NCBI_THROW(CException, eUnknown, "Only whole and interval locations supported"); } .... }
Kami melihat dua variabel bernama
loc1 dan
loc2 . Dan juga kesalahan dalam kode: variabel
loc2 tidak digunakan, karena alih-alih itu
loc1 digunakan sekali lagi.
Contoh lain:
V560 Bagian dari ekspresi kondisional selalu salah: s1.IsSet (). valid_biosource.cpp 3073
static bool s_PCRPrimerSetLess(const CPCRPrimerSet& s1, const CPCRPrimerSet& s2) { if (!s1.IsSet() && s1.IsSet()) { return true; } else if (s1.IsSet() && !s2.IsSet()) { return false; } else if (!s1.IsSet() && !s2.IsSet()) { return false; } else if (s1.Get().size() < s2.Get().size()) { return true; } else if (s1.Get().size() > s2.Get().size()) { return false; } else { ..... }
Baris pertama dari kode mencampur variabel
s1 dan
s2 . Berdasarkan namanya, ini adalah fungsi perbandingan. Tetapi kesalahan seperti itu bisa terjadi di mana saja, karena dengan memberi nama variabel
Nomor 1 dan
Nomor 2 , programmer hampir pasti akan membuat kesalahan di masa depan. Dan semakin banyak menggunakan nama-nama tersebut dalam suatu fungsi, semakin tinggi kemungkinan kesalahan.
Kesalahan ketik dan salin-tempel lainnya
V501 Ada sub-ekspresi identik ke kiri dan ke kanan Operator '! =': Bd.bit_.bits [i]! = Bd.bit_.bits [i] bm.h 296
bool compare_state(const iterator_base& ib) const { .... if (this->block_type_ == 0 { if (bd.bit_.ptr != ib_db.bit_.ptr) return false; if (bd.bit_.idx != ib_db.bit_.idx) return false; if (bd.bit_.cnt != ib_db.bit_.cnt) return false; if (bd.bit_.pos != ib_db.bit_.pos) return false; for (unsigned i = 0; i < bd.bit_.cnt; ++i) { if (bd.bit_.bits[i] != bd.bit_.bits[i]) return false; } } .... }
Saya percaya bahwa setelah semua pemeriksaan, ukuran array
bit dari objek
bd.bit_ dan
ib_db.bit_ sama. Oleh karena itu, penulis kode menulis satu siklus untuk perbandingan elemen array
bit , tetapi membuat kesalahan ketik atas nama salah satu objek yang dibandingkan. Akibatnya, objek yang dibandingkan dapat secara keliru dianggap sama dalam beberapa situasi.
Contoh ini layak untuk artikel "
Jahat hidup dalam fungsi perbandingan "
V501 Ada sub-ekspresi identik 'CFieldHandler :: QualifierNamesAreEquivalent (bidang, kFieldTypeSeqId)' ke kiri dan ke kanan '||' operator. field_handler.cpp 152
bool CFieldHandlerFactory::s_IsSequenceIDField(const string& field) { if ( CFieldHandler::QualifierNamesAreEquivalent(field, kFieldTypeSeqId) || CFieldHandler::QualifierNamesAreEquivalent(field, kFieldTypeSeqId)) { return true; } else { return false; } }
Kemungkinan besar, salah satu dari cek itu berlebihan. Saya tidak menemukan variabel kode yang mirip dengan
kFieldTypeSeqId . Namun demikian, panggilan fungsi tambahan dimungkinkan karena operator "||", yang menurunkan kinerja.
Beberapa lagi dari jenis tempat yang sama dengan peringatan penganalisa, membutuhkan verifikasi:
- V501 Ada sub-ekspresi identik 'uf-> GetData (). IsBool ()' di sebelah kiri dan di sebelah kanan operator '&&'. variasi_utils.cpp 1711
- V501 Ada sub-ekspresi identik 'uf-> GetData (). IsBool ()' di sebelah kiri dan di sebelah kanan operator '&&'. variasi_utils.cpp 1735
V766 Item dengan kunci yang sama 'kArgRemote' telah ditambahkan. blast_args.cpp 3262
void CBlastAppArgs::x_IssueWarningsForIgnoredOptions(const CArgs& args) { set<string> can_override; .... can_override.insert(kArgOutputFormat); can_override.insert(kArgNumDescriptions); can_override.insert(kArgNumAlignments); can_override.insert(kArgMaxTargetSequences); can_override.insert(kArgRemote);
Alat analisis mendeteksi penambahan 2 nilai identik ke wadah yang
ditetapkan . Ingat bahwa wadah ini hanya menyimpan nilai unik, jadi duplikat tidak ditambahkan ke dalamnya.
Kode seperti di atas sering ditulis menggunakan metode salin-tempel. Mungkin saja ada nilai tambahan, atau mungkin penulis lupa untuk mengganti nama salah satu variabel ketika ia menyalin. Saat Anda menghapus panggilan tambahan untuk
disisipkan, kode sedikit dioptimalkan, yang, bagaimanapun, tidak signifikan. Lebih penting lagi, kesalahan serius dapat disembunyikan di sini karena elemen yang hilang di set.
V523 Pernyataan 'maka' setara dengan fragmen kode berikutnya. vcf_reader.cpp 1105
bool CVcfReader::xAssignFeatureLocationSet(....) { .... if (data.m_SetType == CVcfData::ST_ALL_DEL) { if (data.m_strRef.size() == 1) {
Fungsi ini berisi fragmen kode besar dan benar-benar identik. Namun, mereka berisi berbagai komentar yang menyertainya. Kode tidak ditulis secara optimal, membingungkan, dan mungkin mengandung kesalahan.
Seluruh daftar tempat mencurigakan dengan pernyataan if-else terlihat seperti ini:
- V523 Pernyataan 'lalu' sama dengan pernyataan 'lain'. blk.c 2142
- V523 Pernyataan 'maka' setara dengan fragmen kode berikutnya. odbc.c 379
- V523 Pernyataan 'maka' setara dengan fragmen kode berikutnya. odbc.c 1414
- V523 Pernyataan 'lalu' sama dengan pernyataan 'lain'. seqdbvol.cpp 1922
- V523 Pernyataan 'lalu' sama dengan pernyataan 'lain'. seqdb_demo.cpp 466
- V523 Pernyataan 'maka' setara dengan fragmen kode berikutnya. blast_engine.c 1917
- V523 Pernyataan 'lalu' sama dengan pernyataan 'lain'. blast_filter.c 420
- V523 Pernyataan 'lalu' sama dengan pernyataan 'lain'. blast_parameters.c 636
- V523 Pernyataan 'lalu' sama dengan pernyataan 'lain'. unordered_spliter.cpp 684
- V523 Pernyataan 'lalu' sama dengan pernyataan 'lain'. bme.cpp 333
- V523 Pernyataan 'lalu' sama dengan pernyataan 'lain'. gme.cpp 484
/ * dengan keamanan paling baik menjadi bertele-tele * /
V597 Kompiler dapat menghapus pemanggilan fungsi 'memset', yang digunakan untuk membersihkan buffer 'passwd_buf'. Fungsi memset_s () harus digunakan untuk menghapus data pribadi. challenge.c 366
void tds_answer_challenge(....) { #define MAX_PW_SZ 14 .... if (ntlm_v == 1) { .... memset(hash, 0, sizeof(hash)); memset(passwd_buf, 0, sizeof(passwd_buf)); memset(ntlm2_challenge, 0, sizeof(ntlm2_challenge)); } else { .... } }
Seperti yang mungkin sudah Anda tebak, komentar lucu tentang keamanan dari kode digunakan pada judul bagian.
Singkatnya, fungsi
memset akan dihapus oleh kompiler, karena buffer flush tidak lagi digunakan. Dan data seperti
hash atau
passwd_buf tidak akan benar-benar nol. Untuk informasi lebih lanjut tentang mekanisme kompiler yang tidak jelas ini, lihat artikel "
Aman Membersihkan Data Pribadi ".
V597 Kompilator dapat menghapus panggilan fungsi 'memset', yang digunakan untuk menyiram objek 'jawab'. Fungsi memset_s () harus digunakan untuk menghapus data pribadi. challenge.c 561
static TDSRET tds7_send_auth(....) { .... memset(&answer, 0, sizeof(TDSANSWER)); return tds_flush_packet(tds); }
Itu bukan satu-satunya contoh dengan komentar tentang "keamanan". Dilihat dari komentar, dapat diasumsikan bahwa keamanan sangat penting untuk proyek. Oleh karena itu, saya melampirkan seluruh daftar masalah yang tidak kecil:
- V597 Kompiler dapat menghapus pemanggilan fungsi 'memset', yang digunakan untuk menyiram objek 'heap'. Fungsi memset_s () harus digunakan untuk menghapus data pribadi. ncbi_heapmgr.c 1300
- V597 Kompiler dapat menghapus panggilan fungsi 'memset', yang digunakan untuk menyiram objek 'konteks'. Fungsi memset_s () harus digunakan untuk menghapus data pribadi. tantangan.c 167
- V597 Kompiler dapat menghapus panggilan fungsi 'memset', yang digunakan untuk menyiram objek 'ks'. Fungsi memset_s () harus digunakan untuk menghapus data pribadi. tantangan.c 339
- V597 Kompiler dapat menghapus panggilan fungsi 'memset', yang digunakan untuk menyiram objek 'md5_ctx'. Fungsi memset_s () harus digunakan untuk menghapus data pribadi. tantangan.c 353
- V597 Kompiler dapat menghapus pemanggilan fungsi 'memset', yang digunakan untuk membersihkan buffer 'hash'. Fungsi memset_s () harus digunakan untuk menghapus data pribadi. challenge.c 365
- V597 Kompiler dapat menghapus panggilan fungsi 'memset', yang digunakan untuk menyiram objek 'ks'. Fungsi memset_s () harus digunakan untuk menghapus data pribadi. tantangan.c 406
- V597 Kompiler dapat menghapus panggilan fungsi 'memset', yang digunakan untuk menyiram objek 'ntlm_v2_response'. Fungsi memset_s () harus digunakan untuk menghapus data pribadi. login.c 795
- V597 Kompilator dapat menghapus panggilan fungsi 'memset', yang digunakan untuk menyiram objek 'jawab'. Fungsi memset_s () harus digunakan untuk menghapus data pribadi. login.c 801
- V597 Kompiler dapat menghapus panggilan fungsi 'memset', yang digunakan untuk membersihkan buffer 'paket'. Fungsi memset_s () harus digunakan untuk menghapus data pribadi. numeric.c 256
- V597 Kompiler dapat menghapus panggilan fungsi 'memset', yang digunakan untuk membersihkan buffer 'paket'. Fungsi memset_s () harus digunakan untuk menghapus data pribadi. numeric.c 110
- V597 Kompiler dapat menghapus pemanggilan fungsi 'memset', yang digunakan untuk membersihkan buffer 'pwd'. Fungsi memset_s () harus digunakan untuk menghapus data pribadi. getpassarg.c 50
- V597 Kompiler dapat menghapus panggilan fungsi 'memset', yang digunakan untuk menyiram objek 'konteks'. Fungsi memset_s () harus digunakan untuk menghapus data pribadi. tantangan.c 188
- V597 Kompiler dapat menghapus pemanggilan fungsi 'memset', yang digunakan untuk membersihkan buffer 'buf'. Fungsi memset_s () harus digunakan untuk menghapus data pribadi. tantangan.c 243
- V597 Kompiler dapat menghapus pemanggilan fungsi 'memset', yang digunakan untuk membersihkan buffer 'ntlm_v2_hash'. Fungsi memset_s () harus digunakan untuk menghapus data pribadi. tantangan.c 309
- V597 Kompiler dapat menghapus panggilan fungsi 'memset', yang digunakan untuk menyiram objek 'md5_ctx'. Fungsi memset_s () harus digunakan untuk menghapus data pribadi. challenge.c 354
- V597 Kompiler dapat menghapus pemanggilan fungsi 'memset', yang digunakan untuk membersihkan buffer 'passwd_buf'. Fungsi memset_s () harus digunakan untuk menghapus data pribadi. tantangan.c 380
- V597 Kompiler dapat menghapus panggilan fungsi 'memset', yang digunakan untuk menyiram objek 'ks'. Fungsi memset_s () harus digunakan untuk menghapus data pribadi. tantangan.c 393
- V597 Kompiler dapat menghapus pemanggilan fungsi 'memset', yang digunakan untuk membersihkan buffer 'hash'. Fungsi memset_s () harus digunakan untuk menghapus data pribadi. challenge.c 394
- V597 Kompiler dapat menghapus pemanggilan fungsi 'memset', yang digunakan untuk membersihkan buffer 'ntlm2_challenge'. Fungsi memset_s () harus digunakan untuk menghapus data pribadi. tantangan.c 395
- V597 Kompiler dapat menghapus panggilan fungsi 'memset', yang digunakan untuk menyiram objek 'ks'. Fungsi memset_s () harus digunakan untuk menghapus data pribadi. challenge.c 419
- V597 Kompiler dapat menghapus panggilan fungsi 'memset', yang digunakan untuk menyiram objek 'ntlm_v2_response'. Fungsi memset_s () harus digunakan untuk menghapus data pribadi. challenge.c 556
Siklus yang mencurigakan
V534 Kemungkinan variabel yang salah sedang dibandingkan di dalam operator 'untuk'. Pertimbangkan untuk meninjau 'i'. taxFormat.cpp 569
void CTaxFormat::x_LoadTaxTree(void) { .... for(size_t i = 0; i < alignTaxids.size(); i++) { int tax_id = alignTaxids[i]; .... for(size_t j = 0; i < taxInfo.seqInfoList.size(); j++) { SSeqInfo* seqInfo = taxInfo.seqInfoList[j]; seqInfo->taxid = newTaxid; } .... } .... }
Saya pikir, dalam kondisi loop dalam, variabel
saya mendapatkannya secara acak. Sebagai gantinya, variabel
j harus digunakan.
V535 Variabel 'i' digunakan untuk loop ini dan untuk loop luar. Periksa baris: 302, 309. sls_alp.cpp 309
alp::~alp() { .... if(d_alp_states) { for(i=0;i<=d_nalp;i++)
Dua siklus identik bersarang, di mana penghitung global juga diatur ulang, terlihat sangat mencurigakan. Pengembang harus memeriksa apa yang terjadi di sini.
Array pengindeksan tidak normal
V520 Operator koma ',' dalam ekspresi indeks larik '[- ββi2, - k]'. nw_spliced_aligner16.cpp 564
void CSplicedAligner16::x_DoBackTrace ( const Uint2* backtrace_matrix, CNWAligner::SAlignInOut* data, int i_global_max, int j_global_max) { .... while(intron_length < m_IntronMinSize || (Key & donor) == 0) { Key = backtrace_matrix[--i2, --k]; ++intron_length; data->m_transcript.push_back(eTS_Intron); } .... }
Saya harus mengatakan segera bahwa sepertinya tidak ada kesalahan (untuk saat ini, lol). Pertimbangkan baris berikut:
Key = backtrace_matrix[--i2, --k];
Kata 'matrix' dan pengindeksan ganda dapat menunjukkan bahwa array adalah dua dimensi, tetapi tidak. Ini adalah pointer reguler ke array bilangan bulat. Tetapi diagnostik
V520 tidak muncul begitu saja. Pemrogram benar-benar bingung tentang cara mengindeks array dua dimensi.
Dalam hal ini, penulis hanya memutuskan untuk menghemat satu baris kode, meskipun ia dapat menulis seperti ini:
--i2; Key = backtrace_matrix[--k];
V661 Ekspresi mencurigakan 'A [B == C]'. Mungkin berarti 'A [B] == C'. ncbi_service_connector.c 180
static EHTTP_HeaderParse s_ParseHeader(const char* header, ....) { .... if (sscanf(header, "%u.%u.%u.%u%n", &i1, &i2, &i3, &i4, &n) < 4 || sscanf(header + n, "%hu%x%n", &uuu->port, &tkt, &m) < 2 || (header[m += n] && !(header[m] == '$') && !isspace((unsigned char)((header + m) [header[m] == '$'])))) { break; } .... }
Contoh lain dari kode di mana saya menghabiskan waktu lama mencoba memahami apa yang sedang terjadi: D. Fungsi
isspace () memeriksa karakter dengan indeks
m , tetapi jika karakter ini adalah '$', maka karakter dengan indeks
m + 1 diteruskan ke fungsi. Apalagi perbandingan dengan '$' sudah lebih dulu. Mungkin tidak ada kesalahan di sini, tetapi kode pasti dapat ditulis ulang lebih jelas.
V557 Array overrun dimungkinkan. Indeks 'baris' menunjuk di luar batas array. aln_reader.cpp 412
bool CAlnReader::x_IsGap(TNumrow row, TSeqPos pos, const string& residue) { if (m_MiddleSections.size() == 0) { x_CalculateMiddleSections(); } if (row > m_MiddleSections.size()) { return false; } if (pos < m_MiddleSections[row].first) { .... } .... }
Di sini ada kesalahan serius. Pemeriksaan indeks
baris yang benar harus seperti ini:
if (row >= m_MiddleSections.size()) { return false; }
Jika tidak, dimungkinkan untuk mengakses data di luar vektor
MiddleSections .
Banyak tempat seperti itu:
- V557 Array overrun dimungkinkan. Indeks 'i' menunjuk di luar batas array. resource_pool.hpp 388
- V557 Array overrun dimungkinkan. Indeks 'baris' menunjuk di luar batas array. aln_reader.cpp 418
- V557 Array overrun dimungkinkan. Indeks 'fmt_idx' menunjuk di luar batas array. seq_writer.cpp 384
- V557 Array overrun dimungkinkan. Indeks 'fmt_idx' menunjuk di luar batas array. blastdb_formatter.cpp 183
- V557 Array overrun dimungkinkan. Indeks 'num' menunjuk di luar batas array. newcleanupp.cpp 13035
Cara mendapatkan ketidakpercayaan fungsi
V570 Variabel 'm_onClickFunction' ditugaskan untuk dirinya sendiri. alngraphic.hpp 103
void SetOnClickFunctionName(string onClickFunction) { m_onClickFunction = m_onClickFunction; }
Bahkan tidak ada yang bisa dikomentari. Anda hanya dapat bersimpati dengan orang yang mengklik sesuatu, mengklik, tetapi tidak ada yang berubah.
Dua kasus penugasan variabel untuk saya sendiri akan menghasilkan daftar:
- V570 Variabel 'iter-> level' ditugaskan untuk dirinya sendiri. align_format_util.cpp 189
- V570 Variabel 'd_elements_values ββ[ind]' ditugaskan untuk dirinya sendiri. sls_alp_data.cpp 1416
V763 Parameter 'w1' selalu ditulis ulang di badan fungsi sebelum digunakan. bmfunc.h 5363
Sebuah fungsi di mana argumen menjadi usang segera setelah memasuki fungsi dapat menyesatkan para pengembang yang menggunakannya. Kode harus diperiksa ulang.
Kesalahan Desain Kelas
V688 Argumen fungsi 'm_qsrc' memiliki nama yang sama dengan salah satu anggota kelas, yang dapat mengakibatkan kebingungan. compart_matching.cpp 873
class CElementaryMatching: public CObject { .... ISequenceSource * m_qsrc; .... void x_CreateIndex (ISequenceSource *m_qsrc, EIndexMode index_more, ....); void x_CreateRemapData(ISequenceSource *m_qsrc, EIndexMode mode); void x_LoadRemapData (ISequenceSource *m_qsrc, const string& sdb); .... };
Segera 3 fungsi kelas berisi argumen yang namanya bertepatan dengan bidang kelas. Ini dapat menyebabkan kesalahan dalam badan fungsi: programmer mungkin berpikir bahwa ia bekerja dengan anggota kelas, sebenarnya mengubah nilai variabel lokal.
V614 Variabel tidak diinisialisasi 'm_BitSet' digunakan. SnpBitAttributes.hpp 187
Salah satu konstruktor dengan
sembrono bekerja dengan variabel
m_BitSet . Faktanya adalah bahwa variabel tersebut tidak diinisialisasi. Nilai "sampah" digunakan pada iterasi pertama loop, setelah inisialisasi terjadi. Ini adalah kesalahan yang sangat serius, yang mengarah ke perilaku program yang tidak ditentukan.
V603 Objek telah dibuat tetapi tidak sedang digunakan. Jika Anda ingin memanggil konstruktor, 'this-> SIntervalComparisonResult :: SIntervalComparisonResult (....)' harus digunakan. compare_feats.hpp 100
Dulu sekali saya tidak menemui kesalahan seperti itu saat memeriksa proyek. Namun masalahnya masih relevan. Kesalahannya adalah bahwa memanggil konstruktor parameter dengan cara ini membuat dan menghapus objek sementara. Dan bidang kelas tetap tidak diinisialisasi. Konstruktor lain harus dipanggil melalui daftar inisialisasi (lihat
Mendelegasikan konstruktor ).
Fungsi V591 Non-void harus mengembalikan nilai. bio_tree.hpp 266
Penganalisa menganggap bahwa garis hilang dalam pernyataan kelebihan beban:
return *this;
V670 Anggota kelas yang tidak diinisialisasi 'm_OutBlobIdOrData' digunakan untuk menginisialisasi anggota 'm_StdOut'. Ingat bahwa anggota diinisialisasi dalam urutan deklarasi mereka di dalam kelas. remote_app.hpp 215
class NCBI_XCONNECT_EXPORT CRemoteAppResult { public: CRemoteAppResult(CNetCacheAPI::TInstance netcache_api, size_t max_inline_size = kMaxBlobInlineSize) : m_NetCacheAPI(netcache_api), m_RetCode(-1), m_StdOut(netcache_api, m_OutBlobIdOrData, m_OutBlobSize), m_OutBlobSize(0), m_StdErr(netcache_api, m_ErrBlobIdOrData, m_ErrBlobSize), m_ErrBlobSize(0), m_StorageType(eBlobStorage), m_MaxInlineSize(max_inline_size) { } .... };
3 peringatan penganalisa segera dikeluarkan untuk fragmen kode ini. Bidang kelas diinisialisasi bukan dalam urutan yang tercantum dalam daftar inisialisasi, tetapi dengan cara mereka dinyatakan dalam kelas. Penyebab klasik kesalahan ini adalah bahwa tidak semua programmer ingat atau tahu tentang aturan ini. Di sini dan di daftar inisialisasi hanya urutan yang salah. Orang mendapat perasaan bahwa daftar bidang dimasukkan dalam urutan acak.
V746 Mengiris objek. Pengecualian harus ditangkap dengan referensi bukan oleh nilai. cobalt.cpp 247
void CMultiAligner::SetQueries(const vector< CRef<objects::CBioseq> >& queries) { .... try { seq_loc->SetId(*it->GetSeqId()); } catch (objects::CObjMgrException e) { NCBI_THROW(CMultiAlignerException, eInvalidInput, (string)"Missing seq-id in bioseq. " + e.GetMsg()); } m_tQueries.push_back(seq_loc); .... }
Menangkap pengecualian berdasarkan nilai dapat menyebabkan hilangnya beberapa informasi tentang pengecualian karena pembuatan objek baru. Jauh lebih baik dan lebih aman untuk menangkap pengecualian dengan referensi.
Tempat serupa:
- V746 Mengiris objek. Pengecualian harus ditangkap dengan referensi bukan oleh nilai. agp_validate_reader.cpp 562
- V746 Mengiris objek. Pengecualian harus ditangkap dengan referensi bukan oleh nilai. aln_build_app.cpp 320
- V746 Mengiris objek. Pengecualian harus ditangkap dengan referensi bukan oleh nilai. aln_test_app.cpp 458
- V746 Mengiris objek. Pengecualian harus ditangkap dengan referensi bukan oleh nilai. cobalt.cpp 691
- V746 Mengiris objek. Pengecualian harus ditangkap dengan referensi bukan oleh nilai. cobalt.cpp 719
- V746 Mengiris objek. Pengecualian harus ditangkap dengan referensi bukan oleh nilai. cobalt.cpp 728
- V746 Mengiris objek. Pengecualian harus ditangkap dengan referensi bukan oleh nilai. cobalt.cpp 732
Tentang kode yang tidak terjangkau dan masalah eksekusi kode lainnya
V779 Kode tidak terjangkau terdeteksi. Mungkin saja ada kesalahan. merge_tree_core.cpp 627
bool CMergeTree::x_FindBefores_Up_Iter(....) { .... FirstFrame->Curr = StartCurr; FirstFrame->Returned = false; FirstFrame->VisitCount = 0; FrameStack.push_back(FirstFrame); while(!FrameStack.empty()) { .... if(Rel == CEquivRange::eAfter) { Frame->Returned = false; FrameStack.pop_back(); continue; } else if(Rel == CEquivRange::eBefore) { .... continue; } else { if(Frame->VisitCount == 0) { .... continue; } else { .... continue; } } Frame->Returned = false;
Kode pernyataan kondisional ditulis sehingga benar-benar semua cabang kode diakhiri dengan pernyataan
continue . Hal ini menyebabkan beberapa baris pembentukan kode yang tidak terjangkau dalam
loop while . Garis-garis ini terlihat sangat mencurigakan. Kemungkinan besar, masalah ini muncul setelah refactoring kode, dan sekarang membutuhkan tinjauan kode yang cermat.
V519 Variabel 'interval_width' diberi nilai dua kali berturut-turut. Mungkin ini sebuah kesalahan. Periksa baris: 454, 456. aln_writer.cpp 456
void CAlnWriter::AddGaps(....) { .... switch(exon_chunk->Which()) { case CSpliced_exon_chunk::e_Match: interval_width = exon_chunk->GetMatch(); case CSpliced_exon_chunk::e_Mismatch: interval_width = exon_chunk->GetMismatch(); case CSpliced_exon_chunk::e_Diag: interval_width = exon_chunk->GetDiag(); genomic_string.append(....); product_string.append(....); genomic_pos += interval_width; product_pos += interval_width/res_width; break; .... } .... }
Variabel
interval_width ditimpa beberapa kali, karena tidak ada pernyataan
istirahat di cabang
kasus . Meski klasik, tapi kesalahannya sangat buruk.
Beberapa tempat mencurigakan:
- V779 Kode tidak terjangkau terdeteksi. Mungkin saja ada kesalahan. dbapi_driver_utils.cpp 351
- V779 Kode tidak terjangkau terdeteksi. Mungkin saja ada kesalahan. net.c 780
- V779 Kode tidak terjangkau terdeteksi. Mungkin saja ada kesalahan. bcp.c 1495
- V779 Kode tidak terjangkau terdeteksi. Mungkin saja ada kesalahan. remote_blast.cpp 1470
- V779 Kode tidak terjangkau terdeteksi. Mungkin saja ada kesalahan. remote_blast.cpp 1522
V571 Cek berulang. Kondisi 'if (m_QueryOpts-> filtering_options)' sudah diverifikasi di baris 703. blast_options_local_priv.hpp 713
inline void CBlastOptionsLocal::SetFilterString(const char* f) { .... if (m_QueryOpts->filtering_options)
Jelas, cabang
lain membutuhkan penulisan ulang. Saya punya beberapa ide yang ingin saya lakukan dengan pointer
m_QueryOpts-> filtering_options , tetapi kodenya entah bagaimana masih membingungkan. Saya memohon kepada penulis kode.
Nah, masalahnya tidak datang sendiri:
- V571 Cek berulang. Kondisi 'if (sleeptime)' sudah diverifikasi di baris 205. request_control.cpp 208
- V571 Cek berulang. Kondisi 'jika (assignValue.empty ())' sudah diverifikasi di baris 712. classstr.cpp 718
Kesalahan Baca Data
V739 EOF tidak boleh dibandingkan dengan nilai tipe 'char'. 'Linestring [0]' harus dari tipe 'int'. alnread.c 3509
static EBool s_AfrpInitLineData( .... char* linestring = readfunc (pfile); .... while (linestring != NULL && linestring [0] != EOF) { s_TrimSpace (&linestring); .... } .... }
Karakter yang Anda rencanakan untuk dibandingkan dengan EOF tidak boleh disimpan dalam variabel
char . Jika tidak, ada risiko bahwa karakter dengan nilai 0xFF (255) akan berubah menjadi -1 dan akan ditafsirkan dengan cara yang sama dengan akhir file (EOF). Juga (untuk berjaga-jaga) ada baiknya memeriksa implementasi fungsi
readfunc .
V663 Infinite loop dimungkinkan. Kondisi 'cin.eof ()' tidak cukup untuk memutuskan dari loop.
Pertimbangkan untuk menambahkan pemanggilan fungsi 'cin.fail ()' ke ekspresi kondisional. ncbicgi.cpp 1564 typedef std::istream CNcbiIstream; void CCgiRequest::Serialize(CNcbiOstream& os) const { .... CNcbiIstream* istrm = GetInputStream(); if (istrm) { char buf[1024]; while(!istrm->eof()) { istrm->read(buf, sizeof(buf)); os.write(buf, istrm->gcount()); } } }
Penganalisa mendeteksi kesalahan potensial karena loop infinite dapat terjadi. Jika kegagalan terjadi saat membaca data, memanggil fungsi eof () akan selalu kembali salah . Untuk menyelesaikan loop dalam kasus ini, diperlukan pemeriksaan tambahan dari nilai yang dikembalikan oleh fungsi gagal () .Kesalahan lain-lain
V502 Mungkin operator '?:' Bekerja dengan cara yang berbeda dari yang diharapkan. Operator '?:' Memiliki prioritas lebih rendah daripada operator '&&'. ncbi_connutil.c 1135 static const char* x_ClientAddress(const char* client_host, int local_host) { .... if ((client_host == c && x_IsSufficientAddress(client_host)) || !(ip = *c && !local_host ? SOCK_gethostbyname(c) : SOCK_GetLocalHostAddress(eDefault)) || SOCK_ntoa(ip, addr, sizeof(addr)) != 0 || !(s = (char*) malloc(strlen(client_host) + strlen(addr) + 3))) { return client_host; } .... }
Perhatikan ungkapan: !local_host ? SOCK_gethostbyname(c) : SOCK_GetLocalHostAddress(eDefault)
Itu tidak dihitung seperti yang diharapkan oleh programmer, karena seluruh ekspresi seperti ini: ip = *c && !local_host ? SOCK_gethostbyname(c) : SOCK_GetLocalHostAddress(...)
Prioritas && operator lebih tinggi daripada ?: . Karena alasan ini, kode tidak dieksekusi sebagaimana dimaksud.V561 Mungkin lebih baik untuk memberikan nilai pada variabel 'seq' daripada mendeklarasikannya lagi. Deklarasi sebelumnya: validator.cpp, baris 490. validator.cpp 492 bool CValidator::IsSeqLocCorrectlyOrdered(const CSeq_loc& loc, CScope& scope) { CBioseq_Handle seq; try { CBioseq_Handle seq = scope.GetBioseqHandle(loc); } catch (CObjMgrException& ) {
Karena programmer mendeklarasikan variabel seq baru di dalam bagian try / catch, variabel seq lainnya tetap tidak diinisialisasi dan digunakan di bawah ini dalam kode.V562 Aneh membandingkan nilai tipe bool dengan nilai 0: (((status) & 0x7f) == 0)! = 0. ncbi_process.cpp 111 bool CProcess::CExitInfo::IsExited(void) const { EXIT_INFO_CHECK; if (state != eExitInfo_Terminated) { return false; } #if defined(NCBI_OS_UNIX) return WIFEXITED(status) != 0; #elif defined(NCBI_OS_MSWIN)
Tidak ada yang sakit, tapi WIFEXITED ternyata menjadi pembukaan makro dengan cara ini: return (((status) & 0x7f) == 0) != 0;
Ternyata fungsi mengembalikan nilai yang berlawanan.Dalam kode tersebut, ada fungsi lain seperti itu, yang mengeluarkan peringatan:- V562 Aneh membandingkan nilai tipe bool dengan nilai 0. ncbi_process.cpp 126
V595 Pointer 'dst_len' digunakan sebelum diverifikasi terhadap nullptr. Periksa baris: 309, 315. zlib.cpp 309 bool CZipCompression::CompressBuffer( const void* src_buf, size_t src_len, void* dst_buf, size_t dst_size, size_t* dst_len) { *dst_len = 0;
Pointer dst_len ditereferensi pada awal fungsi, sementara lebih lanjut pada kode diperiksa untuk kesetaraan ke nol. Kesalahan telah dibuat dalam kode yang mengarah ke perilaku tidak terdefinisi jika pointer dst_len adalah nullptr .V590 Pertimbangkan untuk memeriksa ekspresi 'ch! =' \ 0 '&& ch ==' ''. Ekspresi berlebihan atau mengandung kesalahan cetak. cleanup_utils.cpp 580 bool Asn2gnbkCompressSpaces(string& val) { .... while (ch != '\0' && ch == ' ') { ptr++; ch = *ptr; } .... }
Kondisi untuk menghentikan loop hanya bergantung pada apakah karakter ch adalah spasi atau tidak. Ekspresi dapat disederhanakan sebagai berikut: while (ch == ' ') { .... }
Kesimpulan
Penggunaan program komputer dalam penelitian ilmiah membantu dan akan membantu membuat penemuan. Mari kita berharap bahwa yang sangat penting tidak akan terlewatkan karena kesalahan ketik.Saya mengundang pengembang proyek NCBI Genome Workbench untuk menghubungi kami, dan kami akan memberikan laporan lengkap yang dikeluarkan oleh penganalisa PVS-Studio.Semoga penelitian kode kecil ini membantu memperbaiki banyak bug dan umumnya meningkatkan keandalan proyek. Coba jalankan PVS-Studio pada kode proyek Anda, jika Anda belum melakukannya. Anda mungkin menyukainya :).
Jika Anda ingin berbagi artikel ini dengan audiens yang berbahasa Inggris, silakan gunakan tautan ke terjemahan: Svyatoslav Razmyslov. NCBI Genome Workbench: Riset Ilmiah di Bawah Ancaman