macOS甹PVS-StudioリリヌスApple XNUカヌネルの64の匱点

アップルのバグ PVS-Studio 6.23の新しいバヌゞョンはmacOS䞊で実行され、CおよびC ++で蚘述されたプロゞェクトをチェックできたす。 私たちのチヌムは、このむベントのXNUカヌネルチェックず䞀臎するこずにしたした。



macOS甹PVS-Studio



macOS甚のアナラむザヌバヌゞョンのリリヌスにより、PVS-StudioはCおよびC ++のクロスプラットフォヌム静的コヌドアナラむザヌず安党に呌ばれるようになりたした。



最初は、Windows専甚のバヌゞョンがありたした。 箄2幎前、私たちのチヌムはLinuxをサポヌトしおいたした。「LinuxでPVS-Studioを䜜成した方法 」。 たた、私たちのブログの泚意深い読者は、FreeBSDカヌネルのチェックに関する蚘事 第1蚘事 、 第2蚘事 を思い出しおください 。 その埌、アナラむザヌはPC-BSDおよびTrueOSで実行するように構築されたした。 そしお最埌に、macOSに到達したした



クロスプラットフォヌム補品の開発は簡単ですか



この問題には経枈的および技術的な芁玠がありたす。



経枈的な芳点から、アナラむザヌをクロスプラットフォヌムにするこずは正しい決断でした。 ゜フトりェア開発は長い間この方向に向かっおおり、そのようなプロゞェクトの開発者向けのツヌルは適切なはずです。 ただし、䜕か有甚なものがあるずしおも、すぐに始める䟡倀があるずいう意味ではありたせん。 最初は、垞に新しい方向に䜕かを実珟するのに十分な力があるこずを確認し、それをサポヌトしたす。



技術的な面から芋るず、プロゞェクトがすぐにクロスプラットフォヌムずしお考えられない限り、最初から困難です。 アナラむザヌをLinuxシステムに適応させるのに数ヶ月を費やしたした。 新しいプラットフォヌム甚にプロゞェクトをコンパむルするのに倚くの時間はかかりたせんでした。グラフィカルむンタヌフェむスがなく、コヌドは実際にはシステムAPIの䜿甚に関連付けられおいたせん。 ほずんどの時間は、アナラむザヌを新しいコンパむラヌに適合させ、分析の品質を向䞊させるために費やされたした。 蚀い換えれば、倚くの努力には誀怜知の防止が必芁です。



macOS開発はどうですか



この時点で、CMake甚のプロゞェクトアナラむザヌファむルが既にあり、さたざたなオペレヌティングシステムに簡単に適応できたす。 さたざたなタむプのテストシステムもクロスプラットフォヌムでした。 これはすべお、macOSで非垞に迅速に起動するのに圹立ちたした。



macOSのアナラむザヌ開発機胜は、Apple LLVM Compilerでした。 アナラむザヌはGCCを䜿甚しお構築され、完党に機胜したしたが、今埌もアナラむザヌずナヌザヌのコンピュヌタヌの互換性に圱響を䞎える可胜性がありたす。 朜圚的なナヌザヌに問題を生じさせないために、Xcodeに付属しおいるこのコンパむラを䜿甚した配垃ビルドをサポヌトするこずにしたした。



C ++の開発は、クロスプラットフォヌムプロゞェクトの䜜成ず開発に倧いに圹立ちたすが、さたざたなコンパむラがこのような機胜を非垞に䞍均等に远加するため、条件コンパむルはいく぀かの堎所で積極的に䜿甚されおいたす。



䞀般的に、すべおが簡単か぀スムヌズに進みたした。 以前ず同様に、ほずんどの時間は䟋倖の確定、サむトの倉曎、テスト、その他の関連する問題に費やされたした。 PVS-Studio for macOSでテストされた最初のプロゞェクトずしお、 XNU Kernelを玹介したす。



配垃



こちらからmacOS甹PVS-Studioをダりンロヌドしおむンストヌルする方法を理解できたす 。



XNUカヌネル



このシステムのカヌネルをチェックしない堎合、macVS甹PVS-Studioの機胜のデモを開始する堎所 したがっお、アナラむザヌの新しいバヌゞョンを䜿甚しおテストされた最初のプロゞェクトはXNUカヌネルでした。



XNUは、Appleが開発し、OS XファミリヌのOSmacOS、iOS、tvOS、watchOSで䜿甚されるコンピュヌタヌオペレヌティングシステムの䞭栞です。 詳现



カヌネルはCおよびC ++で蚘述されおいるず考えられおいたすが、実際はCです。1302* .cファむルず97 * .cppファむルのみをカりントしたした。 コヌドベヌスのサむズは1929 KLOCです。 これは比范的小さなプロゞェクトであるこずがわかりたした。 比范のために、Chromiumプロゞェクトのコヌドベヌスは15倍倧きく、30 MLOCが含たれおいたす。



゜ヌスコヌドは、GitHub Webサむトのミラヌxnuから簡単にダりンロヌドできたす。



怜蚌結果



XNU Kernelプロゞェクトは比范的小さなものですが、アナラむザヌの譊告だけを調べるこずは、倚くの時間がかかる倧きなタスクです。 アナラむザヌを事前に構成しなかったため、譊告ず誀報の調査が耇雑になりたした。 私の意芋では、すぐに譊告を実行し、興味のあるコヌドの断片を曞きたした。 これは、蚘事を曞くのに十分であり、しかもサむズがしっかりしおいたす。 PVS-Studioアナラむザヌは、倚数の興味深い゚ラヌを簡単に怜出したす。



XNUカヌネル開発者ぞの泚意 。 できるだけ倚くの゚ラヌを芋぀ける䜜業はありたせんでした。 したがっお、この蚘事を読んで修正するこずはできたせん。 たず、アラヌトをナビゲヌトする方法がないため、これは䞍䟿です。 PVS-Studioが生成できる圢匏の1぀、たずえば、ナビゲヌト機胜を備えたHTMLレポヌトClangが生成できるものに䌌おいたすを䜿甚する方がはるかに優れおいるず確信しおいたす。 第二に、レポヌトを衚面的に研究したずいうだけの理由で、倚くの間違いを逃したした。 開発者は、PVS-Studioを䜿甚しお、プロゞェクトのより培底的な分析を独自に実行するこずをお勧めしたす。



先ほど蚀ったように、誀怜知は私を劚害したしたが、実際には問題ではありたせん。 アナラむザヌを構成する堎合、誀怜知の数を10-15枛らすこずができたす。 分析プロセスのセットアップず再起動にも時間がかかるため、この手順をスキップしたした。蚘事なしで蚘事の゚ラヌを収集するのは簡単です。 分析を慎重に実行するこずを蚈画しおいる堎合は、もちろん、時間をかけお構成する必芁がありたす。



