Consolidate memory ownership and allocation documentation in header

This commit is contained in:
evgeny committed 2026-10-21 17:53:00 +02:00
1 parent 3944459c29
commit e403a2f689
2 files changed
+16 -49

No files matched your search

+16 -1
View File
@@ -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
}
-48
View File
@@ -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) |