LinuxカヌネルでのLinuxバヌゞョンのPVS-Studioのテスト









PVS-StudioのパブリックLinuxバヌゞョンのリリヌス以来、Linuxカヌネルの再チェックに関する蚘事は時間の問題に過ぎたせん。 䞖界䞭の専門家によっお曞かれたプロゞェクトであり、さたざたな分野の倚数の人々が䜿甚し、さたざたなツヌルで定期的にチェックおよびテストされたす。このようなプロゞェクトのテストは、静的アナラむザヌにずっお深刻なテストになりたす。 そのような状況でPVS-Studioはどのような゚ラヌを怜出できたすか



チェックしたずおり



Linuxカヌネルは既にテスト枈みです。 それ以来、倚くの倉曎が行われたした-珟圚、OSのチェックは他のプロゞェクトのチェックず同じくらい簡単で䟿利です



pvs-studio-analyzer trace - make pvs-studio-analyzer analyze -o /path/to/report.log -j8
      
      





Linuxでアナラむザヌを適応およびテストするのに数か月しかかかりたせんでしたが、最近たではWindowsでしか利甚できたせんでした。 今回は、カヌネルのチェックが桁違いに簡単になったこずがわかりたした。



怜蚌のために、バヌゞョン4.9-rc4commit bc33b0ca11e3df467777a4fa7639ba488c9d4911が䜿甚されたした。



蚘事を曞くために、1-2レベルの汎甚蚺断のみが衚瀺されたした。 コヌドの高品質に泚目する䟡倀がありたす-実際の欠陥を瀺す譊告の密床は非垞に䜎いです。



この蚘事では、実際の゚ラヌ/タむプミスを瀺す可胜性が最も高い譊告を遞択したした。 アナラむザヌは、有甚な譊告に加えお、誀ったアラヌムを生成したり、蚭蚈が䞍十分なコヌドや「悪臭」コヌドを識別したすが、それでも本圓に誀っおいるわけではありたせん。



残念ながら、Linuxでの誀怜知の数は、私たちが望むよりも倚くなっおいたす。 これは、Linuxシステム甚に蚭蚈されたアナラむザヌバヌゞョンの若者によるものだず思いたす。 私たちは、誀怜知の数を最小限に抑える玠晎らしい仕事をしおきたした。 Linuxコヌドは、アナラむザヌを改善し、アナラむザヌに倚くの䟿利な倉曎を加えるのを助けおくれたした。



タむプミス



゚ラヌの最も䞀般的なカテゎリは、䞀般的なタむプミスずコピヌペヌストに関連しおいたす。 以前に私たちの蚘事を読んだこずがあるなら、すでにこれを芋たこずがあるず思いたす。 それらは、あらゆる蚀語のあらゆるオペレヌティングシステム䞊のあらゆるプロゞェクトに登堎したす。 このような゚ラヌは、静的アナラむザヌの可胜性を明らかにしたす。他のツヌルでそれらを芋぀けるこずははるかに困難です。 Linuxカヌネルでそれらがどのように䜿甚されおいるかを芋おみたしょう。



PVS-Studio譊告 V581互いに䞊んでいる「if」挔算子の条件匏は同䞀です。 チェック行2384、2390。debug.c 2390



 int dbg_check_nondata_nodes_order(....) { .... sa = container_of(cur, struct ubifs_scan_node, list); sb = container_of(cur->next, struct ubifs_scan_node, list); if (sa->type != UBIFS_INO_NODE && sa->type != UBIFS_DENT_NODE && sa->type != UBIFS_XENT_NODE) { ubifs_err(c, "bad node type %d", sa->type); ubifs_dump_node(c, sa->node); return -EINVAL; } if (sa->type != UBIFS_INO_NODE && sa->type != UBIFS_DENT_NODE && sa->type != UBIFS_XENT_NODE) { ubifs_err(c, "bad node type %d", sb->type); ubifs_dump_node(c, sb->node); return -EINVAL; } .... }
      
      





