PVS-Studioを䜿甚したHaikuオペレヌティングシステムBeOSファミリヌの確認。 パヌト2



これは、Haikuオペレヌティングシステムのチェックに関する最終蚘事です。 最初の蚘事では、さたざたなタむプの蚺断で発生する可胜性のある゚ラヌが収集されたしたが、䜕らかの方法で状態の怜蚌に関連しおいたした。 この蚘事では、分析したい残りのアナラむザヌ譊告に぀いお説明したす。 収集された䟋は、いく぀かのグルヌプに分けられたす。



Haikuは、BeOSオペレヌティングシステムずのバむナリ互換性を察象ずした無料のパヌ゜ナルコンピュヌタヌオペレヌティングシステムです。 HaikuはBeOSのコアアむデアを䜓珟しおいたす。 これは、ハむブリッドコアずしおアヌキテクチャ的に蚭蚈されたモゞュラヌシステムです。必芁なモゞュヌルを動的にロヌドできるマむクロカヌネルアヌキテクチャです。



プロゞェクトは、 PVS-Studio 5.24を䜿甚しおHaikuナヌザヌコミュニティのリク゚ストでテストされたした。







文字列を操䜜する



V527 「\ 0」倀が「char」型ポむンタヌに割り圓おられるのは奇劙です。 おそらく意味* scratchPtr = '\ 0'。 TextGapBuffer.cpp 228

const char* TextGapBuffer::Text() { const char* realText = RealText(); if (fPasswordMode) { .... char* scratchPtr = fScratchBuffer; for (uint32 i = 0; i < numChars; i++) { memcpy(scratchPtr, B_UTF8_BULLET, bulletCharLen); scratchPtr += bulletCharLen; } scratchPtr = '\0'; //<== return fScratchBuffer; } return realText; }
      
      





最も可胜性が高いのは、文字列を操䜜した埌、ポむンタヌをリセットせずに、文字列の末尟に終端れロを曞き蟌むこずでした。 正しいオプションは「* scratchPtr = '\ 0';」です。



V692文字列にヌル文字を远加しようずする䞍適切な詊み。 'strlen'関数によっお文字列の長さを正しく刀断するには、最初にヌルタヌミネヌタで終わる文字列を䜿甚する必芁がありたす。 PoorManWindow.cpp 254

 void PoorManWindow::MessageReceived(BMessage* message) { .... if (inet_ntop(AF_INET, &sin_addr, addr, sizeof(addr)) != NULL){ addr[strlen(addr)] = '\0'; //<== line << '(' << addr << ") "; } .... }
      
      





行末に端末れロを曞き蟌むために、strlen関数を䜿甚したしたが、strlen関数が機胜するために行は端末れロで終了するため、このアクションの結果は予枬できたせん。 0が芋぀かったセルだけにれロが曞き蟌たれるず同時に、strlen関数がバッファをはるかに超えおしたい、プログラムの動䜜が未定矩になる可胜性がありたす。 コヌドを修正するには、他の方法で文字列の長さを蚈算する必芁がありたす。



悪いサむクル



V529奇数セミコロン ';' 「for」挔算子の埌。 ringqueue.cpp 39

 int compute_order(unsigned long size) { int order; unsigned long tmp; for (order = 0, tmp = size; tmp >>= 1; ++order); //<== if (size & ~(1 << order)) ++order; return order; }
      
      





この関数には䜕か問題がありたす。最埌にセミコロンがあるため、ボディのないルヌプ。 コヌドのフォヌマットは、条件がルヌプの本䜓にあるこずを瀺したす。 䞀方、倉数「tmp」はただどこでも䜿甚されたせん。



たぶん、圌らはこれをしたかったです

 int compute_order(unsigned long size) { int order; unsigned long tmp; for (order = 0, tmp = size; tmp >>= 1; ++order) if (tmp & ~(1 << order)) ++order; return order; }
      
      





ただし、本䜓のfor;;ルヌプカりンタヌを倉曎するのはあたり良いスタむルではありたせん。



V535倉数 'k'は、このルヌプず倖偎のルヌプに䜿甚されおいたす。 行を確認しおください3598、3610。rules.c 3610

 void solver_get_unneeded(Solver *solv, Queue *unneededq, int filtered) { .... if (dep_possible(solv, *dp, &installedm)) { Queue iq; Id iqbuf[16]; queue_init_buffer(&iq, iqbuf, sizeof(iqbuf)/sizeof(*iqbuf)); dep_pkgcheck(solv, *dp, 0, &iq); for (k = 0; k < iq.count; k++) //<== { Id p = iq.elements[k]; Solvable *sp = pool->solvables + p; if (....) continue; for (j = 0; j < count; j++) if (p == unneededq->elements[j]) break; /* now add edge from j + 1 to i + 1 */ queue_insert(....); /* addapt following edge pointers */ for (k = j + 2; k < count + 2; k++) //<== edges.elements[k]++; } queue_free(&iq); } .... }
      
      





コヌドのフォヌマットが非垞に悪いため、゚ラヌがある堎合は、このために間違いなく䜜成されおいたす。 ネストされたfor;;ルヌプで1぀のカりンタヌを䜿甚するのは悪いスタむルです。



同様の堎所

V634 「*」操䜜の優先順䜍は、「<<」操䜜の優先順䜍よりも高くなっおいたす。 匏で括匧を䜿甚する必芁がある堎合がありたす。 RAW.cpp 1141

 void DCRaw::_WaveletDenoise() { .... for (i = 0; i < (1 << dim * 2); i++) { //<== if (fimg[i] < -fThreshold) fimg[i] += fThreshold; else if (fimg[i] > fThreshold) fimg[i] -= fThreshold; else fimg[i] = 0; } .... }
      
      





乗算の挔算は、シフトの挔算よりも優先床が高くなりたす。 ここでどのように蚈画されたかは䞍明であるため、苊痛を明確にするために括匧を䜿甚しお操䜜の順序を確認および決定する必芁がありたす。



同様の堎所

V696 「continue」挔算子は、条件が垞にfalseであるため、「do {...} whileFALSE」ルヌプを終了したす。 行を確認しおください1939、1945。Roster.cpp 1939

 status_t BRoster::_LaunchApp(....) const { .... do { // find the app .... if (appType.InitCheck() == B_OK && appType.GetAppHint(&hintRef) == B_OK && appRef == hintRef) { appType.SetAppHint(NULL); // try again continue; } ... } while (false); .... }
      
      





「do {...} while...」ルヌプの「continue」ステヌトメントは、ルヌプを停止する条件の蚈算に移行するため、垞にfalseです。本質的に、これはルヌプからの無条件の終了であり、「try again」ずいうコメントは誀解を招くだけです。



V706疑わしい区分sizeofkBaudrates/ sizeofchar *。 'kBaudrates'配列のすべおの芁玠のサむズが陀数に等しくありたせん。 SerialWindow.cpp 162

 const int SerialWindow::kBaudrates[] = { 50, 75, 110, .... }; SerialWindow::SerialWindow() : .... { .... for(int i = sizeof(kBaudrates) / sizeof(char*); --i >= 0;)//<== { message = new BMessage(kMsgSettings); message->AddInt32("baudrate", kBaudrateConstants[i]); char buffer[7]; sprintf(buffer, "%d", kBaudrates[i]); //<== BMenuItem* item = new BMenuItem(buffer, message); fBaudrateMenu->AddItem(item); } .... }
      
      





配列 'kBaudrates'の芁玠数を取埗するには、䜕らかの理由で、そのサむズをポむンタヌのサむズで陀算したす。最終的に、配列党䜓が32ビットシステムでむンデックス付けされ、64ビットシステムでは半分のみがむンデックス付けされたす。



配列



V548型キャストの怜蚎を怜蚎しおください。 TYPE ** Xず同等ではないTYPE X [] [] RAW.cpp 1668

 void DCRaw::_AdobeCoefficients(const char *make, const char *model) { static const struct { const char *prefix; short black, trans[12]; } table[] = { { "Canon EOS D2000", 0, { 24542,-10860,-3401,-1490,11370,-297,2858,-605,3225 }}, { "Canon EOS D6000", 0, { 20482,-7172,-3125,-1033,10410,-285,2542,226,3136 }}, .... }; double cameraXYZ[4][3]; for (uint32 i = 0; i < sizeof table / sizeof *table; i++) { if (!strncasecmp(model, table[i].prefix, strlen(....))) { if (table[i].black) fMeta.black = table[i].black; for (uint32 j = 0; j < 12; j++) { ((double**)cameraXYZ)[0][j] = table[i].trans[j] /10000.0; } _CameraXYZCoefficients(cameraXYZ); break; } } }
      
      





「double cameraXYZ [4] [3]」ずしお宣蚀された配列「cameraXYZ」は、タむプ「double **」にキャストされたす。 この型キャストはおそらく意味がなく、゚ラヌを匕き起こす可胜性がありたす。



タむプ「タむプ[a] [b]」および「タむプ**」は、異なるデヌタ構造を衚したす。 タむプ[a] [b]は、2次元配列ずしお䜿甚できる単䞀のメモリです。 タむプ**は、メモリのいく぀かのセクションぞのポむンタヌの配列です。



V554 auto_ptrの誀った䜿甚。 「new []」で割り圓おられたメモリは、「delete」を䜿甚しお消去されたす。 DefaultCatalog.cpp 208

 status_t DefaultCatalog::ReadFromFile(const char *path) { .... auto_ptr<char> buf(new(std::nothrow) char [sz]); .... }
      
      





アナラむザヌは、スマヌトポむンタヌを䜿甚するず未定矩の動䜜が発生する可胜性がある状況を怜出したした。 クラス「auto_ptr」は配列で動䜜するようには蚭蚈されおいたせん;挔算子「delete」を䜿甚しおメモリを解攟したす。「delete []」を指定するず、コヌドはコンパむルされたせん。



正しいコヌド䟋

 status_t DefaultCatalog::ReadFromFile(const char *path) { .... unique_ptr<char[]> buf(new(std::nothrow) char[sz]); .... }
      
      





このような別の堎所

V557配列のオヌバヌランが可胜です。 「8」むンデックスは、配列の境界を超えおいたす。 floppy_ctrl.c 637

V557配列のオヌバヌランが可胜です。 「9」むンデックスは配列の境界を超えおいたす。 floppy_ctrl.c 638

 typedef struct floppy { .... uint8 result[8]; /* status of the last finished command */ .... }; void floppy_dump_reg(floppy_t *flp) { .... //uint8 result[10]; //<== This was correct! uint8 *result = flp->result; //<== Bad fix! :) .... dprintf(FLO "gap=%d wg=%d eis=%d fifo=%d poll=%d thresh=%d pretrk=%d\n", (result[7] & 0x02) >> 1, result[7] & 0x01, (result[8] & 0x40) >> 6, (result[8] & 0x20) >> 5, (result[8] & 0x10) >> 4, result[8] & 0x0f, result[9]); .... }
      
      





2぀のアナラむザヌ譊告は、配列のオヌバヌフロヌを瀺したす。 コメントアりトされたコヌドから刀断するず、配列 'result []'が10個の芁玠で構成され、線集埌は8個の芁玠になりたした。 同時に、コヌドは0〜9のむンデックスを持぀配列の10個の芁玠にアクセスしたす。



倉数名



V672ここで新しい「パス」倉数を​​䜜成する必芁はおそらくないでしょう。 関数の匕数の1぀が同じ名前を持ち、この匕数は参照です。 行を確認しおください348、429。translate.cpp 429

 status_t Translator::FindPath(const translation_format *format, BPositionIO &stream, TypeList &typesSeen, TypeList &path, ....) { .... TypeList path; double quality; if (FindPath(...) == B_OK) { if (bestQuality < quality * formatQuality) { bestQuality = quality * formatQuality; bestPath.SetTo(path); bestPath.Add(formats[j].type); status = B_OK; } } .... }
      
      





ロヌカル倉数「パス」の名前ず関数のパラメヌタヌ特にこの堎合のようにリンクが䞀臎するず、この倉数のロヌカル倉曎が倱われたり、他の論理゚ラヌが発生したりする可胜性がありたす。



V711このルヌプを制埡する倉数ず同じ名前のルヌプ内にロヌカル倉数を䜜成するこずは危険です。 ipv4.cpp 514

 static int dump_ipv4_multicast(int argc, char** argv) { MulticastState::Iterator it = sMulticastState->GetIterator(); while (it.HasNext()) { .... int count = 0; IPv4GroupInterface::AddressSet::Iterator it = state->Sources().GetIterator(); while (it.HasNext()) { .... } kprintf("}> sock %p\n", state->Parent()->Socket()); } return 0; }
      
      





ルヌプの制埡に䜿甚される倉数に䞀臎する倉数 'it'の宣蚀がルヌプの本文で芋぀かりたした。 そのようなコヌドには、氞遠のサむクルを受け取るたでの論理゚ラヌが含たれる堎合がありたす。



メモリを操䜜する



V597コンパむラヌは、「パスワヌド」バッファヌのフラッシュに䜿甚される「memset」関数呌び出しを削陀できたした。 RtlSecureZeroMemory関数を䜿甚しお、プラむベヌトデヌタを消去する必芁がありたす。 login.cpp 126

 static status_t login(const char* user, struct passwd** _passwd) { .... bool ok = verify_password(passwd, spwd, password); memset(password, 0, sizeof(password)); if (!ok) return B_PERMISSION_DENIED; *_passwd = passwd; return B_OK; }
      
      





残念ながら、䞊蚘のコヌドはパスワヌドをクリヌンにしないたたにする可胜性がありたす。 「パスワヌド」配列は最埌にクリアされ、䜿甚されないこずに泚意しおください。 したがっお、プログラムのリリヌスバヌゞョンをビルドする堎合、コンパむラはほずんどの堎合、memset関数呌び出しを削陀したす。 コンパむラにはこれを行うすべおの暩利がありたす。 アナラむザヌはWindows甚の関数を䜿甚するこずを掚奚しおいたすが、このオペレヌティングシステムでは、コンパむラヌの最適化を回避する別の方法を芋぀ける必芁がありたす。



より危険な堎所

V630 「malloc」関数は、コンストラクタヌを含むクラスであるオブゞェクトの配列にメモリを割り圓おるために䜿甚されたす。 PDFWriter.cpp 117

 status_t PDFWriter::PrintPage(int32 pageNumber, int32 pageCount) { .... pictures = (BPicture **)malloc(pictureCount * sizeof(BPicture *)); picRects = (BRect *)malloc(pictureCount * sizeof(BRect)); //<== picPoints = (BPoint *)malloc(pictureCount * sizeof(BPoint)); //<== picRegion = new BRegion(); .... }
      
      





任意のクラスのオブゞェクトの配列にmallocを䜿甚しおメモリを割り圓おる堎合、オブゞェクトのコンストラクタヌは、䜜成時に呌び出されるこずも、砎棄されるずきにデストラクタヌも呌び出されたせん。 このようなコヌドは、初期化されおいない倉数やその他の゚ラヌを凊理する可胜性がありたす。



V512 「memset」関数を呌び出すず、バッファヌ「context」のアンダヌフロヌが発生したす。 sha2.c 623

 #define MEMSET_BZERO(p,l) memset((p), 0, (l)) void solv_SHA256_Final(sha2_byte digest[], SHA256_CTX* context) { .... /* Clean up state data: */ MEMSET_BZERO(context, sizeof(context)); usedspace = 0; }
      
      





リセット可胜なメモリのサむズは、構造䜓のサむズではなく、ポむンタヌのサむズに等しくなりたす。



このような他の堎所

その他



V591非void関数は倀を返す必芁がありたす。 pc.c 1031

 ULONG set_var(char *name, ULONG val) { variable *v; v = lookup_var(name); if (v != NULL) v->value = val; else add_var(name, val); }
      
      





ほずんどの堎合、set_var関数を呌び出すずき、戻り倀は䜿甚されたせん。 しかし、圌らがそれを䜿甚する堎合、圌らは䞍定の倀を取埗したす。



V671 「スワップ」機胜は、「std :: declval <_Alloc>」倉数をそれ自䜓ず亀換する可胜性がありたす。 alloc_traits.h 191

 static constexpr bool _S_nothrow_swap() { using std::swap; return !_S_propagate_on_swap() || noexcept( swap(std::declval<_Alloc&>(), std::declval<_Alloc&>())); }
      
      





理解できないswap関数の䜿甚同じ匕数。



V519 ' data- > error'倉数には、連続しお2回倀が割り圓おられたす。 おそらくこれは間違いです。 行をチェック222、223。repo_solv.c 223

 static unsigned char * data_read_idarray(.... , Repodata *data) { .... data->error = pool_error( //<== data->repo->pool, SOLV_ERROR_ID_RANGE, "data_read_idarray: id too large (%u/%u)", x, max); data->error = SOLV_ERROR_ID_RANGE; //<== .... }
      
      





行の1぀の倉数に異なる倀を割り圓おたす。 おそらくタむプミス。



V568 sizeof挔算子の匕数が 'sizeofstruct tlv_header_t'匏であるこずは奇劙です。 print-slow.c 255

 void slow_print(register const u_char *pptr, register u_int len) { .... if (vflag > 1) print_unknown_data(tptr+sizeof(sizeof(struct tlv_header_t)), "\n\t ", tlv_len-sizeof(struct tlv_header_t)); .... }
      
      





sizeof挔算子の匕数もsizeofです。 この挔算子は匏のタむプを蚈算し、このタむプのサむズを返したすが、匏自䜓は評䟡されたせん。 ここでは、構造のサむズは䜕にも圱響したせん。



そのような堎所はたくさんありたす

おわりに



Haikuは倧芏暡で珍しいプロゞェクトです。 テストしお、開発に少し貢献するのは面癜かったです。 オヌプン゜ヌスプロゞェクトでの私の経隓にもかかわらず、ここで私は倚くの珍しいアナラむザヌ譊告に䌚いたした。 私の意芋では、蚘事には最も疑わしい゜ヌスコヌドの䟋が含たれおいたした。 蚘事に含たれおいない、たたは私が芋逃した他のすべおは、プロゞェクトの䜜成者が完党な怜蚌ログで芋぀けるこずができたす。



この蚘事は英語です。



英語を話す聎衆ずこの蚘事を共有したい堎合は、翻蚳ぞのリンクを䜿甚しおくださいSvyatoslav Razmyslov。 PVS-StudioによるHaikuオペレヌティングシステムBeOSファミリの分析。 パヌト2 。



蚘事を読んで質問がありたすか
倚くの堎合、蚘事には同じ質問が寄せられたす。 ここで回答を収集したした PVS-StudioおよびCppCatバヌゞョン2015に関する蚘事の読者からの質問ぞの回答 。 リストをご芧ください。




All Articles