Бестиарий программирования
1.14K subscribers
373 photos
5 videos
5 files
469 links
Наблюдения за жизнью ошибок в коде.
Андрей Карпов.

ГОСТ Р 71207-2024, ГОСТ Р 56939-2024, РБПО, Статический анализ кода

Канал-дублёр в MAX: https://max.ru/join/3VWTp9apkQvTMSRQ__LGiTQ5NGVBj8p_tOpwlQO6vS8
Download Telegram
Элегантность кода благодаря C++23

Продолжим разбирать приёмы рефакторинга и посмотрим, как std::ranges::views::enumerate помогает сделать код более элегантным.

В заметке я рассматривал рефакторинг и оптимизацию кода за счёт объединения циклов. В итоге я остановился на следующем варианте кода:
static TensorImpl make_contiguous_tensor(const std::vector<int64_t>& sizes)
{
  auto q = sizes.size();
  std::vector<int64_t> strides(q);
  int64_t acc = 1;
  int64_t ne = 1;
  for (auto sz : std::ranges::views::reverse(sizes))
  {
    strides[--q] = acc;
    acc *= (sz == 0 ? 1 : sz);
    ne *= sz;
  }
  //....
}

Его недостаток в том, что всё равно необходимо использовать переменную q для работы с контейнером strides. Как можно написать ещё лаконичнее, я не сообразил, но такой способ есть!

После публикации мне подсказали про enumerate. Эта штука появилась в C++23, и я как-то её пропустил. Сложно уследить за всеми нововведениями C++.

С помощью enumerate можно сразу перебирать и элементы, и их индексы:
constexpr static auto v = {'A', 'B', 'C', 'D'};
for (auto const [index, letter] : std::views::enumerate(v))
    std::cout << '(' << index << ':' << letter << ") ";

Будет напечатано: (0:A) (1:B) (2:C) (3:D).

Но нам нужен обратный порядок, и такой вариант не подходит:
for (auto const [i, sz] :
  std::views::enumerate(
    std::ranges::views::reverse(sizes)))

Этот цикл будет перебирать элементы с конца, а индексы — по возрастанию от 0. Можно сделать, чтобы значения i также шли в обратном порядке? Можно. Это делается с помощью std::views::enumerate(sizes) | std::views::reverse.

В итоге можно сократить код ещё на одну строчку:
static TensorImpl make_contiguous_tensor(const std::vector<int64_t>& sizes)
{
  std::vector<int64_t> strides(sizes.size());
  int64_t acc = 1;
  int64_t ne = 1;
  for (auto const [i, sz] : std::views::enumerate(sizes) | std::views::reverse)
  {
    strides[i] = acc;
    acc *= (sz == 0 ? 1 : sz);
    ne *= sz;
  }
  //....
}

Нельзя назвать это большим достижением, но зато на практике применили одну из новых возможностей C++23.

Что со скоростью кода? Замер показал, что в рамках погрешности его скорость не изменилось. Т.е. этот вариант работает так же, как вариант №2 — "Мой вариант одним циклом" (см. замер скорости работы). Это не удивительно, так как по сути код идентичен предыдущему.
👍6
У нас еще одна крутая новость! Мы запускаем новый цикл вебинаров "Надёжность, качество, безопасность ПО: методология и инструменты" ⚡️

В предыдущем цикле вебинаров "Вокруг РБПО" мы рассмотрели 25 процессов из ГОСТ Р 56939—2024, направленных на создание безопасных программных решений. Теперь поговорим не только о безопасности, но и в целом о создании качественного и надёжного ПО.

Вместе с экспертами из разных компаний мы поговорим про развитие DevSecOps практик, подходы к написанию качественного кода, вопросы регуляторики, ИИ, функциональную безопасность встраиваемого ПО, а также про инструменты композиционного, статического, динамического анализа.

Первый вебинар "Методы защиты и обеспечения безопасности ПО" пройдет 12 августа в 15:00 🗓

- Александра Уварова (Developer Advocate, PVS-Studio) в своем докладе "Автоматизация процессов обеспечения безопасности" расскажет, как одновременно с ускорением разработки и поставки программного обеспечения должна развиваться безопасность. В рамках вебинара рассмотрим методы эффективной интеграции SAST-инструмента в DevSecOps-пайплайн, чтобы выявлять уязвимости на ранних этапах, снижать риски и повышать эффективность безопасной разработки.

- Дьяков Алексей (Эксперт по монетизации и защите ПО, Guardant) в своем докладе "Защита ПО: от анализа угроз до выбора стратегии" расскажет, почему защита ПО — необходимость, а не опция. Рассмотрим модели угроз, способы защиты и оценим, что компания может сделать самостоятельно. Главное — честно сравним, когда выгоднее строить свое решение, а когда покупать готовое.

Регистрация доступна по ссылке 🔗

📲Мы в MAX
#вебинар
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
Статья вне тренда: Помешательство вокруг ИИ парализовало принятие решений

И некоторые комментарии хороши:
Мне очень интересно наблюдать за людьми которые одновременно понимают, что нейрослоп не может написать сказку для детей так, чтобы на второй странице не потерять смысл. И при этом убежденны, что эта же конструкция способна давать советы по бизнесу или легко написать операционную систему.
💯9👀2
SAST и DAST спешат на помощь (часть 1 из 3)

Никто не спорит о пользе статического или динамического анализа кода. Но некоторые разработчики воспринимают эту пользу как абстрактную и останавливаются на уровне написания юнит-тестов. Сейчас вспомнился один пример из мира C, который хорошо показывает, что юнит-тесты иногда плохо помогают находить даже самые типовые ошибки.

Баг, который я сейчас покажу, я уже рассматривал в статье "Красивая ошибка в реализации функции конкатенации строк". В проекте LFortran была вот такая функция для конкатенации (объединения) двух строк в новом буфере:
void _lfortran_strcat(char** s1, char** s2, char** dest)
{
    int cntr = 0;
    char trmn = '\0';
    int s1_len = strlen(*s1);
    int s2_len = strlen(*s2);
    int trmn_size = strlen(&trmn);
    char* dest_char = (char*)malloc(s1_len+s2_len+trmn_size);
    for (int i = 0; i < s1_len; i++) {
        dest_char[cntr] = (*s1)[i];
        cntr++;
    }
    for (int i = 0; i < s2_len; i++) {
        dest_char[cntr] = (*s2)[i];
        cntr++;
    }
    dest_char[cntr] = trmn;
    *dest = &(dest_char[0]);
}

Здесь классическая ошибка, когда выделяемый буфер на 1 байт меньше необходимого. Не учтён терминальный ноль. Вернее, учтён, но его размер вычисляется неправильно.
char trmn = '\0';
int trmn_size = strlen(&trmn);

Здесь символ trmn интерпретируется как пустая строка. Её длина нулевая. Соответственно, переменная trmn_size, название которой хранит размер терминального нуля, всегда будет равна 0. В результате терминальный ноль будет записан уже за пределами выделенного буфера.

Ошибка простая и понятная. Выход за границу буфера в программе на C это вообще типовая проблема. Что тут ещё обсуждать? Ошибка найдена, дело закрыто.

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

Функция malloc на самом деле выделяет не столько памяти, сколько у неё просят. Она запрашивает у операционной системы большие блоки и нарезает их на куски, добавляя служебную информацию. Точный размер блока зависит от конкретной реализации.

Даже если вы запросите malloc(1), аллокатор все равно вернёт указатель на блок размером, скажем, 32 байта (минус служебные поля — вам достанется 24 полезных байта). Это делается для обеспечения выравнивания памяти (обычно 16-байтного) и упрощения менеджера памяти.

Возвращаемый указатель должен быть выровнен. Поскольку функция malloc ничего не знает о том, какие типы будут храниться в выделенной памяти, она ориентируется на самое большое выравнивание, которое может потребоваться. На практике (x86-64) malloc выдаёт адреса, кратные 16. Поэтому размер блока всегда округляется вверх до следующей границы выравнивания.

Я описал всё очень поверхносно и приблизительно. Важно то, что на практике в рассмотренном коде обычно будет выделяться памяти больше, чем требуется! Если блоки кратны 16 байтам, то запись терминального нуля в соседний блок случится, только если результирующая строка также кратна 16 байтам. Другими словами, вероятность, что ошибка проявит себя, равна 1 к 16 (или не 16 — это число взято как одно из возможных).

Тут следует сразу сделать оговорку про неопределённое поведение. Формально код в любом случае некорректен, так как содержит выход за границу массива. Нельзя рассуждать, как он будет работать, как ошибка проявит себя и т.д.

Однако неопределённое поведение — это в том числе ситуация, когда неправильный код работает так, как ожидалось. Рассматриваемая ситуация, когда из-за особенностей работы менеджера памяти этой памяти выделяется больше, как раз и может привести к видимости, что всё работает хорошо.
SAST и DAST спешат на помощь (часть 2 из 3)

Можно написать юнит-тесты типа таких:
void Test1()
{
    char *a = "a";
    char *b = "";
    char *q;
    _lfortran_strcat(&a, &b, &q);
    int ok = strcmp(q, "a") == 0;
    printf("%s+%s=%s %s\n", a, b, q, ok ? "ok" : "err");
    free(q);
}
 
void Test2()
{
    char *a = "12";
    char *b = "345";
    char *q;
    _lfortran_strcat(&a, &b, &q);
    int ok = strcmp(q, "12345") == 0;
    printf("%s+%s=%s %s\n", a, b, q, ok ? "ok" : "err");
    free(q);
}

И ничего не заметить. Тесты проходят успешно:
a+=a ok
12+345=12345 ok

Тесты на коротких строках не выявят проблему. В голову может и не прийти мысль попробовать работать с длинными строками. Зачем? На первый взгляд такие тесты ничего не дают. Скорее всего, будут созданы тесты на краевые случаи (пустые строки), но они нерелевантные для поиска обсуждаемого бага.

При этом даже с длинными строками, вероятность заскочить в соседний блок всего 1 к N (где N, например 16). Можно объединить две огромные строки из 111111 символов и всё будет хорошо, ведь конец результирующей строки (222222 символов) не лежит на границе 16-байтного блока.

Ещё юнит-тесты

Только не подумайте, что я критикую юнит-тесты. Это замечательная штука! Однако бывают ошибки, которым легко от этих юнит-тестов спрятаться. И перед нами как раз такой случай.
Дело в том, что легко не заметить, даже когда будет пересекаться тот невидимый рубеж выделенного блока памяти. Следующий тест создаёт не такие уж короткие строки длинной 37 символов. Как думаете, такой тест приведёт к падению программы или ещё чему-то?
void Test3()
{
    char *a = "123";
    for (unsigned i = 1; i != 35; ++i)
    {
        char *b = (char *)malloc(i + 1);
        memset(b, 'a', i);
        b[i] = '\0';
        char *q;
        _lfortran_strcat(&a, &b, &q);
        int ok = strlen(q) == 3 + i;
        printf("%u %s %s\n", i, q, ok ? "ok" : "err");
        free(b);
        free(q);
    }
}