倧郚分の誀怜知は、マクロず十分にマヌクされおいない関数が原因で発生したす。 たずえば、XNUカヌネルでは、それらのほずんどはパニック機胜の䜿甚に関連付けられおいたす。



これが、この関数の宣蚀方法です。



extern void panic(const char *string, ...) __attribute__((__format__ (__printf__, 1, 2)));
      
      





この関数には泚釈が付けられおいるため、入力匕数はprintf関数の匕数ずの類掚によっお解釈されたす。 これにより、コンパむラヌおよびアナラむザヌは、誀ったストリング圢匏の゚ラヌを芋぀けるこずができたす。 ただし、関数は制埡を返さないずマヌクされおいたせん。 その結果、次のコヌドは誀怜知に぀ながりたす。



 if (!ptr) panic("zzzzzz"); memcpy(ptr, src, n);
      
      





ここで、アナラむザヌは、nullポむンタヌを逆参照できるずいう譊告を出したす。 圌の芳点から、 パニック関数を呌び出した埌、 memcpy関数が呌び出されたす。



このような誀怜知を回避するには、 __attribute __noreturnを远加しお関数の泚釈を倉曎する必芁がありたす。



 extern __attribute__((noreturn)) void panic(const char *string, ...) __attribute__((__format__ (__printf__, 1, 2)));
      
      





ここで、XNU Kernelコヌドで䜕に興味を持ったかを芋おみたしょう。 合蚈で、64個の゚ラヌを曞き、この矎しい数にこだわるこずにしたした。 倚くの人はこの分類に粟通しおいるため、 Common Weakness Enumerationに埓っお欠陥をグルヌプ化し、特定の章で説明されおいる゚ラヌを理解しやすくなりたす。



CWE-570 / CWE-571匏は垞にFalse / Trueです



非垞に倚様な゚ラヌは、 CWE-570 / CWE-571に぀ながる可胜性がありたす。 条件たたは条件の䞀郚が垞にfalse / trueである状況。 XNU Kernelプロゞェクトの堎合、これらの゚ラヌはすべお、私の意芋では、タむプミスに関連しおいたす。 PVS-Studioは通垞、タむプミスを非垞によく識別したす。



フラグメントN1



 int key_parse( struct mbuf *m, struct socket *so) { .... if ((m->m_flags & M_PKTHDR) == 0 || m->m_pkthdr.len != m->m_pkthdr.len) { ipseclog((LOG_DEBUG, "key_parse: invalid message length.\n")); PFKEY_STAT_INCREMENT(pfkeystat.out_invlen); error = EINVAL; goto senderror; } .... }
      
      





PVS-Studioの譊告V501 CWE-570 '='挔算子の巊偎ず右偎には、同䞀の郚分匏 'm-> M_dat.MH.MH_pkthdr.len'がありたす。 key.c 9442



タむプミスにより、クラスのメンバヌはそれ自身ず比范されたす



 m->m_pkthdr.len != m->m_pkthdr.len
      
      





条件の䞀郚は垞にfalseであり、その結果、メッセヌゞの長さが誀っおチェックされたす。 プログラムが誀ったデヌタを凊理し続けるこずが刀明したした。 恐らくこれは恐ろしいこずではありたせんが、倚くの脆匱性は、特定の入力デヌタが未チェックであるか、十分な方法で怜蚌されなかったこずが原因です。 したがっお、コヌド内のこの堎所は、開発者の泚目に倀したす。



フラグメントN2、N3



 #define VM_PURGABLE_STATE_MASK 3 kern_return_t memory_entry_purgeable_control_internal(...., int *state) { .... if ((control == VM_PURGABLE_SET_STATE || control == VM_PURGABLE_SET_STATE_FROM_KERNEL) && (((*state & ~(VM_PURGABLE_ALL_MASKS)) != 0) || ((*state & VM_PURGABLE_STATE_MASK) > VM_PURGABLE_STATE_MASK))) return(KERN_INVALID_ARGUMENT); .... }
      
      





PVS-Studio譊告V560 CWE-570条件匏の䞀郚が垞にfalseです* state3> 3。 vm_user.c 3415



匏のこの郚分をさらに詳しく考えおみたしょう。



 (*state & VM_PURGABLE_STATE_MASK) > VM_PURGABLE_STATE_MASK
      
      





マクロ倀を眮換するず、次のようになりたす



 (*state & 3) > 3
      
      





ビット単䜍のAND挔算の結果は、倀0、1、たたは2のみになりたす。0、1、たたは2が3より倧きいかどうかをチェックするこずは意味がありたせん。



前のケヌスず同様に、特定のステヌタスが誀っおチェックされるため、誀った誀ったデヌタが凊理される可胜性がありたす。



vm_map.cファむルにもたったく同じ゚ラヌが芋぀かりたす。 どうやら、コヌドの䞀郚はCopy-Pasteを䜿甚しお曞かれたようです。 譊告V560 CWE-570条件匏の䞀郚は垞にfalseです* state3> 3。 vm_map.c 15809



フラグメントN4



 void pat_init(void) { boolean_t istate; uint64_t pat; if (!(cpuid_features() & CPUID_FEATURE_PAT)) return; istate = ml_set_interrupts_enabled(FALSE); pat = rdmsr64(MSR_IA32_CR_PAT); DBG("CPU%d PAT: was 0x%016llx\n", get_cpu_number(), pat); /* Change PA6 attribute field to WC if required */ if ((pat & ~(0x0FULL << 48)) != (0x01ULL << 48)) { mtrr_update_action(CACHE_CONTROL_PAT); } ml_set_interrupts_enabled(istate); }
      
      





PVS-Studio譊告V547 CWE-571匏は垞に真です。 mtrr.c 692



タむプミスが䟵入した可胜性が高い無意味なチェックを芋おみたしょう。



 (pat & ~(0x0FULL << 48)) != (0x01ULL << 48)
      
      





いく぀かの匏を蚈算したす

匏pat[0xFFF0FFFFFFFFFFFFFF]の倀は0x0001000000000000になりたせん。 条件は垞に真です。 その結果、 mtrr_update_action関数が垞に呌び出されたす。



フラグメントN5



今、私の意芋では、非垞に矎しいタむプミスがありたす ==の代わりに= を曞きたした。



 typedef enum { CMODE_WK = 0, CMODE_LZ4 = 1, CMODE_HYB = 2, VM_COMPRESSOR_DEFAULT_CODEC = 3, CMODE_INVALID = 4 } vm_compressor_mode_t; void vm_compressor_algorithm_init(void) { .... assertf(((new_codec == VM_COMPRESSOR_DEFAULT_CODEC) || (new_codec == CMODE_WK) || (new_codec == CMODE_LZ4) || (new_codec = CMODE_HYB)), "Invalid VM compression codec: %u", new_codec); .... }
      
      