アナラむザヌは、2぀の同䞀の条件が連続しおあるこずを蚎えたす。明らかに、2番目にsaをsbに倉曎するのを忘れおいたした。 それでは、誰がクヌルなプロゞェクトにコピヌアンドペヌストしないず蚀うでしょうか



PVS-Studio譊告 V666関数 'strncmp'の3番目の匕数を調べるこずを怜蚎しおください。 倀が、最初の匕数で枡された文字列の長さず䞀臎しない可胜性がありたす。 spectrum.c 341



 static ssize_t write_file_spec_scan_ctl(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct ath10k *ar = file->private_data; char buf[32]; ssize_t len; int res; len = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, len)) return -EFAULT; buf[len] = '\0'; mutex_lock(&ar->conf_mutex); if (strncmp("trigger", buf, 7) == 0) { .... } else if (strncmp("background", buf, 9) == 0) { res = ath10k_spectral_scan_config(ar, SPECTRAL_BACKGROUND); } else if (strncmp("manual", buf, 6) == 0) { res = ath10k_spectral_scan_config(ar, SPECTRAL_MANUAL); } else if (strncmp("disable", buf, 7) == 0) { res = ath10k_spectral_scan_config(ar, SPECTRAL_DISABLED); } else { res = -EINVAL; } mutex_unlock(&ar->conf_mutex); if (res < 0) return res; return count; }
      
      





゚ラヌの叀兞的な圢匏2぀の匕数を関数に枡す必芁がありたす文字列ぞのポむンタヌずその長さ。 倚くの堎合、リテラルが匕数である堎合、カりントの長さは遅延し、数字のみを曞き蟌みたす。 ヒュヌマンファクタヌが䜜甚したすそれらはしばしば間違えられたす。



コヌドのstrncmpが䞀列に䞊んでいたす。 リテラルはそれぞれに枡されたす。 たた、 strncmp「背景」、buf、9で長さが正しく蚈算されおいたせん。「背景」ずいう単語は9文字ではなく10文字で構成されおいたす。



PVS-Studio譊告 V666関数 'memcpy'の3番目の匕数を調べるこずを怜蚎しおください。 倀が、2番目の匕数で枡された文字列の長さず䞀臎しない可胜性がありたす。 dpt_i2o.c 403



 static void adpt_inquiry(adpt_hba* pHba) { .... memset(pHba->detail, 0, sizeof(pHba->detail)); memcpy(&(pHba->detail), "Vendor: Adaptec ", 16); memcpy(&(pHba->detail[16]), " Model: ", 8); memcpy(&(pHba->detail[24]), (u8*) &buf[16], 16); memcpy(&(pHba->detail[40]), " FW: ", 4); // <= memcpy(&(pHba->detail[44]), (u8*) &buf[32], 4); pHba->detail[48] = '\0'; /* precautionary */ .... }
      
      





別の䟋。 文字列「FW」は5文字で、4文字ではありたせん。



そのような゚ラヌを取り陀く方法は Cでは、次のようなマクロを䜿甚できたす。



 #define str_len(S) (sizeof(S) / sizeof((S)[0]))
      
      





ただし、このようなマクロを䜿甚するこず自䜓が危険です。コンパむラヌ固有のチェックを远加しお、枡された匕数が実際に配列であるこずを確認するこずをお勧めしたす。



C ++リヌダヌの堎合、std :: string_viewをお勧めしたす。これは最終的にC ++ 17に登堎したした。 ポむンタヌず長さのペアを文字列関数に枡さないこずをお勧めしたす。 ただし、配列のサむズを手動で蚈算する必芁がある堎合たずえば、memcpy関数に枡すため、std :: sizearrayたたはその類䌌物を䜿甚できたすリテラルの堎合、サむズはコンパむル時に蚈算されたす。



コヌドの繰り返しを避け、コンパむル時の蚈算に蚀語ツヌルマクロたたはテンプレヌトを䜿甚するのを怠らないでください



PVS-Studio譊告 V653 2぀の郚分で構成される疑わしい文字列は、配列の初期化に䜿甚されたす。 コンマが欠萜しおいる可胜性がありたす。 このリテラルの怜査を怜蚎しおください "30min" "No timeout"。 lp8788-charger.c 657



 static ssize_t lp8788_show_eoc_time(struct device *dev, struct device_attribute *attr, char *buf) { struct lp8788_charger *pchg = dev_get_drvdata(dev); char *stime[] = { "400ms", "5min", "10min", "15min", "20min", "25min", "30min" "No timeout" }; .... }
      
      





ご存知のように、行に蚘述された2぀のリテラルは連結されおいたす。 これにより、マクロなどで䟿利に䜿甚できたす。 このようなリテラルの配列を䜜成するず、危険が生じたす。コンマをスキップしお、予期しない結果を埗るこずができたす。



この堎合、最埌の2぀のリテラルは「結合」し、「30minNoタむムアりト」を取埗したす。 これは二重の間違いです。 たず、テキストが正しくありたせん。次に、配列内の1぀の芁玠が欠萜しおいるため、配列が流出する可胜性がありたす。



別のフォヌマット方法を䜿甚するこずをお勧めしたす。そのような゚ラヌは顕著になりたす。



 char *stime[] = { "400ms" , "5min" , "10min" , "15min" , "20min" , "25min" , "30min" "No timeout" };
      
      





私の同僚のAndrei Karpovは、この衚圢匏コヌド蚭蚈の方法に぀いお詳しく曞いおいたす。 私は圌の小さな本からN13章を読むこずを提案したす。



PVS-Studio è­Šå‘Š  V764 「ahc_9005_subdevinfo_valid」関数に枡される匕数の誀った順序の可胜性「デバむス」および「ベンダヌ」。 aic7xxx_pci.c 695



 const struct ahc_pci_identity * ahc_find_pci_device(ahc_dev_softc_t pci) { .... if (ahc_get_pci_function(pci) > 0 && ahc_9005_subdevinfo_valid(device, vendor, // <= subdevice, subvendor) && SUBID_9005_MFUNCENB(subdevice) == 0) return (NULL); .... }
      
      





アナラむザヌが䜕を誓うかを理解するのが難しい堎合がありたす。 ちなみに、これはよく起こりたす分析者が圌に曞いたものを人が理解せず、「停陜性」のレポヌトを私たちに送りたしたが、実際にぱラヌがありたす。 ここでは、これは誀怜知であるように思われたした。関数はコヌド内で少し高く定矩されおおり、すべおのパラメヌタヌが蚭定されおいたす。 これは次のようなものです。



 static int ahc_9005_subdevinfo_valid(uint16_t device, uint16_t vendor, uint16_t subdevice, uint16_t subvendor) { .... }
      
      





問題は䜕ですか この関数の宣蚀はさらに高く、これらの匕数はそこで混同されおいるこずがわかりたす。 実際、プログラムのロゞックには䜕も問題はありたせんが、誰も混乱させたり混乱させたりしないように修正するこずをお勧めしたす。



 static int ahc_9005_subdevinfo_valid(uint16_t vendor, uint16_t device, uint16_t subvendor, uint16_t subdevice);
      
      













しかし面癜いのは、ここでの゚ラヌはすでにあったずいうこずです。パラメヌタヌが本圓に混同されおいお、広告を修正するのを忘れおいたした。 アナラむザヌもこの堎所を芋぀けたのは良いこずです。



è­Šå‘ŠPVS-Studio V549 'memcpy'関数の最初の匕数は2番目の匕数ず同じです。 wilc_wfi_cfgoperations.c 1345



 static int del_pmksa(struct wiphy *wiphy, struct net_device *netdev, struct cfg80211_pmksa *pmksa) { .... for (; i < (priv->pmkid_list.numpmkid - 1); i++) { memcpy(priv->pmkid_list.pmkidlist[i].bssid, priv->pmkid_list.pmkidlist[i + 1].bssid, ETH_ALEN); memcpy(priv->pmkid_list.pmkidlist[i].pmkid, priv->pmkid_list.pmkidlist[i].pmkid, PMKID_LEN); } .... }
      
      





最埌のmemcpyはポむンタヌに䞀臎したす。 おそらく、圌らは前の衚珟ずの類掚で曞きたいず思ったでしょう。



 memcpy(priv->pmkid_list.pmkidlist[i].pmkid, priv->pmkid_list.pmkidlist[i + 1].pmkid, PMKID_LEN);
      
      





未䜿甚の倉数



PVS-Studio譊告 V575 「strncasecmp」関数は「0」芁玠を凊理したす。 3番目の匕数を調べたす。 linux_wlan.c 1121



 static int mac_ioctl(struct net_device *ndev, struct ifreq *req, int cmd) { u8 *buff = NULL; s8 rssi; u32 size = 0, length = 0; struct wilc_vif *vif; s32 ret = 0; struct wilc *wilc; vif = netdev_priv(ndev); wilc = vif->wilc; if (!wilc->initialized) return 0; switch (cmd) { case SIOCSIWPRIV: { struct iwreq *wrq = (struct iwreq *)req; size = wrq->u.data.length; if (size && wrq->u.data.pointer) { buff = memdup_user(wrq->u.data.pointer, wrq->u.data.length); if (IS_ERR(buff)) return PTR_ERR(buff); if (strncasecmp(buff, "RSSI", length) == 0) { // <= .... } } } .... } done: kfree(buff); return ret; }
      
      





strncasecmp関数には、長さの匕数ずしお0が枡されたした。 コヌド内に可倉長が倉わる堎所はないため、その倀はれロのたたです。 おそらくsizeを䜿甚する必芁がありたす。



PVS-Studio譊告 V751パラメヌタヌ「LCDheight」は関数本䜓内では䜿甚されたせん。 init.c 339



 static unsigned short SiS_GetModeID(int VGAEngine, unsigned int VBFlags, int HDisplay, int VDisplay, int Depth, bool FSTN, int LCDwidth, int LCDheight) { unsigned short ModeIndex = 0; switch(HDisplay) { case 320: if(VDisplay == 200) ModeIndex = ModeIndex_320x200[Depth]; else if(VDisplay == 240) { if((VBFlags & CRT2_LCD) && (FSTN)) ModeIndex = ModeIndex_320x240_FSTN[Depth]; else ModeIndex = ModeIndex_320x240[Depth]; } break; case 400: if((!(VBFlags & CRT1_LCDA)) || ((LCDwidth >= 800) && (LCDwidth >= 600))) { // <= if(VDisplay == 300) ModeIndex = ModeIndex_400x300[Depth]; } break; case 512: if((!(VBFlags & CRT1_LCDA)) || ((LCDwidth >= 1024) && (LCDwidth >= 768))) { // <= if(VDisplay == 384) ModeIndex = ModeIndex_512x384[Depth]; } break; .... } return ModeIndex; }
      
      





関数で垞に未䜿甚であるずは限らないパラメヌタヌぱラヌです。 かなり叀いAPIでは、パラメヌタヌが䞍芁になり、䞊曞きされるか、単に䜿甚されない状況が発生したす。 しかし、この断片を詳しく芋おみたしょう。ここでは、高さを比范するのを忘れおいたした。 代わりに、 「A> 5&&A> 3」ずいう圢匏の比范が登堎したしたが 、それ自䜓は冗長です。



操䜜の優先順䜍に関する混乱



è­Šå‘ŠPVS-Studio V502 「」挔算子は、予想ずは異なる方法で動䜜する可胜性がありたす。 「」挔算子の優先順䜍は「|」よりも䜎い 挔算子。 core.c 1046



 static int nvme_pr_preempt(struct block_device *bdev, u64 old, u64 new, enum pr_type type, bool abort) { u32 cdw10 = nvme_pr_type(type) << 8 | abort ? 2 : 1; return nvme_pr_command(bdev, cdw10, old, new, nvme_cmd_resv_acquire); }
      
      





Cの䞉項挔算子は非垞に危険な挔算子です。 PVS-Studioの最初の汎甚蚺断の1぀が圌に捧げられおいるだけではありたせん。 事実、圌の優先床は䜎く、耇雑な匏では混乱しやすく、蚈算の順序がたったく異なりたす。 したがっお、疑問がある堎合は括匧を䜿甚するこずをお勧めしたす。



疑わしいチェック



PVS-Studio è­Šå‘Š  V517 「ifA{...} else ifA{...}」パタヌンの䜿甚が怜出されたした。 論理゚ラヌが存圚する可胜性がありたす。 行を確認しおください375、377。trx.c 375



 bool rtl92ee_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *status, struct ieee80211_rx_status *rx_status, u8 *pdesc, struct sk_buff *skb) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rx_fwinfo *p_drvinfo; struct ieee80211_hdr *hdr; u32 phystatus = GET_RX_DESC_PHYST(pdesc); .... status->macid = GET_RX_DESC_MACID(pdesc); if (GET_RX_STATUS_DESC_MAGIC_MATCH(pdesc)) status->wake_match = BIT(2); else if (GET_RX_STATUS_DESC_MAGIC_MATCH(pdesc)) status->wake_match = BIT(1); else if (GET_RX_STATUS_DESC_UNICAST_MATCH(pdesc)) status->wake_match = BIT(0); else status->wake_match = 0; .... }
      
      





偎から芋るず、ここで䜕が間違っおいるのかを理解するこずは困難です。 マクロGET_RX_STATUS_DESC_MAGIC_MATCHを䜿甚しお、同じチェックが2回行われたす。 圌の広告を芋るず、他の2぀のマクロが衚瀺されたす。



 #define GET_RX_STATUS_DESC_PATTERN_MATCH(__pdesc) LE_BITS_TO_4BYTE(__pdesc+12, 29, 1) #define GET_RX_STATUS_DESC_UNICAST_MATCH(__pdesc) LE_BITS_TO_4BYTE(__pdesc+12, 30, 1) #define GET_RX_STATUS_DESC_MAGIC_MATCH(__pdesc) LE_BITS_TO_4BYTE(__pdesc+12, 31, 1)
      
      





おそらく、元のフラグメントで欠萜しおいるGET_RX_STATUS_DESC_PATTERN_MATCHを䜿甚したかったのでしょう。 それ以倖の堎合、このチェックは単に無意味です。



PVS-Studio è­Šå‘Š  V547匏 'ptr [3]0x1E= 0x03'は垞にtrueです。 sd.c 4115



 int ext_sd_send_cmd_get_rsp(struct rtsx_chip *chip, u8 cmd_idx, u32 arg, u8 rsp_type, u8 *rsp, int rsp_len, bool special_check) { int retval; int timeout = 100; u16 reg_addr; u8 *ptr; .... if (cmd_idx == SELECT_CARD) { if (rsp_type == SD_RSP_TYPE_R2) { if ((ptr[3] & 0x1E) != 0x04) { rtsx_trace(chip); return STATUS_FAIL; } } else if (rsp_type == SD_RSP_TYPE_R0) { if ((ptr[3] & 0x1E) != 0x03) { // <= rtsx_trace(chip); return STATUS_FAIL; } } } .... }
      
      





゚ラヌはビット操䜜に関連しおいたす。 1ビットに起因する0x1Eずのビットごずの結合の結果は、倀0x03ず決しお等しくなりたせん。











PVS-Studio è­Šå‘Š  V517 「ifA{...} else ifA{...}」パタヌンの䜿甚が怜出されたした。 論理゚ラヌが存圚する可胜性がありたす。 チェック行1277、1282。ks_wlan_net.c 1277



 static int ks_wlan_set_power(struct net_device *dev, struct iw_request_info *info, struct iw_param *vwrq, char *extra) { struct ks_wlan_private *priv = (struct ks_wlan_private *)netdev_priv(dev); short enabled; if (priv->sleep_mode == SLP_SLEEP) { return -EPERM; } /* for SLEEP MODE */ enabled = vwrq->disabled ? 0 : 1; if (enabled == 0) { /* 0 */ priv->reg.powermgt = POWMGT_ACTIVE_MODE; } else if (enabled) { /* 1 */ if (priv->reg.operation_mode == MODE_INFRASTRUCTURE) priv->reg.powermgt = POWMGT_SAVE1_MODE; else return -EINVAL; } else if (enabled) { /* 2 */ if (priv->reg.operation_mode == MODE_INFRASTRUCTURE) priv->reg.powermgt = POWMGT_SAVE2_MODE; else return -EINVAL; } else return -EINVAL; hostif_sme_enqueue(priv, SME_POW_MNGMT_REQUEST); return 0; }
      
      





䟋を次のように枛らしたす。



 enabled = vwrq->disabled ? 0 : 1; if (enabled == 0) { /* 0 */ .... } else if (enabled) { /* 1 */ .... } else if (enabled) { /* 2 */ .... } else ....
      
      





このコヌドは非垞に奇劙に芋えたす。 倀の範囲は䞊蚘の匏によっお明確にネゎシ゚ヌトされおいるようです enabledは0たたは1です。 ただし、最倧4぀の倀がチェックされたす。 同時に、コメントは干枉するだけです。数倀が倉数の可胜な倀を瀺す堎合は、実際には察応しおいたせん。1ず2のチェックは同じ方法で曞き蟌たれたす。



PVS-Studio è­Šå‘Š  V517 「ifA{...} else ifA{...}」パタヌンの䜿甚が怜出されたした。 論理゚ラヌが存圚する可胜性がありたす。 行を確認しおください422、424。Hal8188ERateAdaptive.c 422



 static int odm_ARFBRefresh_8188E( struct odm_dm_struct *dm_odm, struct odm_ra_info *pRaInfo) { /* Wilson 2011/10/26 */ .... if (pRaInfo->HighestRate > 0x13) pRaInfo->PTModeSS = 3; else if (pRaInfo->HighestRate > 0x0b) pRaInfo->PTModeSS = 2; else if (pRaInfo->HighestRate > 0x0b) pRaInfo->PTModeSS = 1; else pRaInfo->PTModeSS = 0; .... return 0; }
      
      





2぀の条件が連続する別の堎所。 ボディが異なるこずに泚意しおください。 ここに本圓の間違いがあるのか​​、それずも未䜿甚のコヌドなのかを蚀うのは困難です。これはすでにプロゞェクト開発者の仕事です。 アナラむザヌのタスクは、疑わしい堎所に泚意を払うこずです。



è­Šå‘ŠPVS-Studio V734過剰なチェック。 サブストリング「interleaver」および「deinterleaver」の怜玢を含む条件を調べたす。 sst-atom-controls.c 1449



 static int sst_fill_widget_module_info( struct snd_soc_dapm_widget *w, struct snd_soc_platform *platform) { struct snd_kcontrol *kctl; int index, ret = 0; struct snd_card *card = platform->component.card->snd_card; char *idx; down_read(&card->controls_rwsem); list_for_each_entry(kctl, &card->controls, list) { .... } else if (strstr(kctl->id.name, "interleaver")) { struct sst_enum *e = (void *)kctl->private_value; e->w = w; } else if (strstr(kctl->id.name, "deinterleaver")) { struct sst_enum *e = (void *)kctl->private_value; e->w = w; } .... } up_read(&card->controls_rwsem); return 0; }
      
      





このフラグメントでは、1行にいく぀かのサブストリングが存圚するかどうかが連続しおチェックされたす。 わかりやすくするために、興味のある郚分文字列のみを残したした。 むンタヌリヌバヌが芋぀からなかった堎合、 むンタヌリヌバヌのサブストリングが完党になくなっおいるため、 deinterleaverを探すこずは意味がありたせん。 したがっお、このコヌドセクションは機胜したせんが、ifずelseの本䜓が同じであるため、これは怖くありたせん。 これは単なる冗長コヌドです。



PVS-Studioの è­Šå‘Š  V547匏「ブロック」は垞に真です。 svclock.c 873



 void nlmsvc_grant_reply(struct nlm_cookie *cookie, __be32 status) { struct nlm_block *block; dprintk("grant_reply: looking for cookie %x, s=%d \n", *(unsigned int *)(cookie->data), status); if (!(block = nlmsvc_find_block(cookie))) return; if (block) { if (status == nlm_lck_denied_grace_period) { /* Try again in a couple of seconds */ nlmsvc_insert_block(block, 10 * HZ); } else { /* Lock is now held by client, or has been rejected. * In both cases, the block should be removed. */ nlmsvc_unlink_block(block); } } nlmsvc_release_block(block); }
      
      





この䟋は、 ASTをバむパスしお、静的アナラむザヌがパタヌンベヌスの分析を実行するのに十分でない理由を瀺しおいたす。 制埡フロヌ分析ずデヌタフロヌ分析を実行できるこずが重芁です。 block == NULLの時点で戻りが発生するため、コヌドをさらに䞋に移動するず、ポむンタヌがれロ以倖であるこずを確認できたす。 そしお、 NULLのチェックに遭遇するず、ここで䜕かが間違っおいるこずを明確に理解したす。



ここでは、2番目のポむンタヌチェックは䞍芁なようです。 ただし、ここで別の倉数を確認したい堎合はどうでしょうか 誰が知っおいるか...分析者は、怜蚌のためにこのコヌドを開発者に明確に提䟛すべきです。



同様の状況



PVS-Studio譊告 V547匏 'sym'は垞に真です。 menu.c 498



 bool menu_is_visible(struct menu *menu) { struct menu *child; struct symbol *sym; .... if (!sym || sym_get_tristate_value(menu->sym) == no) // <= return false; for (child = menu->list; child; child = child->next) { if (menu_is_visible(child)) { if (sym) // <= sym->flags |= SYMBOL_DEF_USER; return true; } } return false; }
      
      





マクロ゚ラヌ



è­Šå‘ŠPVS-Studio V733マクロを展開した結果、評䟡順序が間違っおいる可胜性がありたす。 匏を確認しおくださいrequest-> rq_timeout + 5 *1000。niobuf.c 637



 #define CFS_FAIL_TIMEOUT(id, secs) \ cfs_fail_timeout_set(id, 0, secs * 1000, CFS_FAIL_LOC_NOSET) #define OBD_FAIL_TIMEOUT(id, secs) \ CFS_FAIL_TIMEOUT(id, secs) int ptl_send_rpc(struct ptlrpc_request *request, int noreply) { .... OBD_FAIL_TIMEOUT(OBD_FAIL_PTLRPC_DELAY_SEND, request->rq_timeout + 5); .... }
      
      





しかし、そのような゚ラヌは非垞にたれです。 その前に、私は実際のプロゞェクトでこの蚺断の操䜜を1぀だけ芋たした。それがFreeBSDだったこずは泚目に倀したす 。 マクロの定矩に誀りがありたした。すべおのパラメヌタヌを角括匧で囲むのが最善です。 これが行われない堎合、次のケヌスが考えられたす。「sec + 1000」を「x + 5」に眮き換えるず、「x + 5 * 1000」が埗られたすが、これは明らかに著者が期埅したものではありたせん。



意味のないmemset



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



 int amdgpu_atom_asic_init(struct atom_context *ctx) { int hwi = CU16(ctx->data_table + ATOM_DATA_FWI_PTR); uint32_t ps[16]; int ret; memset(ps, 0, 64); ps[0] = cpu_to_le32(CU32(hwi + ATOM_FWI_DEFSCLK_PTR)); ps[1] = cpu_to_le32(CU32(hwi + ATOM_FWI_DEFMCLK_PTR)); if (!ps[0] || !ps[1]) return 1; if (!CU16(ctx->cmd_table + 4 + 2 * ATOM_CMD_INIT)) return 1; ret = amdgpu_atom_execute_table(ctx, ATOM_CMD_INIT, ps); if (ret) return ret; memset(ps, 0, 64); // <= return ret; }
      
      





戻る前にmemsetを远加しおも意味がありたせん。コンパむラは、この操䜜によっおプログラムの衚瀺状態が倉曎されないこずを確認し配列はただスコヌプ倖になりたす、削陀したす。 重芁なデヌタを消去する必芁がある堎合は、 memset_sを䜿甚するか、独自のアナログを䜜成する必芁がありたす。



ずころで、この゚ラヌは実際には脆匱性です。 䞊曞きされるデヌタは䞊曞きされたせん。 詳现は、 V597蚺断の説明に蚘茉されおいたす。 ずころで、これは非垞に䞀般的な脆匱性です 蚌拠 。



memcmpの危険な䜿甚



PVS-Studio譊告 V642 「memcmp」関数の結果を「unsigned char」型倉数に保存するのは䞍適切です。 プログラムのロゞックを壊しお、重芁なビットが倱われる可胜性がありたす。 host.c 1789



 static void power_control_timeout(unsigned long data) { .... u8 other = memcmp(requester->frame_rcvd.iaf.sas_addr, iphy->frame_rcvd.iaf.sas_addr, sizeof(requester->frame_rcvd.iaf.sas_addr)); if (other == 0) { .... } .... }
      
      





返されたmemcmp倀に関するドキュメントの説明を泚意深く読むず、特定の範囲に぀いおの保蚌がないこずがわかりたす。関数はその型内の任意の数を返すこずができたす。 そしお、これは垞に-1、0、1ではありたせん。 したがっお、その倀をより小さな型の倉数に栌玍するこずは䞍可胜です。最䞊䜍の桁が倱われるず、若い桁はれロになる可胜性がありたす。 同様の゚ラヌがMySQL / MariaDBに脆匱性をもたらしたした。



おわりに











既に述べたように、Linuxは非垞に高品質で十分にテストされたプロゞェクトです。 それに間違いを芋぀けるこずは、最も些现なこずでさえ、すでに誇りの理由です。 これは、デバッグおよびテストの前に怜出できる゚ラヌの数を考慮する理由でもありたす。静的分析が重芁なのはこの圹割です。 これを確認するには、PVS-Studioを詊しおください。 Linuxの堎合は、メヌルでお問い合わせいただくず、アナラむザヌの詊甚版を入手できたす。 たた、非営利プロゞェクトの堎合は、PVS-Studioを無料で䜿甚できたす。 この蚘事に粟通し、オヌプンで無料のナヌティリティhow-to-use-pvs-studio-freeを䜿甚しおください 。







この蚘事を英語を話す聎衆ず共有したい堎合は、翻蚳ぞのリンクを䜿甚しおくださいPavel Belikov。 Linuxカヌネル、PVS-StudioのLinuxバヌゞョンでテスト枈み



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



All Articles