Приведёт или нет — неизвестно, ведь тут неопределённое поведение. Но на практике я собираю его gcc с ключом -O2 и не наблюдаю какого-то проявления ошибки, хотя, по идее, блоки памяти уже испорчены. Но по тесту всё ещё кажется, что всё нормально:
1 123a ok
2 123aa ok
3 123aaa ok
4 123aaaa ok
5 123aaaaa ok
6 123aaaaaa ok
7 123aaaaaaa ok
8 123aaaaaaaa ok
9 123aaaaaaaaa ok
10 123aaaaaaaaaa ok
11 123aaaaaaaaaaa ok
12 123aaaaaaaaaaaa ok
13 123aaaaaaaaaaaaa ok
14 123aaaaaaaaaaaaaa ok
15 123aaaaaaaaaaaaaaa ok
16 123aaaaaaaaaaaaaaaa ok
17 123aaaaaaaaaaaaaaaaa ok
18 123aaaaaaaaaaaaaaaaaa ok
19 123aaaaaaaaaaaaaaaaaaa ok
20 123aaaaaaaaaaaaaaaaaaaa ok
21 123aaaaaaaaaaaaaaaaaaaaa ok
22 123aaaaaaaaaaaaaaaaaaaaaa ok
23 123aaaaaaaaaaaaaaaaaaaaaaa ok
24 123aaaaaaaaaaaaaaaaaaaaaaaa ok
25 123aaaaaaaaaaaaaaaaaaaaaaaaa ok
26 123aaaaaaaaaaaaaaaaaaaaaaaaaa ok
27 123aaaaaaaaaaaaaaaaaaaaaaaaaaa ok
28 123aaaaaaaaaaaaaaaaaaaaaaaaaaaa ok
29 123aaaaaaaaaaaaaaaaaaaaaaaaaaaaa ok
30 123aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ok
31 123aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ok
32 123aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ok
33 123aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ok
34 123aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ok

Падение случится при константе 38 в цикле for (unsigned i = 1; i != 38; ++i):
free(): invalid pointer
Program terminated with signal: SIGSEGV

Не надо искать какой-то особый смысл в числе 38, просто так получилось. Интересен момент, как долго ошибка пряталась от юнит-тестов.
SAST и DAST спешат на помощь (часть 3 из 3)

При этом такую ошибку можно моментально обнаружить с помощью статического или динамического анализа.

Статический анализатор PVS-Studio сразу предупреждает об аномалии в коде с помощью сообщения: V742 Function receives an address of a 'char' type variable instead of pointer to a buffer. Inspect the first argument.
Или достаточно воспользоваться динамическим анализом. AddressSanitizer (gcc ключ: fsanitize=address). Он сразу покажет проблему уже на первом самом простом тесте Test1.

Юнит-тесты и динамический анализ работают в паре. Санитайзер обнаруживает проблему на этапе запуска юнит-теста. Не будет теста — ошибка всплывёт гораздо позже.

Заключение

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

Используйте всё. По началу это потребует вложений, но со временем окупит себя ранним выявлением большого процента багов на самых ранних этапах разработки. Чем раньше ошибка обнаружена, тем проще и дешевле её исправление (подход shift-left testing).
👍7
Раздутый C++ код (часть 1 из 2)

Есть такая старая программистская байка, что нельзя платить программистам за строки кода, так как тогда они будут писать длинный бестолковый код и любить метод copy-paste. Будущее наступило. Этими "программистами" является генеративный ИИ (GenAI), которому как раз платят за строки кода.

Я проверил VibeTensor с помощью статического анализатора PVS-Studio, а также посмотрел С++ код глазами. Было интересно узнать, как много ошибок в нём можно найти с помощью классического обзора кода и статического анализа.

Так вот, у меня нет ответа на этот вопрос. Непонятно, потому что главная проблема этого кода в том, что он ужасно раздут. Это сильно мешает его обзору. Мне тяжело продираться сквозь это болото, а вместе со мной "вязнет" и статический анализатор.

Впрочем, ожидать большого количества ошибок здесь тоже не стоит: по-настоящему полезного кода в этом проекте кот наплакал. Как же так? Проект вроде не такой уж маленький. Проанализированных C++ файлов более 400, а количество строк кода — около 100,000.

Да, но это только кажется, что там есть что смотреть и анализировать. Повторюсь: основная проблема качества этого кода — его избыточность. Она выражена как в постоянном повторении блоков кода, так и просто в бессмысленных лишних действиях, растягивающих код.

Раньше бы сказали, что этот проект писался методом copy-paste. В данном случае это не так, но генерация кода приводит ровно к таким же последствиям. Вместо выноса обобщённой функциональности в функции, вновь и вновь генерируется код для решения схожим проблем.

Например, я уже писал в статье "C++: Пиши, сокращай, оптимизируй", про блок кода, который встречается 9 раз и при этом сам растянут. Скоро напишу другую статью, а пока рассмотрю ещё один пример.
👍3
Раздутый C++ код (часть 2 из 2)

А вот другой пухлый код, где PVS-Studio выдаёт сразу три предупреждения:
• V547 [CWE-570] Expression 'is_empty' is always false. tensor_bindings.cc 3007
• V547 [CWE-570] Expression 'print_size' is always false. tensor_bindings.cc 3015
• V547 [CWE-571] Expression '!parts.empty()' is always true. tensor_bindings.cc 3023

Код, на который выданы предупреждения, на первый взгляд умный, с массивом, с циклом... А если присмотреться — лабуда.
bool is_empty = false; // handled above; always false here
bool print_size = is_empty && (self.sizes().size() != 1);
bool suppress_dtype_non_empty = (!is_empty) &&
  (self.dtype() == ScalarType::Float32 ||
   self.dtype() == ScalarType::Int64 ||
   self.dtype() == ScalarType::Bool);
bool print_dtype = !suppress_dtype_non_empty;
if (is_empty) {
  // For empty tensors, only print dtype when dtype != default float32
  print_dtype = (self.dtype() != ScalarType::Float32);
}
 
std::string out = "tensor(";
out += body;
std::vector<std::string> parts;
if (print_size) {
  parts.push_back(std::string("size=") + format_sizes(self.sizes()));
}
if (print_dtype) {
  parts.push_back(std::string("dtype=") + dtype_name(self.dtype()));
}
// Always include device suffix for CUDA tensors
parts.push_back(std::string("device='cuda:") +
                std::to_string((int)self.device().index) + "'");
if (!parts.empty()) {
  out += ", ";
  for (std::size_t i = 0; i < parts.size(); ++i) {
    if (i) out += ", ";
    out += parts[i];
  }
}
out += ")";
return out;

Если присмотреться получше, то всё это можно сократить в три раза:
std::string out = "tensor(" + body + ", ";
 
if (self.dtype() != ScalarType::Float32 &&
    self.dtype() != ScalarType::Int64 &&
    self.dtype() != ScalarType::Bool)
{
  out += std::string("dtype=") + dtype_name(self.dtype()) + ", ";
}
out += "device='cuda:" + std::to_string((int)self.device().index) + "')";
return out;

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

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

Итого: нет в проекте никаких настоящих 100,000 строк С++ кода. Думаю, что если вынести дубликаты в функции и провести рефакторинг, количество кода сократится раз в 5. Проект на 20,000 строк кода — это баловство. Вот и вижу в нём не ошибки, а проблему раздутого кода и предупреждения анализатора про большое количество ложных/истинных условий и т.п.
👏4
Forwarded from feelin (Valerii Filatov)
GuardConf 2026

12 ноября этого года в Москве пройдёт конференция GuardConf 2026, посвящённая разработке и защите софта.

В этом году я состою в программном комитете, и мы с коллегами прямо сейчас собираем программу — на прошлой неделе открыли Call For Papers. Если есть практический опыт, которым хочется поделиться, можно податься с докладом на один из двух треков:

Инструменты разработчика: качество кода, инженерные практики, автоматизация, безопасность, Open Source, производительность, AI и другие инструменты.

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

Подробнее про конференцию можно прочитать на сайте, там же уже открыта форма регистрации. А по поводу спикерства можно написать мне напрямую.

Увидимся в ноябре!

🎤 feelin
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3👍1
На следующей неделе сразу два интересных вебинара 🔥

1️⃣ Как разговорить человека и договориться

Small talk — это не просто короткая беседа, а способ устанавливать контакт, создавать доверие и открывать новые возможности для общения. На вебинаре разберём, как легко начинать и поддерживать такие разговоры, а также как использовать small talk в рабочих ситуациях — от нетворкинга и новых знакомств до сложных обсуждений, переговоров и договорённостей.

🗓 27 августа в 14:00

Регистрация по ссылке 🔗

2️⃣ Агенты пишут код, а кто его проверяет?

Третий вебинар цикла "Качество и безопасность ПО в эпоху GenAI". Мы разберём, как выглядит работа с агентами в продакшене уже сегодня: от планирования и итераций до ревью, файлов-памяти и автоматизации проверки кода, а также обсудим, как контролировать результат такой разработки.

🗓 28 августа в 15:00

Регистрация по ссылке 🔗

Присоединяйтесь и приглашайте коллег!

📲Мы в MAX

#вебинар #ai #smalltalk
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Сегодня День рождения моего любимого писателя Говарда Филлипса Лавкрафта. В его честь я позволю себе шалость и презентую обзор материалов по РБПО в его литературном стиле. Заодно пригодится тем, кто пропустил предыдущие посты про РБПО-вебинары. Встречайте неименуемый ужас творений беспечных программистов.

В тёмных коридорах современной разработки ПО, куда редко проникает свет тестирования и куда не доносится эхо код-ревью, зародилось нечто. Оно притаилось в недрах непроверенных библиотек. Нечто, что шепчет сквозь строки небезопасного кода. Древние именовали это Багом. Современные адепты тайных знаний зовут Уязвимостью.

Долгие годы человеческие умы пытались объять необъятное — описать ритуалы, способные защитить творения рук наших от порождений Хаоса. Так в 2016 году был создан первый фолиант — ГОСТ Р 56939, призванный оградить код от тварей из бездны. Но время шло, и Древние Стандарты ветшали. Они молчали о кошмарах цепочек поставок, о тёмной магии CI/CD, о незримой Поверхности Атаки, сквозь которую сущности извне проникают в наш мир.

И тогда, в конце 2024 года, был явлен новый манускрипт — ГОСТ Р 56939–2024 «Разработка безопасного программного обеспечения», начертанный коллективным разумом посвящённых в тайны РБПО.

Перед вами не просто выжимка. Это ключ к запретному знанию, запертому на странице сайта древних Единорогов. Это труд жреца статического анализа из ордена PVS-Studio, который осмелился не просто прочесть Писания, но и сопроводить их голосами тридцати экспертов, чьи разумы соприкоснулись с Истиной.

Узрите! Разработка безопасного программного обеспечения (РБПО) по ГОСТ Р 56939—2024!

Новый ГОСТ говорит нам о 25 процессах. Не об абстрактных идеях, а о конкретных ритуалах, каждый из которых — это грань защитного пентакля, оберегающего ваше творение от Хаоса.

О тех, кто должен познать страх (процессы 1–2: Планирование и Обучение). Безумец тот, кто решит, что способен в одиночку противостоять Тьме. Фолиант гласит: внедрение обрядов безопасности потребует ресурсов. 10–15% бюджета — такова цена жертвы, которую нужно принести, чтобы ваша команда не стала слепыми котятами, играющими с неведомыми силами. В полном тексте хроник вы найдёте отсылки к курсам, чтобы ваши адепты не накликали беду неосторожным заклинанием.

Картография незримого (процессы 6–7: Архитектура и Поверхность Атаки). Самые страшные угрозы проникают не через щели в коде, а через изъяны в фундаменте вашего творения. Гайд научит вас видеть незримое — определять ту самую Поверхность Атаки, через которую Древнее Зло может просочиться в реальность. Ибо бессмысленно проверять каждую строку, если дверь в подвал распахнута настежь. В полном тексте — откровения о taint-анализе и инструменте Natch, чьё имя звучит как шёпот ангела во тьме.

Некрономикон кодирования (процессы 8–10: Правила, Экспертиза и Статический анализ). Код, написанный без правил, подобен безумным письменам на стенах Жёлтых домов. Даже несовершенный стандарт лучше, чем хаос, где каждая переменная названа именем Древнего Бога. Гайд поведает о «форматировании таблицей» — древнем заклинании против опечаток, и о том, как Статический Анализ (вкупе с фолиантом ГОСТ Р 71207–2024) становится вашим всевидящим оком, способным узреть дефекты там, где человеческий разум меркнет.

