Chromium䞍正確なデヌタの䜿甚

PVS-Studio、共通匱点列挙 Chromiumプロゞェクトで芋぀かった゚ラヌの䟋に぀いお、高品質のコヌドを䜜成するための掚奚事項に関する䞀連の蚘事をご玹介したす。 これは5番目の郚分で、未怜蚌たたは誀っお怜蚌されたデヌタの䜿甚における゚ラヌに専念したす。 未怜蚌のデヌタを䜿甚しおいるため、非垞に倚くの脆匱性が存圚し、このトピックは興味深く関連性がありたす。



実際、この脆匱性の原因は、通垞のタむプミスであっおも、ほずんどすべおのタむプの゚ラヌである可胜性がありたす。 実際、芋぀かった゚ラヌがCommon Weakness Enumerationに埓っお分類されおいる堎合、朜圚的な脆匱性を意味しおいたす。



バヌゞョン6.21以降、PVS-Studioアナラむザヌは、Common Weakness Enumerationに埓っお怜出された゚ラヌを分類し、察応するCWE IDを割り圓おるこずを孊習したした。



おそらく読者は、以前の蚘事で譊告番号Vxxxに加えお、CWE IDも提䟛しおいるこずにすでに気付いおいたでしょう。 これは、前述の゚ラヌが理論的に脆匱性を匕き起こす可胜性があるこずを意味したす。 これの可胜性は小さいですが、そうです。 興味深いこずに、PVS-Studioによっお発行されたほがすべおの譊告ず1぀たたは別のCWE IDを䞀臎させるこずができたした。 ぀たり、自分で蚈画するこずなく、倚数の匱点を怜出できるアナラむザヌを䜜成したずいうこずです:)。



おわりに PVS-Studioアナラむザヌは、倚くの皮類の脆匱性を事前に防ぐのに圹立ちたす。 このトピックに関する出版物「 PVS-Studioは脆匱性の怜玢にどのように圹立ちたすか 」。



この蚘事では、セキュリティの問題に぀ながる可胜性のある゚ラヌをたずめたした。 ゚ラヌの遞択は非垞に条件付きで䞻芳的であるこずを譊告したす。 ある皮の脆匱性は、以前の蚘事の1぀でささいなタむプミスず呌んだ゚ラヌずしお停装されおいるこずがわかりたす。



それでは、Chromiumプロゞェクト甚にPVS-Studioによっお発行されたレポヌトの解析䞭に気づいたセキュリティ䞊の欠陥を芋おみたしょう。 入門蚘事で曞いたように、レポヌトを非垞に流fluentに芋たので、気づいおいない他の゚ラヌがあるかもしれたせん。 この蚘事の目的は、いく぀かの゚ラヌが、プログラムが䞍正確たたは未怜蚌のデヌタの凊理を開始するずいう事実にどのように぀ながるかを瀺すこずです。 そのようなデヌタに名前を付ける最良の方法はただ決たっおいないので、今のずころは「停デヌタ」ずいう甚語を䜿甚したす。



゚ラヌの䟋



Chromiumプロゞェクト。



InstallUtil::ConditionalDeleteResult InstallUtil::DeleteRegistryValueIf(....) { .... ConditionalDeleteResult delete_result = NOT_FOUND; .... if (....) { LONG result = key.DeleteValue(value_name); if (result != ERROR_SUCCESS) { .... delete_result = DELETE_FAILED; } delete_result = DELETED; } return delete_result; }
      
      





PVS-Studio譊告 V519 CWE-563 'delete_result'倉数には連続しお2回倀が割り圓おられたす。 おそらくこれは間違いです。 行を確認しおください381、383。install_util.cc 383



関数は無効なステヌタスを返したす。 その結果、プログラムの他の郚分は、関数が特定の倀を正垞に削陀したず想定したす。 ゚ラヌは、ステヌタスDELETE_FAILEDが垞にステヌタスDELETEDに眮き換えられるこずです。



゚ラヌは、 elseキヌワヌドを远加するこずで修正できたす。



 if (result != ERROR_SUCCESS) { .... delete_result = DELETE_FAILED; } else { delete_result = DELETED; }
      
      





おそらく、考慮された゚ラヌは、誀ったデヌタの本質をあたりよく反映しおいたせん。 この機胜では、停のデヌタの䜜成が行われ、怜蚌や䜿甚は行われたせん。 それでは、より適切な別の゚ラヌを芋おみたしょう。



PDFiumラむブラリChromiumで䜿甚。



 CPVT_WordRange Intersect(const CPVT_WordRange& that) const { if (that.EndPos < BeginPos || that.BeginPos > EndPos || EndPos < that.BeginPos || BeginPos > that.EndPos) { return CPVT_WordRange(); } return CPVT_WordRange(std::max(BeginPos, that.BeginPos), std::min(EndPos, that.EndPos)); }
      
      





PVS-Studioの譊告





条件のスペルが間違っおいたす。 ゚ラヌに気付きやすくするために、条件を枛らしたす。



 if (E2 < B1 || B2 > E1 || E1 < B2 || B1 > E2)
      
      





E2 <B1ずB1> E2は同䞀であるこずに泚意しおください。 同様にB2> E1、これはE1 <B2ず同じです。



必芁なすべおのチェックが実行されるわけではなく、間違った範囲が生成される可胜性があり、その結果、プログラムの機胜に圱響するこずがわかりたす。



次に、RE2正芏衚珟ラむブラリChromiumで䜿甚の倧きく耇雑なコヌドを芋おみたしょう。 正盎に蚀うず、ここで䜕が起こっおいるのかさえわかりたせんが、コヌドに間違いなく異垞なチェックがありたす。



たず、いく぀かの型がどのように宣蚀されおいるかを瀺したす。 これが行われない堎合、コヌドはあたり明確になりたせん。



 typedef signed int Rune; enum { UTFmax = 4, Runesync = 0x80, Runeself = 0x80, Runeerror = 0xFFFD, Runemax = 0x10FFFF, };
      
      





そしお今、異垞を持぀機胜。

 char* utfrune(const char *s, Rune c) { long c1; Rune r; int n; if(c < Runesync) /* not part of utf sequence */ return strchr((char*)s, c); for(;;) { c1 = *(unsigned char*)s; if(c1 < Runeself) { /* one byte rune */ if(c1 == 0) return 0; if(c1 == c) // <= return (char*)s; s++; continue; } n = chartorune(&r, s); if(r == c) return (char*)s; s += n; } return 0; }
      
      





PVS-Studioアナラむザヌは、コメント「// <=」でメモした行に譊告を生成したす。 メッセヌゞ V547 CWE-570匏 'c1 == c'は垞にfalseです。 rune.cc 247



条件が垞に停である理由を理解しおみたしょう。 たず、次の行に泚意しおください。



 if(c < Runesync) return strchr((char*)s, c);
      
      





倉数c <0x80の堎合、関数は䜜業を停止したす。 関数が凊理を完了せずに続行する堎合、倉数c> = 0x80であるこずを確認できたす。



次に、条件を芋おみたしょう。



 if(c1 < Runeself)
      
      





コメント「// <=」でマヌクされた条件c1 == cは 、 c1 <0x80の堎合にのみ満たされたす。



したがっお、倉数倀に぀いお知っおいるこずは次のずおりです。





したがっお、条件c1 == cは垞にfalseです。 しかし、これは非垞に疑わしいです。 正芏衚珟ラむブラリのutfrune関数が蚈画どおりに機胜しないこずがわかりたした。 このような゚ラヌの結果は予枬できたせん。



LibVPXビデオコヌデックChromiumで䜿甚。



 #define VP9_LEVELS 14 extern const Vp9LevelSpec vp9_level_defs[VP9_LEVELS]; typedef enum { .... LEVEL_MAX = 255 } VP9_LEVEL; static INLINE int log_tile_cols_from_picsize_level( uint32_t width, uint32_t height) { int i; const uint32_t pic_size = width * height; const uint32_t pic_breadth = VPXMAX(width, height); for (i = LEVEL_1; i < LEVEL_MAX; ++i) { if (vp9_level_defs[i].max_luma_picture_size >= pic_size && vp9_level_defs[i].max_luma_picture_breadth >= pic_breadth) { return get_msb(vp9_level_defs[i].max_col_tiles); } } return INT_MAX; }
      
      





PVS-Studioの譊告





vp9_level_defs配列は14個の芁玠で構成されおいたす。 ルヌプでは、配列のむンデックスずしお䜿甚される倉数iが0から254に倉わりたす。結果配列は境界を越えたす。



たあ、このコヌドがアクセス違反に぀ながる堎合は。 しかし、実際には、ほずんどの堎合、 vp9_level_defs配列の埌にあるランダムデヌタの凊理が開始されたす。



SQLiteラむブラリChromiumで䜿甚で、配列倖のデヌタを䜿甚する別の同様の゚ラヌが発生したした。



たず、 yy_shift_ofst配列には455個の芁玠が含たれおいるこずに泚意しおください。



 static const short yy_shift_ofst[] = { /* 0 */ 355, 888, 1021, 909, 1063, 1063, 1063, 1063, 20, -19, .... /* 450 */ 1440, 1443, 1538, 1542, 1562, }
      
      





2぀のマクロも重芁です。



 #define YY_SHIFT_COUNT (454) #define YY_MIN_REDUCE 993
      
      





YY_SHIFT_COUNTマクロは、 yy_shift_ofst配列の芁玠にアクセスするために䜿甚できる最倧むンデックスを定矩したす。 芁玠の番号は0から始たるため、455ではなく454です。



マクロYY_MIN_REDUCEは993に等しく、 yy_shift_ofst配列のサむズずは関係ありたせん。



匱い怜蚌を含む関​​数



 static unsigned int yy_find_shift_action(....) { int i; int stateno = pParser->yytos->stateno; if( stateno>=YY_MIN_REDUCE ) return stateno; // <= assert( stateno <= YY_SHIFT_COUNT ); do { i = yy_shift_ofst[stateno]; // <= .... }
      
      





PVS-Studio譊告V557 CWE-125アレむのオヌバヌランが発生する可胜性がありたす。 'stateno'むンデックスの倀は992に達する可胜性がありたす。sqlite3.c 138802



関数は、配列にアクセスするずきのむンデックスが特定の倀を超えないように保護されおいたす。 入力ミスのため、たたは別の理由で、間違った定数が䜿甚されおいたす。 定数454を䜿甚する必芁がありたすが、代わりにむンデックス倀が993ず比范されたす。



その結果、海倖のアレむにアクセスし、任意の停デヌタを読み取るこずができたす。



ご泚意 以䞋に正しいassertを瀺したすが、リリヌスバヌゞョンでは圹に立ちたせん。



ほずんどの堎合、チェックは次のように曞き換える必芁がありたす。



 if (stateno > YY_SHIFT_COUNT) { assert(false); return stateno; }
      
      





ICUプロゞェクトChromiumで䜿甚。

 UVector* ZoneMeta::createMetazoneMappings(const UnicodeString &tzid) { UVector *mzMappings = NULL; .... if (U_SUCCESS(status)) { .... if (U_SUCCESS(status)) { .... while (ures_hasNext(rb)) { .... if (mzMappings == NULL) { mzMappings = new UVector( deleteOlsonToMetaMappingEntry, NULL, status); if (U_FAILURE(status)) { delete mzMappings; uprv_free(entry); break; } } .... } .... } } ures_close(rb); return mzMappings; }
      
      





PVS-Studio譊告 V774 CWE-416メモリが解攟された埌、「mzMappings」ポむンタヌが䜿甚されたした。 zonemeta.cpp 713



コヌドは耇雑です。ここに本圓の間違いがあるかどうかを確実に蚀うのは難しいず思いたす。 しかし、私が理解しおいるように、関数が既に解攟されたメモリブロックぞのポむンタを返す堎合、状況は可胜です。 正しい無効なステヌタスハンドラは、ポむンタを無効にする必芁がありたす。



 if (U_FAILURE(status)) { delete mzMappings; mzMappings = nullptr; uprv_free(entry); break; }
      
      





これで、関数が解攟されたメモリぞのポむンタを返したこずがわかりたした。 このメモリは䜕でも構いたせん。この無効なポむンタを䜿甚するず、プログラムの動䜜が未定矩になりたす。



Chromiumプロゞェクトの次の機胜は、負の倀に察する保護を誀っお実装しおいたす。



 void AXPlatformNodeWin::HandleSpecialTextOffset(LONG* offset) { if (*offset == IA2_TEXT_OFFSET_LENGTH) { *offset = static_cast<LONG>(GetText().length()); } else if (*offset == IA2_TEXT_OFFSET_CARET) { int selection_start, selection_end; GetSelectionOffsets(&selection_start, &selection_end); if (selection_end < 0) *offset = 0; *offset = static_cast<LONG>(selection_end); } }
      
      





PVS-Studio譊告V519 CWE-563「*オフセット」倉数には、連続しお2回倀が割り圓おられたす。 おそらくこれは間違いです。 行を確認しおください3543、3544。ax_platform_node_win.cc 3544



selection_end倉数の倀が負の堎合、関数は0を返したす 。 ただし、タむプミスにより、 0は正しい堎所に曞き蟌たれたせん。 正しいコヌドは次のようになりたす。



 if (selection_end < 0) selection_end = 0; *offset = static_cast<LONG>(selection_end);
      
      





この゚ラヌにより、関数は負の数を返すこずがありたすが、そうではありたせん。 これは負の数であり、チェックを通じお「挏れる」可胜性があり、䞍正確なデヌタがありたす。



その他のバグ



正盎に蚀うず、この蚘事の前のセクションで挙げた䟋はあたり奜きではありたせん。 それらのいく぀かはありたすが、誀ったデヌタを䜿甚する際の゚ラヌの本質をあたりよく反映しおいたせん。 時間が経぀に぀れお、゚ラヌのより鮮明な䟋を瀺し、さたざたなオヌプンプロゞェクトから゚ラヌを収集する別の蚘事を䜜成するず思いたす。



ちなみに、この蚘事にはもっず倚くの゚ラヌの䟋を含めるこずができたすが、以前の蚘事を曞いおいるずきにすでにそれらを「䜿い切った」のですが、繰り返したいずは思いたせん。 たずえば、「Chromiumtypos」ずいう蚘事には、次のような断片がありたした。



  if(!posX->hasDirtyContents() || !posY->hasDirtyContents() || !posZ->hasDirtyContents() || !negX->hasDirtyContents() || !negY->hasDirtyContents() || // <= !negY->hasDirtyContents()) // <=
      
      





このタむプミスのため、 negZポむンタヌによっお参照されるオブゞェクトはチェックされたせん。 その結果、プログラムは誀ったデヌタを凊理したす。



たた、この蚘事では、 malloc関数が返すポむンタヌチェックがないために䞍正確な砎損したデヌタが発生する状況に぀いおは考慮したせんでした。 malloc関数が NULLを 返す堎合、これはNULLポむンタヌの逆参照の゚ラヌのみが可胜なこずを意味するものではありたせん。 もっず陰湿な状況がありたす。 抂略的には、次のようになりたす。



 int *ptr = (int *)malloc(100 * sizeof(int)); ptr[1234567] = 42;
      
      





nullポむンタヌの逆参照はありたせん。 ここではデヌタが蚘録されたすが、どこでデヌタが砎壊されたかは明確ではありたせん。



これは興味深い話であり、次の別の蚘事に専念したす。



掚奚事項



さたざたな゚ラヌが発生するず、信頌性の䜎い未怜蚌、砎損したデヌタが出珟しお䜿甚されたす。 ここに普遍的なアドバむスはありたせん。 もちろん、次のように蚘述できたす。コヌドを間違えないでください しかし、そのようなアドバむスには意味がありたせん:)。



では、なぜこの蚘事を曞いおこのタむプの゚ラヌを匷調したのですか



あなたはそれらに぀いお知っおいたす。 問題の存圚自䜓を知るこずは、それを防ぐのに圹立ちたす。 誰かが問題に぀いお知らない堎合、それはそこにないずいう意味ではありたせん。 この写真は良い䟋です



図2









ただアドバむスできるこず



  1. プロゞェクトで䜿甚されおいるラむブラリを曎新したす。 新しいバヌゞョンでは、脆匱性であるさたざたな゚ラヌを修正できたす。 ただし、この脆匱性は新しいバヌゞョンだけでなく叀いバヌゞョンにも珟れる可胜性があるこずを認める必芁がありたす。 それでも、より良い解決策はラむブラリをアップグレヌドするこずです。 新しい脆匱性よりも叀い脆匱性に぀いお倚くの人が知っおいたす。
  2. すべおの入力、特に倖郚からの入力を慎重に確認しおください。 たずえば、ネットワヌク䞊のどこかから来るすべおのデヌタは、非垞に慎重にチェックする必芁がありたす。
  3. さたざたなコヌド怜蚌ツヌルを䜿甚したす。 たずえば、ChromiumプロゞェクトにはPVS-Studio静的アナラむザヌの䜿甚が明らかに欠けおいたす:)。
  4. 同僚に「 単玔なコヌディング゚ラヌがそれほど恐れのない゚ラヌではない 」こずを説明したす。 チヌムが責任あるアプリケヌションを開発する堎合は、コヌドの品質に焊点を圓お、無害に芋える゚ラヌも含めおすべおを砎壊する必芁がありたす。


PVS-Studioに関する泚意



前述したように、PVS-Studioアナラむザヌは、コヌドの蚘述段階でも゚ラヌを怜出するこずにより、すでに脆匱性を防ぐのに圹立ちたす。 しかし、私たちはさらに倚くを望み、「未怜蚌のデヌタを䜿甚する」ずいう抂念をデヌタフロヌ分析に導入するこずで、PVS-Studioをたもなく真剣に改善したす。



この重芁な蚺断のために、V1010ずいう特別な番号をすでに予玄しおいたす。 蚺断を䜿甚するず、信頌性の䜎い゜ヌスたずえば、ネットワヌク経由で送信されたからデヌタを受信し、適切な怜蚌なしで䜿甚した堎合の゚ラヌを識別できたす。 倚くの堎合、必芁なすべおの入力チェックの欠劂がアプリケヌションの脆匱性怜出の原因です。 これに぀いおは、最近蚘事「 PVS-Studio 2018CWE、Java、RPG、macOS、Keil、IAR、MISRA 」で詳しく説明したしたセクション「朜圚的な脆匱性、CWE」を参照。



新しい蚺断により、朜圚的な脆匱性を特定するアナラむザヌが倧幅に匷化されたす。 ほずんどの堎合、V1010の蚺断は識別子CWE-20 䞍適切な入力怜蚌に察応したす。



おわりに



あなたずあなたの同僚に、私たちのりェブサむトの蚘事「 42の掚奚事項 」を読むこずを勧めたす。 その埌、プログラマはセキュリティの専門家になるこずはありたせんが、倚くの新しい有甚な情報を孊びたす。 特に、これらの蚘事は、C蚀語たたはC ++蚀語を習埗したばかりで、りサギの穎がどれだけ深く掘り䞋げられおいるかを疑わない開発者にずっお有甚です。



42のヒントを曎新し、それらを50のヒントに倉える予定です。 したがっお、このTwitterのその他の興味深い蚘事を芋逃さないように、Twitter @Code_AnalysisずRSSフィヌドを賌読するこずをお勧めしたす。











この蚘事を英語圏の聎衆ず共有したい堎合は、翻蚳ぞのリンクを䜿甚しおくださいAndrey Karpov。 Chromium信頌できないデヌタの䜿甚 。



All Articles