Как стать автором
Обновить

Использование подсказок, включаемых в исходный код, помогающих GCC выявлять случаи переполнения буфера

Время на прочтение11 мин
Количество просмотров4.4K
Всего голосов 25: ↑25 и ↓0+25
Комментарии8

Комментарии 8

А еще есть в GLIBC FORTIFY_SOURCE. И clang более жестко ловит подобные ошибки, и приучает к дисциплине и написанию правильного кода, и есть статический анализатор. Плюс в clang старается делать работу с памятью более безопасной, и было такое, что софт падал у людей собравших его с gcc, из-за ошибки при работе с malloc, а в clang не было такой ошибки и он как-то обернул небезопасный код. С правильными параметрами компиляции и elf hardening эксплуатировать уязвимость довольно сложно. Еще есть такие вещи, как -fsanitize-*

Все дистрибутивы давно собирают с подобными настройками, плюс -fPIE в CFLAGS и CXXFLAGS, -Wl,-pie в LDFLAGS:

CPPFLAGS="-D_FORTIFY_SOURCE=2"
CFLAGS="-O2 -fstack-protector-strong --param ssp-buffer-size=4 -fstack-clash-protection"
CXXFLAGS="-O2 -fstack-protector-strong --param ssp-buffer-size=4 -fstack-clash-protection"
LDFLAGS="-Wl,-O1 -Wl,-z,now -Wl,-z,relro -Wl,--as-needed -Wl,--no-copy-dt-needed-entries -Wl,--sort-common -Wl,--hash-style=gnu"

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

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

kozlyuk да, но clang дает больше подсказок об ошибкам, больше варнингов и ошибок, где gcc скомпилирует и ничего не заметит. В том случае там была пропущена проверка на null при работе с malloc. Проблема была усублена еще тем, что скомпилированный пакет был отправлен мейнтейнером в продакшен и многие пользователи попадали. Наоборот собирая все clang открыл много багрепортов в разных open source проектах, и они успешно были исправлены. Где-то сам их фиксил где хватало опыта и возможностей.
Я думаю, что ничего clang не оборачивал, просто сыграл рандом — какие объекты как легли в память. Поэтому случайно получилось, что в GCC побились адреса, занятые ценными объектами, на обращении к которым упало, а в clang этот адрес пришелся на неиспользуемую область (это если ошибка записи по неправильному адресу).
VioletGiraffe Может и не оборачивал. Но я пересобирал много раз, и перепроверял, когда уже увижу креш, так и не увидел. Не смотрел, как развернул код clang, так как проект очень тяжелый, и сборка занимает около 100 гигов и собирал без debug информации, для ускорении сборки идущей 2 часа.
Вот как раз в дебажной сборке намного проще отловить баг благодаря всяким memory guards и дебажным функциям стандартной библиотеки (в MSVC это отлично работает, полагаю, что в clang не хуже). Ну а лучше всего — address sanitizer, раз уже всё равно clang используете. Я понимаю, что у вас очень большой проект и с ним сложно экспериментировать, но вывод о том, что clang обнаружил ошибку и активно помогал вашему коду её обойти, почти наверняка неверен.
VioletGiraffe это не мой проект это chromium. И честно говоря, его ненавижу собирать, он очень долго собирается, и стоит поменять настройки сборки, как он перестает собираться, и приходится писать патчи под сам хромиум, и еще кучу сторонних библиотек и программ, которые он тянет. Волей не волей становишься гребанным заложником и разработчиком. Не все баги официальны разработчики охотно решают и оперативно, поэтому в интернете можно найти патчи, которых даже нет в апстриме. У меня ни с одним опенсорс проектом не было такой порнографии, и все баги решались просто…

А можно подробнее что появилось в GCC11? Более точное определение ошибок? Т.к. я достаточно давно видел использование массивов переменной длины, в закладках же тогда появился такой код:

#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>

void fill(const size_t N, double A[N][N])
{
    for (size_t i = 0; i < N; ++i) {
        for (size_t j = 0; j < N; ++j) {
            A[i][j] = 10 * i + j;
        }
    }
}

void print(const size_t N, double A[N][N])
{
    for (size_t i = 0; i < N; ++i) {
        for (size_t j = 0; j < N; ++j) {
            printf("%4.1f ", A[i][j]);
        }
        printf("\n");
    }
}

int main()
{
    for (size_t N = 5; N < 10; ++N) {
        double(*A)[N][N] = calloc(1, sizeof(*A));
        printf("Created matrix with size %d\n", (int)sizeof(*A));
        fill(N, *A);
        print(N, *A);
        free(A);
    }
    return 0;
}

И он нормально компилируется gcc-4.8. Всё это похоже чем-то на Фортран. Но не популярно т.к. не совместимо с С++.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий