С момента выхода публичной Linux-версии PVS-Studio появление статьи о повторном тестировании ядра Linux было лишь вопросом времени.
Проект, который написан профессионалами со всего мира, которым пользуется большое количество людей в самых разных сферах, который регулярно проверяется и тестируется различными инструментами – проверка такого проекта станет серьёзным испытанием для любой статики.
анализатор.
Какие ошибки удалось найти PVS-Studio в таких условиях?
Как они проверяли
Мы уже проверил Ядро Линукс.С тех пор многое изменилось — теперь проверка ОС так же проста и удобна, как проверка любого другого проекта:
Адаптация и тестирование анализатора на Linux, который до недавнего времени был доступен только для Windows, заняло всего несколько месяцев.pvs-studio-analyzer trace - make pvs-studio-analyzer analyze -o /path/to/report.log -j8
В этот раз проверить ядро оказалось гораздо проще.
На тестирование была взята версия 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);
Но самое смешное, что ошибка здесь.
уже был : параметры действительно перепутали, просто забыли поправить объявление.
Хорошо, что анализатор тоже нашел это место.
Предупреждение 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 :
Предупреждение 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 вы можете получить пробную версию анализатора, написав нам на почту.
А для своих некоммерческих проектов вы можете использовать PVS-Studio бесплатно: для этого достаточно ознакомиться с Эта статья и используйте открытую и бесплатную утилиту как использовать-pvs-studio бесплатно .
Если вы хотите поделиться этой статьей с англоязычной аудиторией, воспользуйтесь ссылкой для перевода: Павел Беликов.
Ядро Linux, протестировано Linux-версией PVS-Studio Вы прочитали статью и у вас есть вопросы? Часто о наших статьях задают одни и те же вопросы.
Ответы на них мы собрали здесь: Ответы на вопросы читателей статей о PVS-Studio версии 2015 .
Пожалуйста, проверьте список.
Теги: #Разработка для Linux #с открытым исходным кодом #C++ #статический анализ кода #ядро Linux #Системное программирование #pvs-studio #статический анализ кода
-
Gotouch — Карманный Проектор
19 Oct, 24 -
Блог Яндекса. Теперь Банан!
19 Oct, 24 -
Перчатка – Устройства Ввода. Датчик Изгиба
19 Oct, 24 -
Планшет Против Нетбука
19 Oct, 24 -
Интернет-Ограничения Ii: Скоро В России
19 Oct, 24