Тестирование Linux-Версии Pvs-Studio На Ядре Linux



Тестирование Linux-версии PVS-Studio на ядре Linux

С момента выхода публичной Linux-версии PVS-Studio появление статьи о повторном тестировании ядра Linux было лишь вопросом времени.

Проект, который написан профессионалами со всего мира, которым пользуется большое количество людей в самых разных сферах, который регулярно проверяется и тестируется различными инструментами – проверка такого проекта станет серьёзным испытанием для любой статики.

анализатор.

Какие ошибки удалось найти PVS-Studio в таких условиях?



Как они проверяли

Мы уже проверил Ядро Линукс.

С тех пор многое изменилось — теперь проверка ОС так же проста и удобна, как проверка любого другого проекта:

  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
   

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

Адаптация и тестирование анализатора на Linux, который до недавнего времени был доступен только для Windows, заняло всего несколько месяцев.

В этот раз проверить ядро оказалось гораздо проще.

На тестирование была взята версия 4.9-rc4 (коммит bc33b0ca11e3df467777a4fa7639ba488c9d4911).

Для написания данной статьи были рассмотрены только диагностики общего назначения 1-2 уровней.

Стоит отметить высокое качество кода — плотность предупреждений, указывающих на реальные недостатки, крайне низка.

Для статьи я выбрал те предупреждения, которые с наибольшей вероятностью указывают на реальные ошибки/опечатки.

Нужно понимать, что помимо полезных предупреждений анализатор выдает еще и ложные срабатывания или выявляет плохо спроектированный или «дурно пахнущий» код, который, тем не менее, не является истинно ошибочным.

К сожалению, количество ложных срабатываний под Linux выше, чем хотелось бы.

Думаю, это связано с молодой версией анализатора, предназначенной для Linux-систем.

Мы проделали и продолжаем делать большую работу по минимизации количества ложных срабатываний.

Код Linux помог нам улучшить и внести много полезных изменений в анализатор, и теперь мы хотели бы ответить ему взаимностью.



Опечатки

Наиболее распространенная категория ошибок включает типичные опечатки и Копировать вставить .

Если вы уже читали наши статьи, думаю, вы уже в этом убедились.

Они появляются в любых проектах на любых операционных системах на любых языках.

Подобные ошибки раскрывают потенциал статического анализатора: их гораздо сложнее найти другими инструментами.

Посмотрим, как с ними обстоят дела в ядре Linux: Предупреждение PVS-Studio: В581 Условные выражения операторов 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; } .

}

Анализатор жалуется на два одинаковых условия подряд: видимо, забыли изменить второе са на сб .

Ну и кто после этого скажет, что крутые проекты не предполагают копипаста? Предупреждение PVS-Studio: В666 Рассмотрите возможность проверки третьего аргумента функции strncmp. Возможно, значение не соответствует длине строки, переданной первым аргументом.

спектральный.

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; }

Классический тип ошибки: функции нужно передать два аргумента: указатель на строку и ее длину.

Часто, когда в качестве аргумента используется литерал, люди ленятся считать длину и просто пишут число.

В игру вступает человеческий фактор: они совершают ошибки часто .

Посмотрите, в коде их несколько подряд стрнкмп .

Каждому из них передается литерал.

И в strncmp("фон", буф, 9) длина рассчитана неверно: слово «фон» состоит из 10, а не 9 символов.

Предупреждение PVS-Studio: В666 Рассмотрите возможность проверки третьего аргумента функции memcpy. Возможно, значение не соответствует длине строки, переданной вторым аргументом.

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: В653 Для инициализации массива используется подозрительная строка, состоящая из двух частей.

Возможно, что запятая пропущена.

Рассмотрите возможность проверки этого литерала: «30 минут» «Нет тайм-аута».

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" }; .

}

Как известно, два литерала, написанные подряд, объединяются.

Это позволяет их удобно использовать, например, в макросах.

Опасность возникает, когда мы пишем массив таких литералов: мы можем пропустить запятую и получить неожиданный результат. В этом случае последние два литерала «слипнутся» и вы получите «30minNo timeout».

Это двойная ошибка.

Во-первых, текст неверен, во-вторых, в массиве будет отсутствовать один элемент, что может привести к его выходу за границу массива.

Советую использовать другой способ форматирования, при котором станет заметна такая ошибка:

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

Подробнее об этом способе форматирования табличного кода написал мой коллега Андрей Карпов.

Предлагаю вам прочитать главу N13 из его короткометражки.

книги .

Предупреждение PVS-Studio: В764 Возможный неверный порядок аргументов, передаваемых в функцию «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);



Тестирование Linux-версии PVS-Studio на ядре Linux

Но самое смешное, что ошибка здесь.

уже был : параметры действительно перепутали, просто забыли поправить объявление.

Хорошо, что анализатор тоже нашел это место.

Предупреждение PVS-Studio: В549 Первый аргумент функции memcpy равен второму аргументу.

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(priv->pmkid_list.pmkidlist[i].

pmkid, priv->pmkid_list.pmkidlist[i + 1].

pmkid, PMKID_LEN);



Неиспользуемые переменные

Предупреждение PVS-Studio: В575 Функция strncasecmp обрабатывает элементы «0».

Проверьте третий аргумент. 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 .

В коде нет места, где изменяется переменная длина , поэтому его значение останется нулевым.

Вероятно, следовало использовать размер .

Предупреждение PVS-Studio: В751 Параметр 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: В502 Возможно, оператор «?:» работает не так, как ожидалось.

Оператор '?:' имеет более низкий приоритет, чем оператор '|'.

оператор.

ядро.

с 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. Дело в том, что он имеет низкий приоритет, а в сложных выражениях легко запутаться и получить совершенно другой порядок вычислений.

Поэтому в случае сомнений лучше использовать круглые скобки.



Подозрительные проверки

Предупреждение PVS-Studio: В517 Обнаружено использование шаблона 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 .

Если мы посмотрим на его объявление, мы увидим еще два макроса:

#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: В547 Выражение '(ptr[3] & 0x1E) != 0x03' всегда истинно.

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; } } } .

}

Ошибка связана с битовыми операциями.

Результат побитового соединения с 0x1E потому что один бит никогда не будет равен значению 0x03 :

Тестирование Linux-версии PVS-Studio на ядре Linux

Предупреждение PVS-Studio: В517 Обнаружено использование шаблона 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 .



Этот код выглядит очень странно.

Кажется, диапазон значений ясно обсуждается выражением выше: включено равно 0 или 1 .

Но целые проверяются 4 значения.

При этом комментарии только мешают: если цифры должны были обозначать возможное значение переменной, то теперь они не соответствуют действительности: проверка на 1 И 2 написано так же.

Предупреждение PVS-Studio: В517 Обнаружено использование шаблона 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; }

Еще одно место, где идут два условия подряд. Обратите внимание, что тела разные.

Трудно сказать, есть ли здесь настоящий баг или это просто неиспользуемый код: это уже задача разработчиков проекта.

Задача анализатора — обратить внимание на подозрительное место.

Предупреждение PVS-Studio: В734 Лишняя проверка.

Изучите условия, содержащие поиск подстрок «перемежитель» и «деперемежитель».

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; }

В этом фрагменте последовательно проверяется наличие нескольких подстрок в одной строке.

Для наглядности я оставил только интересующие нас подстроки.

Предположим, мы не нашли перемежитель - тогда нет смысла искать деперемежитель , потому что подстроки перемежитель определенно больше нет. Поэтому этот участок кода никогда не будет работать, но поскольку тела if и else одинаковы, это не проблема.

Это просто лишний код. Предупреждение PVS-Studio: В547 Выражение «блокировать» всегда истинно.

svlock.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); }

Этот пример демонстрирует, почему статическому анализатору недостаточно выполнить анализ на основе шаблонов, минуя А.

С.

Т.

.

Важно уметь выполнять анализ потока управления и анализ потока данных.

В тот момент, когда блок == НУЛЬ это происходит возвращаться , соответственно, далее по коду мы точно можем сказать, что указатель ненулевой.

И когда мы встретим испытание НУЛЕВОЙ , мы точно понимаем, что здесь что-то не так.

Похоже, вторая проверка указателя здесь просто ненужна.

Однако что, если они захотят проверить здесь другую переменную? Кто знает. Этот код анализатора обязательно нужно предоставить разработчику на проверку.

Аналогичная ситуация: Предупреждение PVS-Studio: В547 Выражение «sym» всегда истинно.

меню.

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: В733 Возможно, расширение макроса привело к неправильному порядку вычислений.

Проверьте выражение: 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); .

}

Но такие ошибки очень редки.

До этого я видел только одно срабатывание этой диагностики в реальном проекте: примечательно, что это было FreeBSD .

В определении макроса допущена ошибка: все его параметры лучше всего заключить в круглые скобки.

Если этого не сделать, то возможен следующий случай: при подстановке 'x+5' в 'secs*1000' результат будет 'x+5*1000', а это явно не то, что ожидал автор.



Бессмысленный мемсет

Предупреждение PVS-Studio: В597 Компилятор может удалить вызов функции memset, который используется для очистки буфера ps. Функцию memset_s() следует использовать для удаления личных данных.

атом.

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_s или напишите свой аналог.

Эта ошибка, кстати, на самом деле является уязвимостью.

Данные, которые необходимо удалить, не перезаписываются.

Подробности можно найти в описании диагностики.

В597 .

Кстати, это очень распространенная уязвимость: доказательство .



Опасное использование memcmp

Предупреждение PVS-Studio: В642 Сохранение результата функции memcmp внутри переменной типа unsigned char нецелесообразно.

Значимые биты могут быть потеряны, нарушая логику программы.

хост.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) { .

} .

}

Если вы внимательно прочитаете, что говорится в документации о возвращаемом значении мемкмп , то мы увидим, что нет никакой гарантии относительно какого-либо конкретного диапазона: функция может возвращать любое число в пределах своего типа.

И это не всегда -1 , 0 И 1 .

Поэтому хранить его значение в переменной меньшего типа нельзя: если старшие биты потеряны, младшие биты могут стать нулевыми.

Аналогичная ошибка привела к уязвимости в MySQL/МарияДБ .



Заключение



Тестирование Linux-версии PVS-Studio на ядре Linux

Как уже говорилось, Linux — очень качественный, хорошо проверенный проект. Обнаружить в нем ошибку, даже самую незначительную – это уже повод для гордости.

Это также повод задуматься о том, сколько ошибок можно найти перед отладкой и тестированием: именно в этой роли ценен статический анализ.

Вы можете убедиться в этом, попробовав PVS-Studio. Для Linux вы можете получить пробную версию анализатора, написав нам на почту.

А для своих некоммерческих проектов вы можете использовать PVS-Studio бесплатно: для этого достаточно ознакомиться с Эта статья и используйте открытую и бесплатную утилиту как использовать-pvs-studio бесплатно .



Тестирование Linux-версии PVS-Studio на ядре Linux

Если вы хотите поделиться этой статьей с англоязычной аудиторией, воспользуйтесь ссылкой для перевода: Павел Беликов.

Ядро Linux, протестировано Linux-версией PVS-Studio Вы прочитали статью и у вас есть вопросы? Часто о наших статьях задают одни и те же вопросы.

Ответы на них мы собрали здесь: Ответы на вопросы читателей статей о PVS-Studio версии 2015 .

Пожалуйста, проверьте список.

Теги: #Разработка для Linux #с открытым исходным кодом #C++ #статический анализ кода #ядро Linux #Системное программирование #pvs-studio #статический анализ кода

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.