Пользовательский malloc () дизайн заголовка реализации

Если Ваши заголовочные файлы обычно запускаются с

#ifndef __SOMEHEADER_H__
#define __SOMEHEADER_H__
// header contents
#endif

(в противоположность использованию #pragma однажды), Вы могли изменить это на:

#ifndef __SOMEHEADER_H__
#define __SOMEHEADER_H__
// header contents
#else 
#pragma message("Someheader.h superfluously included")
#endif

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

6
задан Roman Nikitchenko 18 February 2015 в 01:14
поделиться

7 ответов

Для отладки malloc рассмотрите возможность размещения пробела между структурой управления и началом пользовательских данных, а также между концом пользовательских данных и контрольной суммой. Один байт заполнения должен быть нулевым байтом 0x00 - поэтому строковые операции прекращаются; рассмотрите возможность добавления другого как 0xFF. Если у вас есть фиксированный шаблон и вы заметили, что он изменился, вы знаете, что что-то вышло за пределы, но есть большая вероятность, что ваши конфиденциальные данные управления не были растоптаны. Если вы используете заполнение по 16 байтов по обе стороны от пространства, выделенного пользователю, вы можете пойти дальше и поставить 4 байта нулей, выровненных соответствующим образом (отсюда нулевое 4-байтовое целое число) и, возможно, 0xFFFFFFFF для -1. Кроме того, поскольку вы, вероятно, округлите запрошенный размер до кратного размера вашего базового блока, установите для байтов, которые не могут использоваться пользователем, известное значение - и убедитесь, что они остаются неизменными. Это позволит обнаружить модификации «на единицу сверх выделенной длины» или всего на несколько байтов сверх выделенной длины, которые в противном случае могут остаться незамеченными.

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

Другой вариант, который следует рассмотреть, - это попытка полностью отделить ваши управляющие данные от памяти, возвращаемой пользователю. Конечно, полное разделение невозможно, но хотя бы поддерживайте список выделений (с размерами и указателями) отдельно от выделенных блоков. Еще раз, это дает вам защиту, убирая ваши драгоценные данные управления подальше от неконтролируемых операций по перегрузке памяти. Вы не полностью защищены от ошибочных указателей, но лучше защищены. (И вы все равно можете предоставить буферные зоны вокруг выделенного пространства для обнаружения неконтролируемой записи.) Однако этот дизайн заметно отличается от вопроса.


Предполагая, что вы получаете блок памяти из 'malloc ()', тогда вы бы сделали - примерно:

void *my_malloc(size_t nbytes)
{
    size_t reqblocks = (nbytes + sizeof(header) - 1) / sizeof(header);
    size_t reqspace  = (reqblocks + 2) * sizeof(header) + 2 * sizeof(padding);
    void *space = malloc(reqspace);
    if (space == 0)
        return space;
    void *retval = (char *)space + sizeof(header) + sizeof(padding);
    header *head = space;
    head->next = ...next...;
    head->size = nbytes;
    ...set head padding to chosen value...
    ...set tail padding to chosen value...
    ...set gap between nbytes and block boundary to chosen value...
    return retval;
}

Осталось сделать некоторую интерпретацию ...

3
ответ дан 10 December 2019 в 00:40
поделиться

Я бы сделал что-нибудь вроде

#define MEM_ALIGN 4 // 8 on 64b eventually

struct header {
    union aligned_header {
        struct _s {
            union aligned_header* next;
            size_t size;
        } data;
        char dummy_align_var[sizeof(struct _s) + sizeof(struct _s)%MEM_ALIGN];
    } header_data;
    char user_address;
};

и вернул бы & user_address .

2
ответ дан 10 December 2019 в 00:40
поделиться

Почему вы используете профсоюз? Просто используйте struct и верните пользователю & dummy_align_var в качестве начала свободного блока.

О, и поскольку это для отладки, я предлагаю вам добавить mungwall: Поместите по 16 байт по обе стороны от пользовательской области и заполните их каким-либо шаблоном (например, 0xdeadbeef, повторяется четыре раза). Во время free () проверьте, не изменились ли эти байты.

[EDIT] Вот какой-то псевдокод:

struct header {
    struct header * next;
    unsigned size;
    // put mungwall here
    double user_data;
};

init()
    int blockSize = 1024;
    char * bigMem = original_malloc(blockSize);
    struct header * first = (struct header *)bigMem;
    first->next = NULL;
    first->size = blockSize - (sizeof(struct header) - sizeof(double));
2
ответ дан 10 December 2019 в 00:40
поделиться

Вы также можете объявить dummy_align_var как заголовок объединения * prev , чтобы вы могли поместить свободные блоки памяти в двусвязный список.

Это очень помогает с точки зрения производительности, когда вы хотите объединить освобожденный блок с предыдущим и следующим черными, если они тоже свободны.

Наконец, вы не упоминаете об этом, сортировка списка по размеру позволяет быстрее найти лучший блок для выделения для данного запроса, а сортировка по адресу упрощает объединение освобожденных блоков. Если вы хотите сделать и то, и другое, сделайте пользовательскую часть размером не менее 3 header * , она будет соответствовать указателям, необходимым при освобождении блока.

В дополнение к границам, упомянутым Аароном, перезапишите освобожденные буферы с помощью тот же образец. Это упрощает распознавание кода, который использует уже освобожденные буферы.

1
ответ дан 10 December 2019 в 00:40
поделиться

Предлагаю было бы полезно: Несколько лет назад мне нужно было сделать резервную копию средства malloc () для целей отладки (трассировщик распределения и так далее) ... И было довольно легко взять реализацию FreeBSD из их libstdc. Насколько я помню, это были поздние выпуски FreeBSSD 5.0 ​​или даже 4.x, но забавно то, что их возможности были изолированы в простом библиотечном модуле malloc.o, поэтому перегрузка этого слоя была очень простой копией и вставкой, а реализация была действительно хорошей.

Вы действительно делаете всю эту работу? Да, это просто проверка, я не претендую на лучшее решение.

1
ответ дан 10 December 2019 в 00:40
поделиться

Если вы не хотите использовать malloc (), вам следует взглянуть на sbrk

0
ответ дан 10 December 2019 в 00:40
поделиться

Вы можете использовать исходное объединение, если хотите, например:

union header *hdr = malloc(total_size);
void *user_ptr = hdr + 1;
char *trailer_ptr = (char *)user_ptr + user_size;

Это установит user_ptr в то место, где будет начинаться следующий заголовок объединения , если блок malloc ed обрабатывался как массив этих объединений. Это значение, которое вы возвращаете пользователю.

Он также устанавливает трейлер_ptr так, чтобы он указывал на первый байт после выделения пользователем, куда вы можете поместить свою контрольную сумму.

1
ответ дан 10 December 2019 в 00:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: