diff --git a/lib/mem.h b/lib/mem.h index e19284c8..58e6113a 100644 --- a/lib/mem.h +++ b/lib/mem.h @@ -2,7 +2,10 @@ * mem — выделение памяти с защитными полями, местом аллокации и учётом живых блоков. * Использовать u_malloc/u_calloc/u_realloc/u_strdup и парный u_free; обычный free к этим блокам неприменим. * Макросы передают LOCATION автоматически. OOM возвращает NULL; обнаруженное повреждение завершает процесс. - * realloc при ошибке сохраняет старый блок, при size=0 освобождает его. + * realloc живого блока при ошибке сохраняет его, при size=0 освобождает; NULL обрабатывается как malloc. + * Размеры аргументов _impl — uint32_t; до вызова проверить, что исходные size_t помещаются в этот диапазон. + * Список аллокаций защищён мьютексом; доступ к одному блоку и чтение счётчика согласует вызывающий. + * Защитные поля проверяются для живых блоков; обращение к уже освобождённому/чужому указателю недопустимо. * Отчёт u_report_unfreed_blocks вызывать после остановки потоков, меняющих список аллокаций. */ #ifndef MEM_H @@ -15,18 +18,30 @@ extern "C" { #define XSTR(s) STR(s) #define STR(s) #s #define LOCATION __FILE__ ":" XSTR(__LINE__) +// Проверяет canary/padding живого блока; text/location — строки диагностики. NULL ptr допустим. +// Обнаруженное повреждение логируется в SYS и завершает процесс через exit(EXIT_FAILURE). void u_check(void* ptr, const char* text, const char* location); #define u_malloc(size) u_malloc_impl(size, LOCATION) #define u_calloc(nmemb, size) u_calloc_impl(nmemb, size, LOCATION) #define u_realloc(ptr, size) u_realloc_impl(ptr, size, LOCATION) #define u_free(ptr) u_free_impl(ptr, LOCATION) #define u_strdup(s) u_strdup_impl(s, LOCATION) +// Выделяет size байт с метаданными; NULL при OOM/переполнении полного размера. size=0 тоже создаёт учтённый блок. +// location — обязательная строка места вызова, копируется в метаданные; payload не обнуляется. void* u_malloc_impl(uint32_t size, const char* location); +// Выделяет и обнуляет nmemb*size байт; NULL при произведении > UINT32_MAX или ошибке malloc. void* u_calloc_impl(uint32_t nmemb, uint32_t size, const char* location); +// NULL ptr → malloc(size), включая size=0. Для живого ptr: size=0 → free и NULL; +// иначе новый блок с min(старый размер, size) байтами данных. При OOM возвращает NULL, старый блок сохранён. +// Результат сохранять временно: tmp = u_realloc(ptr, size); if (tmp) ptr = tmp; (при size > 0). void* u_realloc_impl(void* ptr, uint32_t size, const char* location); +// Проверяет и освобождает живой блок из этих обёрток; NULL допустим. Обычный free к нему неприменим. void u_free_impl(void* ptr, const char* location); +// Копирует ASCIIZ-строку (strlen(s)+1 должно помещаться в uint32_t); NULL при NULL s/OOM. Освобождать u_free. char* u_strdup_impl(const char* s, const char* location); +// После остановки изменяющих список потоков: статистика и дампы живых блоков; сами блоки не освобождает. void u_report_unfreed_blocks(void); +// Число живых блоков, чтение без блокировки; параллельные аллокации/освобождения нужно исключить извне. size_t u_get_allocated_count(void); #ifdef __cplusplus } diff --git a/lib/mem_doc.md b/lib/mem_doc.md deleted file mode 100644 index 4ec24b87..00000000 --- a/lib/mem_doc.md +++ /dev/null @@ -1,48 +0,0 @@ -# mem (Memory Wrappers с отслеживанием утечек) - -## 1. Назначение - -Обёртки над стандартными `malloc`/`calloc`/`realloc`/`free`/`strdup` с детекцией: -- **Buffer overflow/underflow** — каждый блок окружён канарейками (`0xDEADBEEF`) и padding-областями, которые проверяются при `u_free` и `u_realloc`. При повреждении — подробный дамп блока и аварийное завершение. -- **Double free** — двусвязный список всех живых аллокаций; повторное освобождение детектируется по отсутствию блока в списке. -- **Утечки памяти** — `u_report_unfreed_blocks()` при завершении выводит все незакрытые блоки с местом аллокации (`файл:строка`), размером и hex-дампом данных. - -Функции потокобезопасны (мьютекс/критическая секция). - -## 2. Как пользоваться - -```c -#include "mem.h" - -// Вместо malloc/calloc/realloc/free/strdup используй соответствующие макросы: -void* buf = u_malloc(data_size); -char* str = u_strdup("hello"); -buf = u_realloc(buf, new_size); -u_free(buf); -u_free(str); - -// В конце main (перед return) — отчёт о незакрытых блоках: -u_report_unfreed_blocks(); - -// Текущее число живых аллокаций (для диагностики): -size_t count = u_get_allocated_count(); -``` - -**Важно:** -- Макросы (`u_malloc`, `u_calloc`, `u_free`, и т.д.) автоматически подставляют `__FILE__:__LINE__` через `LOCATION` — это нужно для диагностики утечек и повреждений. Напрямую вызывай `_impl`-функции только если нужно явно передать location. -- `u_realloc(NULL, size)` эквивалентен `u_malloc(size)`, `u_realloc(ptr, 0)` эквивалентен `u_free(ptr)`. -- Потокобезопасность гарантирована, но аллокации с метаданными заметно дороже обычного `malloc` — используй `memory_pool` для hot-path объектов. -- При обнаружении повреждения (overflow/underflow/double-free) программа завершается через `exit(EXIT_FAILURE)` после вывода отладочной информации. - -## 3. API - -| Функция/макрос | Описание | -|---|---| -| `u_malloc(size)` | Аналог `malloc` с boundary check и трекингом | -| `u_calloc(nmemb, size)` | Аналог `calloc` (выделяет + обнуляет) | -| `u_realloc(ptr, size)` | Аналог `realloc`, с проверкой целостности старого блока | -| `u_free(ptr)` | Освобождение с проверкой canary, double-free и удалением из списка | -| `u_strdup(s)` | Аналог `strdup` через u_malloc | -| `u_report_unfreed_blocks()` | Выводит все незакрытые блоки с дампом; вызывается при shutdown | -| `u_get_allocated_count()` | Возвращает количество живых аллокаций | -| `u_check(ptr, text, location)` | Ручная проверка целостности блока (canary + padding) |