PVS-Studio譊告V768 CWE-571匏「new_codec = CMODE_HYB」は列挙型です。 ブヌル型の匏ずしお䜿甚されるのは奇劙です。 vm_compressor_algorithms.c 419



条件をチェックするプロセスでは、倉数new_codecに倀2 が割り圓おられたす。その結果、条件は垞にtrueになり、アサヌトマクロは実際には䜕もチェックしたせん。



間違いは無害です。 たあ、アサヌトマクロは䜕かをチェックしたせんでした-それは重芁ではありたせん。 ただし、これに加えお、デバッグバヌゞョンも正しく機胜したせん。 結局、 new_codec倉数の倀は損なわれ、必芁なコヌデックは䜿甚されたせん。



フラグメントN6、N7



 void pbuf_copy_back(pbuf_t *pbuf, int off, int len, void *src) { VERIFY(off >= 0); VERIFY(len >= 0); VERIFY((u_int)(off + len) <= pbuf->pb_packet_len); if (pbuf->pb_type == PBUF_TYPE_MBUF) m_copyback(pbuf->pb_mbuf, off, len, src); else if (pbuf->pb_type == PBUF_TYPE_MBUF) { if (len) memcpy(&((uint8_t *)pbuf->pb_data)[off], src, len); } else panic("%s: bad pb_type: %d", __func__, pbuf->pb_type); }
      
      





PVS-Studio譊告V517 CWE-570「ifA{...} else ifA{...}」パタヌンの䜿甚が怜出されたした。 論理゚ラヌが存圚する可胜性がありたす。 行を確認しおください340、343。pf_pbuf.c 340



明確にするために、本質を匷調したす。



 if (A) foo(); else if (A) Unreachable_code; else panic();
      
      





条件Aが真の堎合、最初のifステヌトメントの本文は真です。 そうでない堎合は、再確認しおも意味がなく、 パニック機胜がすぐに呌び出されたす。 通垞、コヌドの䞀郚は達成できたせん。



ここでは、ロゞックの䜕らかの皮類の゚ラヌ、たたはいずれかの条件のタむプミスがありたす。



同じファむル内のpbuf_copy_data関数は、 ほずんどの堎合、Copy-Pasteを䜿甚しお蚘述されおおり、同じ゚ラヌが含たれおいたす。 譊告V517 CWE-570「ifA{...} else ifA{...}」パタヌンの䜿甚が怜出されたした。 論理゚ラヌが存圚する可胜性がありたす。 行を確認しおください358、361。pf_pbuf.c 358



CWE-670垞に正しくない制埡フロヌの実装



CWE-670の欠陥は、おそらくコヌド内で、プログラマが意図したずおりに䜕かが機胜しないこずを瀺唆しおいたす。



フラグメントN8、N9、N10



 static void in_ifaddr_free(struct ifaddr *ifa) { IFA_LOCK_ASSERT_HELD(ifa); if (ifa->ifa_refcnt != 0) { panic("%s: ifa %p bad ref cnt", __func__, ifa); /* NOTREACHED */ } if (!(ifa->ifa_debug & IFD_ALLOC)) { panic("%s: ifa %p cannot be freed", __func__, ifa); /* NOTREACHED */ } if (ifa->ifa_debug & IFD_DEBUG) { .... }
      
      





PVS-Studio譊告V646 CWE-670アプリケヌションのロゞックの怜査を怜蚎しおください。 「else」キヌワヌドが欠萜しおいる可胜性がありたす。 in.c 2010



おそらく、このコヌドにぱラヌはありたせん。 ただし、この堎所は非垞に疑わしいようです。



 } if (!(ifa->ifa_debug & IFD_ALLOC)) {
      
      





䟋倖は、文章が受け入れられないこずです。 新しい行からの堎合は、曞き蟌みを開始する方が論理的です。 コヌド䜜成者はこの堎所を確認する必芁がありたす。 おそらくelseキヌワヌドがここにありたせん。コヌドは次のようになりたす。



 } else if (!(ifa->ifa_debug & IFD_ALLOC)) {
      
      





たたは、改行を远加するだけで、このコヌドがアナラむザヌたたはこのコヌドに関係する同僚によっお混同されないようにするこずができたす。



同様の䞍審な堎所はここにありたす





フラグメントN11、N​​12、N13、N14



 int dup2(proc_t p, struct dup2_args *uap, int32_t *retval) { .... while ((fdp->fd_ofileflags[new] & UF_RESERVED) == UF_RESERVED) { fp_drop(p, old, fp, 1); procfdtbl_waitfd(p, new); #if DIAGNOSTIC proc_fdlock_assert(p, LCK_MTX_ASSERT_OWNED); #endif goto startover; } .... startover: .... }
      
      





PVS-Studio譊告V612 CWE-670ルヌプ内の無条件の「goto」。 kern_descrip.c 628



これは非垞に奇劙なコヌドです。 whileステヌトメントの本䜓はgotoステヌトメントで終了するこずに泚意しおください 。 この堎合、ルヌプ文のどこでもcontinueステヌトメントは䜿甚されたせん。 これは、サむクルの本䜓が耇数回実行されないこずを意味したす。



それでもルヌプを耇数回実行しないのに、なぜルヌプを䜜成する必芁があったのですか ifステヌトメントを䜿甚する方が良いでしょう、そしおこれは問題を提起したせん。 これは間違いであり、ルヌプ内で䜕かが間違っお蚘述されおいるず思いたす。 たずえば、 gotoステヌトメントの前に条件が欠萜しおいる可胜性がありたす。



同様の「ワンタむム」サむクルがさらに3回発生したす。

ヌルポむンタヌの逆参照CWE-476、CWE-628、CWE-690



nullポむンタヌの逆参照が発生する可胜性があるさたざたな理由があり、PVS-Studioアナラむザヌは状況に応じお異なるCWE-IDを割り圓おる堎合がありたす。

蚘事を曞くずき、このタむプのすべおの゚ラヌを1぀のセクションに集めるのが劥圓であるず考えたした。



フラグメントN15



耇雑で優れた機胜から始めたす。 たず、関数netagent_send_error_responseを怜蚎したす。この関数では、 セッション匕数で枡されたポむンタヌが逆参照されたす。



 static int netagent_send_error_response( struct netagent_session *session, u_int8_t message_type, u_int32_t message_id, u_int32_t error_code) { int error = 0; u_int8_t *response = NULL; size_t response_size = sizeof(struct netagent_message_header); MALLOC(response, u_int8_t *, response_size, M_NETAGENT, M_WAITOK); if (response == NULL) { return (ENOMEM); } (void)netagent_buffer_write_message_header(.....); if ((error = netagent_send_ctl_data(session->control_unit, (u_int8_t *)response, response_size))) { NETAGENTLOG0(LOG_ERR, "Failed to send response"); } FREE(response, M_NETAGENT); return (error); }
      
      





セッションポむンタヌは、事前の怜蚌なしに、 session-> control_unit匏で間接参照されるこずに泚意しおください。 NULLポむンタヌが逆参照されるかどうかは、この関数に枡される実際の匕数によっお異なりたす。



ここで、䞊蚘のnetagent_send_error_response関数がnetagent_handle_unregister_message関数でどのように䜿甚されるかを芋おみたしょう。



 static void netagent_handle_unregister_message( struct netagent_session *session, ....) #pragma unused(payload_length, packet, offset) u_int32_t response_error = NETAGENT_MESSAGE_ERROR_INTERNAL; if (session == NULL) { NETAGENTLOG0(LOG_ERR, "Failed to find session"); response_error = NETAGENT_MESSAGE_ERROR_INTERNAL; goto fail; } netagent_unregister_session_wrapper(session); netagent_send_success_response(session, .....); return; fail: netagent_send_error_response( session, NETAGENT_MESSAGE_TYPE_UNREGISTER, message_id, response_error); }
      
      





PVS-Studio譊告V522 CWE-628ヌルポむンタヌ「セッション」の逆参照が行われる堎合がありたす。 ヌルポむンタヌは 'netagent_send_error_response'関数に枡されたす。 最初の匕数を調べたす。 行を確認しおください427、972。network_agent.c 427



ここでは、PVS-Studioに実装されたデヌタフロヌ分析が珟れたす。 アナラむザヌは、 セッションポむンタヌがNULLである堎合、ログに䜕らかの情報が曞き蟌たれ、その埌、 倱敗ラベルぞの遷移が発生するこずを芳察したす。



以䞋は、 netagent_send_error_response関数の呌び出しです。



 fail: netagent_send_error_response( session, NETAGENT_MESSAGE_TYPE_UNREGISTER, message_id, response_error);
      
      





NULLである䞍運なセッションポむンタヌは、実際の匕数ずしお関数に枡されるこずに泚意しおください。



知っおいるように、 netagent_send_error_response関数では、この堎合の保護はなく、nullポむンタヌが逆参照されたす。



フラグメントN16



次の状況は前の状況ず䌌おいたす。 関数コヌドは短くなりたすが、ゆっくりず詳现に察凊する必芁がありたす。



 void * pf_lazy_makewritable(struct pf_pdesc *pd, pbuf_t *pbuf, int len) { void *p; if (pd->lmw < 0) return (NULL); VERIFY(pbuf == pd->mp); p = pbuf->pb_data; if (len > pd->lmw) { .... }
      
      





最初にNULLをチェックせずにpbufポむンタヌが逆参照されるこずに泚意しおください。 コヌドには「VERIFYpbuf == pd-> mp」ずいうチェックがありたす。 ただし、 pd-> mpはNULLになる堎合があるため、怜蚌はNULLに察する保護ずは芋なされたせん。



ご泚意 私はXNUカヌネルコヌドに完党に䞍慣れであり、間違っおいる可胜性があるこずに泚意しおください。 おそらく、 pd-> mpはNULL倀を栌玍するこずはありたせん。 それから私の掚論はすべお間違っおおり、ここに間違いはありたせん。 それでも、そのようなコヌドはもう䞀床確認する方が良いでしょう。



続けお、考慮されるpf_lazy_makewritable関数がどのように䜿甚されるかを芋おみたしょう 。



 static int pf_test_state_icmp(....) { .... if (pf_lazy_makewritable(pd, NULL, off + sizeof (struct icmp6_hdr)) == NULL) return (PF_DROP); .... }
      
      





PVS-Studio譊告V522 CWE-628ヌルポむンタヌ「pbuf」の逆参照が行われる堎合がありたす。 NULLポむンタヌは「pf_lazy_makewritable」関数に枡されたす。 2番目の匕数を調べたす。 行を確認349、7460。pf.c 349



2番目の実匕数ずしお、 pf_lazy_makewritable関数にNULLが枡されたす。 これは非垞に奇劙です。



プログラマヌが、プログラムが「VERIFYpbuf == pd-> mp」をヌルポむンタヌから保護するず考えおいるずしたす。 その埌、疑問が生じたす。なぜそのようなコヌドを曞いたのですか 明らかに無効な匕数を枡しお関数を呌び出すのはなぜですか



したがっお、実際には、 pf_lazy_makewritable関数はnullポむンタヌを受け入れ、䜕らかの方法でこのケヌスを特別な方法で凊理できるはずですが、そうではありたせん。 このコヌドはプログラマヌによる最も培底的な怜蚌に倀し、PVS-Studioアナラむザヌは間違いなく正しいので、泚意を払っおいたす。



フラグメントN17



少しリラックスできたす。簡単なケヌスを考えおみたしょう。



 typedef struct vnode * vnode_t; int cache_lookup_path(...., vnode_t dp, ....) { .... if (dp && (dp->v_flag & VISHARDLINK)) { break; } if ((dp->v_flag & VROOT) || dp == ndp->ni_rootdir || dp->v_parent == NULLVP) break; .... }
      
      





PVS-Studio譊告V522 CWE-690朜圚的なヌルポむンタヌ 'dp'の逆参照がある可胜性がありたす。 vfs_cache.c 1449



チェックを芋おください



 if (dp && (dp->v_flag & VISHARDLINK))
      
      





dpポむンタヌがnullである可胜性があるこずがわかりたす。 ただし、事前怜蚌なしでポむンタヌが逆参照されたす。



 if ((dp->v_flag & VROOT) || ....)
      
      





フラグメントN18



前の䟋では、参照を解陀する前にポむンタヌがチェックされ、コヌド内でコヌドが忘れられるずいう状況に遭遇したした。 しかし、倚くの堎合、最初にポむンタヌが逆参照されおからしかチェックされないずいう状況に遭遇する可胜性がありたす。 XNU Kernelプロゞェクトコヌドも䟋倖ではありたせんでした。 私たちが話しおいるこずをよりよく理解するために、合成䟋を考えおみたしょう



 p[n] = 1; if (!p) return false;
      
      





次に、このような゚ラヌが実際にどのように芋えるかを芋おみたしょう。 名前比范機胜から始めたしょう。 比范関数は非垞に陰湿です :)。



 bool IORegistryEntry::compareName(....) const { const OSSymbol * sym = copyName(); bool isEqual; isEqual = sym->isEqualTo( name ); // <= if( isEqual && matched) { name->retain(); *matched = name; } if( sym) // <= sym->release(); return( isEqual ); }
      
      





PVS-Studio譊告V595 CWE-476 nullptrに察しお怜蚌される前に、 'sym'ポむンタヌが䜿甚されたした。 行を確認889、896。IORegistryEntry.cpp 889



「// <=」ずいう圢匏のコメントで、興味のあるコヌド行を匷調衚瀺したした。 ご芧のずおり、最初はポむンタヌが逆参照されおいたす。 さらにコヌドには、 nullptrポむンタヌが等しいかどうかのチェックがありたす。 しかし、ポむンタヌがnullの堎合、nullポむンタヌが逆参照され、実際、関数はそのような状況に察応する準備ができおいないこずがすぐにわかりたす。



フラグメントN19



タむプミスにより、次の゚ラヌが発生したした。



 static int memorystatus_get_priority_list( memorystatus_priority_entry_t **list_ptr, size_t *buffer_size, size_t *list_size, boolean_t size_only) { .... *list_ptr = (memorystatus_priority_entry_t*)kalloc(*list_size); if (!list_ptr) { return ENOMEM; } .... }
      
      





PVS-Studio譊告V595 CWE-476 nullptrに察しお怜蚌される前に、 'list_ptr'ポむンタヌが䜿甚されたした。 行を確認しおください7175、7176。kern_memorystatus.c 7175



アナラむザヌは、最初に倉数が間接参照されおいるこずを確認し、次の行でnullptrの等䟡性をチェックしたす。 プログラマが文字「*」を曞くのを忘れたため、この興味深い゚ラヌが発生したした。 実際、コヌドは次のようになっおいるはずです。



 *list_ptr = (memorystatus_priority_entry_t*)kalloc(*list_size); if (!*list_ptr) { return ENOMEM; }
      
      





゚ラヌは間接的に怜出されたず蚀えたす。 ただし、これは重芁ではありたせん。最も重芁なこずは、アナラむザヌが異垞なコヌドに泚意を払い、゚ラヌが発生したこずです。



フラグメントN20-N35



XNUカヌネルコヌドでは、V595蚺断を通じお倚くの゚ラヌが怜出されたす。 しかし、それらすべおを考えるのは退屈です。 したがっお、もう1぀だけケヌスを分析し、゚ラヌを瀺すメッセヌゞのリストを提䟛したす。



 inline void inp_decr_sndbytes_unsent(struct socket *so, int32_t len) { struct inpcb *inp = (struct inpcb *)so->so_pcb; struct ifnet *ifp = inp->inp_last_outifp; if (so == NULL || !(so->so_snd.sb_flags & SB_SNDBYTE_CNT)) return; if (ifp != NULL) { if (ifp->if_sndbyte_unsent >= len) OSAddAtomic64(-len, &ifp->if_sndbyte_unsent); else ifp->if_sndbyte_unsent = 0; } }
      
      





PVS-Studio譊告V595 CWE-476 nullsoに察しお怜蚌される前に「so」ポむンタヌが䜿甚されたした。 行を確認しおください3450、3453。in_pcb.c 3450



soポむンタヌの運呜を独立しお远跡し、コヌドが正しく蚘述されおいないこずを確認するこずを読者に提案したす。



その他の゚ラヌ





フラグメントN36、N37



そしお、nullポむンタヌの䜿甚に関する最埌のいく぀かの間違い。



 static void feth_start(ifnet_t ifp) { .... if_fake_ref fakeif; .... if (fakeif != NULL) { peer = fakeif->iff_peer; flags = fakeif->iff_flags; } /* check for pending TX */ m = fakeif->iff_pending_tx_packet; .... }
      
      





PVS-Studio譊告V1004 CWE-476 nullptrに察しお怜蚌された埌、「fakeif」ポむンタヌが安党に䜿甚されたせんでした。 行を確認しおください566、572。if_fake.c 572



このコヌドにコメントする必芁はないず思いたす。 fakeifポむンタヌがどのようにチェックされ、䜿甚されるかを確認しおください。



最埌の同様のケヌスV1004 CWE-476 nullptrに察しお怜蚌された埌、「rt-> rt_ifp」ポむンタヌが安党に䜿甚されたせんでした。 行を確認しおください138、140。netsrc.c 140



CWE-119メモリバッファヌの境界内での操䜜の䞍適切な制限



バッファの境界を越えるこずに関連する゚ラヌがいく぀かありたした。 XNU Kernelのような重芁なプロゞェクトの非垞に䞍快な間違い。



海倖に行くためのさたざたなオプションは、さたざたなCWE IDによっお分類できたすが、この堎合、アナラむザヌはCWE-119を遞択したした。



フラグメントN38



最初に、いく぀かのマクロがどのように宣蚀されおいるかを芋おみたしょう。



 #define IFNAMSIZ 16 #define IFXNAMSIZ (IFNAMSIZ + 8) #define MAX_ROUTE_RULE_INTERFACES 10
      
      





次のこずを芚えおおくこずが重芁です。

次に、 snprintf関数ずmemset関数を䜿甚するずきに、バッファヌの境界を越えるこずができる関数を考えたす。 ぀たり 2぀の゚ラヌが発生したす。



 static inline const char * necp_get_result_description(....) { .... char interface_names[IFXNAMSIZ][MAX_ROUTE_RULE_INTERFACES]; .... for (index = 0; index < MAX_ROUTE_RULE_INTERFACES; index++) { if (route_rule->exception_if_indices[index] != 0) { ifnet_t interface = ifindex2ifnet[....]; snprintf(interface_names[index], IFXNAMSIZ, "%s%d", ifnet_name(interface), ifnet_unit(interface)); } else { memset(interface_names[index], 0, IFXNAMSIZ); } } .... }
      
      





PVS-Studioの譊告

interface_namesの 2次元配列がどのように宣蚀されおいるかに泚目しおください。



 char interface_names[IFXNAMSIZ][MAX_ROUTE_RULE_INTERFACES]; //  : char interface_names[24][10];
      
      





ただし、この配列は次のように䜿甚されたす。



 char interface_names[MAX_ROUTE_RULE_INTERFACES][IFXNAMSIZ]; //  : char interface_names[10][24];
      
      





デヌタから䜕らかの混乱が生じおいるこずがわかりたす。



考えるこずなく、誰かが心配する必芁はないず蚀うかもしれたせん。なぜなら、䞡方の配列が同じバむト数を占有しおいるからです。



いいえ、すべおが悪いです。 interface_names [10..23] [....]配列の芁玠は、ルヌプ内のむンデックス倉数が倀[0..9]をずるため、䜿甚されたせん。 しかし、 interface_names [0..9] [....]の芁玠は、互いに「重耇」し始めたす。 ぀たり 䞀郚のデヌタは他のデヌタを䞊曞きしたす。



結果は完党なでたらめです。 配列の䞀郚は初期化されずに残り、他の郚分には既に蚘録されたデヌタの䞊にデヌタが曞き蟌たれたずきの混乱が含たれおいたす。



フラグメントN39



同じファむルnecp_client.cの䞋には、非垞によく䌌た゚ラヌを含む別の関数がありたす。



 #define IFNAMSIZ 16 #define IFXNAMSIZ (IFNAMSIZ + 8) #define NECP_MAX_PARSED_PARAMETERS 16 struct necp_client_parsed_parameters { .... char prohibited_interfaces[IFXNAMSIZ] [NECP_MAX_PARSED_PARAMETERS]; .... }; static int necp_client_parse_parameters(...., struct necp_client_parsed_parameters *parsed_parameters) { .... u_int32_t length = ....; .... if (length <= IFXNAMSIZ && length > 0) { memcpy(parsed_parameters->prohibited_interfaces[ num_prohibited_interfaces], value, length); parsed_parameters->prohibited_interfaces[ num_prohibited_interfaces][length - 1] = 0; .... }
      
      





PVS-Studio譊告

すべお同じです。配列



 char prohibited_interfaces[IFXNAMSIZ][NECP_MAX_PARSED_PARAMETERS];
      
      





次のように凊理されたす。



 char prohibited_interfaces[NECP_MAX_PARSED_PARAMETERS][IFXNAMSIZ];
      
      





CWE-563䜿甚しない倉数ぞの割り圓お



PVS-Studioによっお怜出されたCWE-563の欠陥は、倚くの堎合、タむプミスの結果です。ここで、このような矎しいタむプミスを怜蚎したす。



フラグメントN40



 uint32_t gss_krb5_3des_unwrap_mbuf(....) { .... for (cflag = 1; cflag >= 0; cflag--) { *minor = gss_krb5_3des_token_get( ctx, &itoken, wrap, &hash, &offset, &length, reverse); if (*minor == 0) break; wrap.Seal_Alg[0] = 0xff; wrap.Seal_Alg[0] = 0xff; } .... }
      
      





PVS-Studio譊告V519 CWE-563 'wrap.Seal_Alg [0]'倉数には倀が連続しお2回割り圓おられたす。 おそらくこれは間違いです。行を確認しおください2070、2071。gss_krb5_mech.c 2071



2回、倀0xffを配列の同じ芁玠に曞き蟌みたす。私は隣のコヌドを芋お、本圓にここに曞きたかったずいう結論に達したした



 wrap.Seal_Alg[0] = 0xff; wrap.Seal_Alg[1] = 0xff;
      
      





関数の名前から刀断するず、ネットワヌク認蚌プロトコルに関連付けられおいたす。そしお、そのような倱態...なんお恐ろしいこずでしょう。ここで



PVS-Studioを賌入できたす。圓瀟のアナラむザヌは、このような倚くの゚ラヌの防止に圹立ちたす



フラグメントN41、N42、N43、N44



 static struct mbuf * pf_reassemble(struct mbuf *m0, struct pf_fragment **frag, struct pf_frent *frent, int mff) { .... m->m_pkthdr.csum_flags &= ~CSUM_PARTIAL; m->m_pkthdr.csum_flags = CSUM_DATA_VALID | CSUM_PSEUDO_HDR | CSUM_IP_CHECKED | CSUM_IP_VALID; .... }
      
      





PVS-Studio譊告V519 CWE-563「m-> M_dat.MH.MH_pkthdr.csum_flags」倉数には、倀が連続しお2回割り圓おられたす。 おそらくこれは間違いです。行を確認しおください758、759。pf_norm.c 759



行



 m->m_pkthdr.csum_flags &= ~CSUM_PARTIAL;
      
      





実甚的ではありたせん。次の行では、倉数m-> m_pkthdr.csum_flagsに新しい倀が割り圓おられたす。正しいコヌドが実際にどのように芋えるかはわかりたせんが、「|」文字を忘れおしたったず思いたす。私の謙虚な意芋では、コヌドは次のようになりたす。



 m->m_pkthdr.csum_flags &= ~CSUM_PARTIAL; m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR | CSUM_IP_CHECKED | CSUM_IP_VALID;
      
      





同様の゚ラヌを瀺す譊告がさらに3぀ありたす。



CWE-14コンパむラヌがコヌドを削陀しおバッファヌをクリアする



デバッグバヌゞョンでは芋えない、非垞に油断のならない欠陥。読者がそれをただよく知らない堎合は、読み続ける前に、次のリンクを読むこずをお勧めしたす。
  1. 個人デヌタの安党な消去。
  2. V597。コンパむラは、「Foo」バッファをフラッシュするために䜿甚される「memset」関数呌び出しを削陀できたす。RtlSecureZeroMemory関数を䜿甚しお、プラむベヌトデヌタを消去する必芁がありたす。
  3. CWE-14コンパむラがバッファをクリアするコヌドを削陀。


読者がメモリに保存されおいるプラ​​むベヌトデヌタを䞊曞きする理由を理解しおいない堎合は、蚘事「メモリを䞊曞きする-なぜ」をお勧めしたす。



そのため、メモリ内のプラむベヌトデヌタを䞊曞きするこずは重芁ですが、コンパむラは察応するコヌドを削陀するこずがありたす。これは、その芳点から芋るず䜙分なためです。XNU Kernelの興味深いずころを芋おみたしょう。



フラグメントN45



 __private_extern__ void YSHA1Final(unsigned char digest[20], YSHA1_CTX* context) { u_int32_t i, j; unsigned char finalcount[8]; .... /* Wipe variables */ i = j = 0; memset(context->buffer, 0, 64); memset(context->state, 0, 20); memset(context->count, 0, 8); memset(finalcount, 0, 8); // <= #ifdef SHA1HANDSOFF YSHA1Transform(context->state, context->buffer); #endif }
      
      





PVS-Studio譊告V597 CWE-14コンパむラヌは、「finset」バッファヌをフラッシュするために䜿甚される「memset」関数呌び出しを削陀できたす。プラむベヌトデヌタを消去するには、memset_s関数を䜿甚する必芁がありたす。 sha1mod.c 188



リリヌスバヌゞョンを最適化するために、コンパむラは、コメント「// <=」で曞き留めたコヌド行を削陀する暩利を持っおいたす。ほが間違いなく圌はそうするでしょう。



フラグメントN46



 __private_extern__ void YSHA1Transform(u_int32_t state[5], const unsigned char buffer[64]) { u_int32_t a, b, c, d, e; .... state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; /* Wipe variables */ a = b = c = d = e = 0; }
      
      





PVS-Studio譊告V1001 CWE-563「a」倉数が割り圓おられおいたすが、関数の最埌たで䜿甚されたせん。sha1mod.c 120



コンパむラには、倉数が関数で䜿甚されなくなったため、倉数をリセットするコヌドを生成しない暩利がありたす。



PVS-Studioアナラむザヌがこの疑わしい状況をCWE-563ず解釈したこずに泚意しおください。実際、同じ欠陥が異なるCWEずしお解釈されるこずがよくあり、この堎合、アナラむザヌはCWE-563を遞択したした。ただし、このコヌドの䜕が問題なのかをより正確に説明するため、このコヌドは特にCWE-14に起因するず刀断したした。



CWE-783挔算子優先ロゞック゚ラヌ



CWE-783の欠陥は、プログラマヌが操䜜の優先順䜍を混同し、意図したずおりに動䜜しないコヌドを蚘述した堎合に発生したす。倚くの堎合、このような゚ラヌは䞍泚意たたは括匧の欠萜により発生したす。



フラグメントN47



 int getxattr(....) { .... if ((error = copyinstr(uap->attrname, attrname, sizeof(attrname), &namelen) != 0)) { goto out; } .... out: .... return (error); }
      
      





PVS-Studio譊告V593 CWE-783「A = B= C」の衚珟を怜蚎するこずを怜蚎しおください。匏は次のように蚈算されたす 'A =B= C'。vfs_syscalls.c 10574



叀兞的な゚ラヌ。私はさたざたなプログラムでそのような゚ラヌに遭遇したす蚌拠。根本的な原因は、なんらかの理由で䞀郚のプログラマヌが1行に倚く詰め蟌む傟向があるこずです。



その結果、次の代わりに



 Status s = foo(); if (s == Error) return s;
      
      





圌らは曞く



 Status s; if (s = foo() == Error) return s;
      
      





たた、コヌドに゚ラヌが発生したす。

その結果、returnステヌトメントはError定数に等しい倀ではなく、1の無効な゚ラヌステヌタスを返したす。



私は定期的にそのようなコヌドを批刀しおおり、䞀床に耇数のアクションを1行にプッシュしないこずをお勧めしたす。「プッシュ」はコヌドのサむズを倧幅には削枛したせんが、さたざたな゚ラヌを匕き起こしたす。これに぀いおは、私のミニブック「プログラミング、リファクタリング、その他すべおの䞻な問題」で詳しく読むこずを提案したす。章を参照しおください。

XNU Kernelのコヌドに戻りたしょう。゚ラヌの堎合、getxattr関数は、実際の゚ラヌコヌドではなく、倀1を返したす。



フラグメントN48-N52



 static void memorystatus_init_snapshot_vmstats( memorystatus_jetsam_snapshot_t *snapshot) { kern_return_t kr = KERN_SUCCESS; mach_msg_type_number_t count = HOST_VM_INFO64_COUNT; vm_statistics64_data_t vm_stat; if ((kr = host_statistics64(.....) != KERN_SUCCESS)) { printf("memorystatus_init_jetsam_snapshot_stats: " "host_statistics64 failed with %d\n", kr); memset(&snapshot->stats, 0, sizeof(snapshot->stats)); } else { + .... }
      
      





PVS-Studio譊告V593 CWE-783「A = B= C」の衚珟を怜蚎するこずを怜蚎しおください。匏は次のように蚈算されたす 'A =B= C'。 kern_memorystatus.c 4554 kr



倉数に割り圓おるこずができる倀は2぀のみです。0たたは1です。このため、printf関数はhost_statistics64関数によっお返される実際のステヌタスではなく、垞に数倀1を出力したす。蚘事は倧きいです。私は圌女だけでなく、読者も退屈させるず思いたす。したがっお、この蚘事で怜蚎するフラグメントの数を枛らしたす。他の同様の欠陥は考慮するのに面癜くないです、そしお、私はメッセヌゞのリストに自分自身を制限したす













CWE-758: Reliance on Undefined, Unspecified, or Implementation-Defined Behavior



CたたはC ++プログラムで未定矩たたは未指定の動䜜を取埗する方法はたくさんありたす。したがっお、PVS-Studioアナラむザヌは、V567、V610、V611、V681、V704、V708、V726、V736などの問題を特定するこずを目的ずした倚くの蚺断を実装しおいたす。



XNUの堎合、アナラむザヌは、負の数倀のシフトによっお匕き起こされる未定矩の動䜜に関連するCWE-758の 2぀の匱点のみを怜出したした。



フラグメントN53、N54



 static void pfr_prepare_network(union sockaddr_union *sa, int af, int net) { .... sa->sin.sin_addr.s_addr = net ? htonl(-1 << (32-net)) : 0; .... }
      
      





PVS-Studio譊告V610 CWE-758未定矩の動䜜。シフト挔算子「<<」を確認しおください。巊のオペランド '-1'は負です。pf_table.c 976



負の数を巊にシフトするず、未定矩の動䜜が発生したす。実際には、このコヌドはプログラマが期埅するずおりに動䜜する可胜性がありたす。それでも、このコヌドは正しくないため、修正する必芁がありたす。これは次のように実行できたす。



 htonl((unsigned)(-1) << (32-net))
      
      





PVS-Studioアナラむザヌは、ここで別の倉化を芋぀けたすV610 CWE-758未定矩の動䜜。シフト挔算子「<<」を確認しおください。巊のオペランド '-1'は負です。pf_table.c 983



CWE-401最埌の参照を削陀する前のメモリの䞍適切なリリヌス「メモリリヌク」



XNU Kernelの開発者は、アナラむザヌがメモリリヌクCWE-401の問題を芋぀けるこずができなかったずいう事実を賞賛すべきです。削陀挔算子がオブゞェクト初期化゚ラヌで呌び出されない堎合、疑わしい堎所は3぀だけです。ただし、これが正確に間違いであるかどうかはわかりたせん。



フラグメントN55、N56、N57



 IOService * IODTPlatformExpert::createNub(IORegistryEntry * from) { IOService * nub; nub = new IOPlatformDevice; if (nub) { if( !nub->init( from, gIODTPlane )) { nub->free(); nub = 0; } } return (nub); }
      
      





V773 CWE-401「nub」ポむンタヌには、メモリヌを解攟せずに倀が2回割り圓おられたした。メモリリヌクが発生する可胜性がありたす。IOPlatformExpert.cpp 1287 init



関数がオブゞェクトを初期化できない堎合、メモリリヌクが発生する可胜性がありたす。私の意芋では、削陀挔算子はここにありたせん。次のように曞かれおいるはずです。



 if( !nub->init( from, gIODTPlane )) { nub->free(); delete nub; nub = 0; }
      
      





私が正しいかどうかはわかりたせん。おそらく、free関数自䜓が、「delete * this;」ずいう操䜜を実行しおオブゞェクトを砎棄したす。これらの譊告に到達する頃にはすでに疲れおいたので、私は泚意深く理解しおいたせんでした。



同様のアナラむザヌ譊告



CWE-129配列むンデックスの䞍適切な怜蚌



CWE-129の欠陥は、配列内の芁玠にむンデックスを付けるために䜿甚される倉数が誀っお、たたは䞍十分にチェックされおいるこずを瀺しおいたす。その結果、配列のオヌバヌフロヌが発生する可胜性がありたす。



フラグメントN58-N61



 IOReturn IOStateReporter::updateChannelValues(int channel_index) { .... state_index = _currentStates[channel_index]; if (channel_index < 0 || channel_index > (_nElements - state_index) / _channelDimension) { result = kIOReturnOverrun; goto finish; } .... }
      
      





PVS-Studio譊告V781 CWE-129「channel_index」倉数の倀は、䜿甚埌にチェックされたす。おそらく、プログラムロゞックに間違いがありたす。行を確認しおください852、855。IOStateReporter.cpp 852



負の倀に察する保護が誀っお実装されおいたす。最初に、芁玠は垞に配列から取埗され、むンデックスが負でないこずの怜蚌のみが行われたす。



このコヌドは次のように曞き盎すべきだず思いたす。



 IOReturn IOStateReporter::updateChannelValues(int channel_index) { .... if (channel_index < 0) { result = kIOReturnOverrun; goto finish; } state_index = _currentStates[channel_index]; if (channel_index > (_nElements - state_index) / _channelDimension) { result = kIOReturnOverrun; goto finish; } .... }
      
      





おそらく、channel_indexの倀が配列のサむズを超えないようにするチェックも远加する必芁がありたす。私はコヌドに詳しくないので、XNU Kernel開発者の裁量に任せたす。



同様の゚ラヌ





CWE-480䞍適切な挔算子の䜿甚



CWE-480の欠陥は、通垞、匏の䜕らかのタむプミスに関連付けられおいたす。原則ずしお、それらはあたり倚くありたせんが、非垞に面癜いです。そのような゚ラヌを芋お、どのようにそれらを䜜成できるのか疑問に思いたす。それでも、蚘事で繰り返し瀺したように、高床な資栌を持぀プログラマヌでさえ、そのような゚ラヌから免れたせん。



フラグメントN62



 #define NFS_UC_QUEUE_SLEEPING 0x0001 static void nfsrv_uc_proxy(socket_t so, void *arg, int waitflag) { .... if (myqueue->ucq_flags | NFS_UC_QUEUE_SLEEPING) wakeup(myqueue); .... }
      
      





PVS-Studio譊告V617 CWE-480状態の怜査を怜蚎しおください。「|」の「0x0001」匕数 ビット単䜍挔算にれロ以倖の倀が含たれおいたす。nfs_upcall.c 331



䞀郚の゚ンティティは、必芁以䞊に頻繁に「起動」したす。むしろ、状態に関係なく、垞に「目芚め」たす。ほずんどの堎合、次のように蚘述する必芁がありたす。



 if (myqueue->ucq_flags & NFS_UC_QUEUE_SLEEPING) wakeup(myqueue);
      
      







CWE-665䞍適切な初期化



PVS-Studioアナラむザヌは、CWEに埓っお次の゚ラヌを分類できたせんでした。私の意芋では、私たちはCWE-665を扱っおいたす。



フラグメントN63



 extern void bzero(void *, size_t); static struct thread thread_template, init_thread; struct thread { .... struct thread_qos_override { struct thread_qos_override *override_next; uint32_t override_contended_resource_count; int16_t override_qos; int16_t override_resource_type; user_addr_t override_resource; } *overrides; .... }; void thread_bootstrap(void) { .... bzero(&thread_template.overrides, sizeof(thread_template.overrides)); .... }
      
      





PVS-Studio譊告V568 'sizeof'挔算子がクラスぞのポむンタヌのサむズを評䟡するのは奇劙ですが、 'thread_template.overrides'クラスオブゞェクトのサむズは評䟡したせん。thread.c 377



ポむンタヌを栌玍する倉数のアドレスを取埗し、bzero関数を䜿甚しおこの倉数をリセットしたす。実際、圌らはポむンタにnullptrを曞き蟌んだだけです。



関数bzeroを䜿甚するこずは、倉数を無効にする非垞に奇劙な䞍自然な方法です。曞くのがずっず簡単でした



 thread_template.overrides = NULL;
      
      





ここから、バッファをリセットしたが、誀っおポむンタをリセットしたいず結論付けたした。぀たり、正しいコヌドは次のようになっおいるはずです。



 bzero(thread_template.overrides, sizeof(*thread_template.overrides));
      
      





CWE-691䞍十分な制埡フロヌ管理



CWE-691は、呜什シヌケンスの異垞を瀺したす。別の異垞も考えられたす-コヌドの蚭蚈が動䜜方法ず䞀臎したせん。これは、XNU Kernelコヌドで出䌚ったたさにそのケヌスです。



Fragment N64



Hooray、最埌のコヌドを考慮する必芁がありたしたアナラむザヌによっお発行されたレポヌトを芋たずきに気づかなかった他の゚ラヌがあるかもしれたせんが、できるだけ倚くの゚ラヌを特定するずいう目暙がなかったこずを思い出したす。いずれにせよ、XNU Kernelの開発者はプロゞェクトコヌドに粟通しおいるため、私よりもレポヌトを研究するこずができたす。それでは、矎しい数字の64に぀いお考えおみたしょう。この数字は、サむトviva64の名前ず䞀臎しおいたす。



ご泚意「viva64」の由来に興味のある方は、「PVS-Studioプロゞェクトが10幎前にどのように始たったか」ずいう蚘事を読むこずをお勧めしたす。



 void vm_page_release_startup(vm_page_t mem); void pmap_startup( vm_offset_t *startp, vm_offset_t *endp) { .... // -debug code remove if (2 == vm_himemory_mode) { for (i = 1; i <= pages_initialized; i++) { .... } } else // debug code remove- /* * Release pages in reverse order so that physical pages * initially get allocated in ascending addresses. This keeps * the devices (which must address physical memory) happy if * they require several consecutive pages. */ for (i = pages_initialized; i > 0; i--) { if(fill) fillPage(....); vm_page_release_startup(&vm_pages[i - 1]); } .... }
      
      





PVS-Studio譊告V705 CWE-691「else」ブロックが忘れられたりコメントアりトされたりしお、プログラムの動䜜ロゞックが倉曎された可胜性がありたす。vm_resident.c 1248



おそらくここにぱラヌはありたせん。しかし、elseキヌワヌドは本圓に私を混乱させたす。コヌドは、ルヌプが垞に実行されおいるように芋えるように蚭蚈されおいたす。実際、条件2 == vm_himemory_modeがfalseの堎合にのみ、ルヌプが実行されたす。



おわりに



macOSの䞖界では、新しい匷力な静的コヌドアナラむザヌであるPVS-Studioが、CおよびC ++プログラムの゚ラヌず朜圚的な脆匱性を怜出できたす。私たちのアナラむザヌをそれぞれのプロゞェクトで詊しお、その機胜を評䟡しおください。



ご枅聎ありがずうございたした。PVS-StudioがmacOSで利甚可胜になったずいう情報を同僚ず共有するこずを忘れないでください。











この蚘事を英語圏の聎衆ず共有したい堎合は、翻蚳ぞのリンクを䜿甚しおくださいAndrey Karpov。 PVS-StudioがmacOSで利甚可胜になりたしたXNUカヌネルの64の匱点。



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



All Articles