Скверна цепочки поставок (процессы 16–17). Величайший ужас современности таится не в вашем коде, а в том, что вы заимствуете у чужих культов. Зловещие артефакты, проникающие под видом безобидных библиотек, — вот истинный лик страха. Треть кошмаров из списка OWASP Top 10 — это порождения данной скверны. Познайте суть SCA, поиска закладок и атак через галлюцинации ИИ. Узрите сущности в лучах сверкающего CodeScoring.

…и это лишь начало. Остальные главы Послания охватывают ужасы фаззинг-тестирования, кошмар неправильной сборки, муки проигнорированных уязвимостей и даже мрачный ритуал вывода ПО из эксплуатации, когда нужно уничтожить все следы, чтобы никто не вызвал то, что спало.

Ф’нглуи мглв’нафх РБПО Р’льех вгах’нагл фхтагн!
🔥11😁7
Записи двух новых вебинара. Первый, продолжение смежных с РБПО тем – рассматриваем инструменты и подходы. Гостем стал Алексей Дьяков из Guardant и рассказал о защите ПО (противодействию пиратству).

Второй вебинар о том, как ставить грамотные цели и достигать их. Гость – Александр Швец (CTO Авито Товары).

Мы открыты к сотрудничеству и приглашаем гостей для участия в вебинарах, посвященных GameDev, управлению командой, GenAI т.д. Подробнее.
4
Релиз PVS-Studio 8.00 с поддержкой языков Go, JS, TS

Мы выпустили восьмую версию PVS-Studio, где расширен список анализируемых языков программирования:
C, C++, C#, Java, Go, JavaScript, TypeScript.

PVS-Studio разрабатывается компанией ООО "ПВС", основанной в 2008 году. Сведения о PVS-Studio включены в единый реестр российских программ для ЭВМ и баз данных на основании приказа Министерства цифрового развития, связи и массовых коммуникаций Российской Федерации от 18.03.2021 №156, запись в реестре №9837 от 18.03.2021.

Инструментальное средство PVS-Studio разрабатывается с учётом требований, предъявляемых к статическим анализаторам в ГОСТ Р 71207-2024. Уровень совместимости с отраслевым стандартом зависит от языка.

C, C++, C#, Java
Статический анализатор кода PVS-Studio для языков C, C++, C# и Java удовлетворяет функциональным требованиям к инструментам, требуемых для проведения исследований согласно разделу 4.2 «Статический анализ объекта оценки (САО)» методического документа «Методика выявления уязвимостей и недекларированных возможностей в программном обеспечении» (утверждён ФСТЭК России 12 мая 2026 г.) для 6, 5 и 4 уровня доверия (по 3, 2 и 1 уровню информация будет представлена позже).

В том числе PVS-Studio соответствует дополнительным требованиям к исследованиям (усиления) для 4-го уровня доверия, заключающихся в соответствии требованиям ГОСТ Р 71207-2024 «Защита информации. Разработка безопасного программного обеспечения. Статический анализ программного обеспечения. Общие требования».

PVS-Studio выявляет критические ошибки и может использоваться при разработке безопасного программного обеспечения согласно требованиям ГОСТ Р 56939-2024.

Go, JavaScript, TypeScript
На данный момент реализованы легковесные движки анализаторов для этих языков. В первой редакции анализаторы содержат по 41 диагностическому правилу, CLI для каждого анализатора, а также плагины для интегрированных сред разработки WebStorm и GoLand.

Некоторые другие новшества
Добавлена поддержка компиляторов kcc ARMV7 и TI C2000-CGT на всех платформах с помощью мониторинга/трассировки компиляции.

Появилась поддержка плагина PVS-Studio для Qt Creator версий 20.x.

Вскоре стартует программа раннего доступа Atlas Server, платформы для автоматической проверки качества и безопасности исходного кода.

Ссылки:
1.      Все варианты загрузки PVS-Studio.
2.      Запросить триальный ключ.
3.      Раздел о сертификации.
4.      Контакты: форма обратной связи / запросить ВКС / телефон +7(903)844-02-22.
🔥4