На сколько байтов можно превысить выделенный блок без ошибки сегментации?

#c #memory-management #malloc

#c #управление памятью #malloc

Вопрос:

Я узнал, что всякий раз, когда вызывается malloc, фактическая память, предоставляемая программе ОС, не соответствует точно запрошенному размеру, а округляется до размера страницы. Из того, что я знаю, размер страницы составляет 1024 или 4096.

Исходя из этой логики (и поправьте меня, если это неправильно), запись за пределы моего выделенного блока не всегда будет вызывать ошибку сегментации, поскольку эта ошибка задается ядром (которое предоставило мне полную страницу и не заботится о том, что я делаю, пока я остаюсь внутри нее).

Странно то, что в приведенной ниже программе я запросил 8 байт у malloc, а затем записал 80000 (sizeof (size_t) * 10000) байт дальше, не получив ошибки сегментации. Однако я получил недопустимые ошибки чтения и записи valgrind.

Может кто-нибудь пролить свет на эту тему?

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

int main()

{

    size_t *ptr = (size_t *)malloc(sizeof(size_t));
    size_t forward = 10000;

    ptr  = forward; 
    *ptr = 8;
    printf("%lun", *ptr);

    ptr -= forward;
    free(ptr);

    return (0);
}
 

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

1. Нет никакого способа сказать наверняка.

2. Не получить segfault и иметь действительную программу на языке Си — это две разные вещи. У вас есть программа с недопустимой обработкой памяти, и Valgrind говорит вам об этом. У вас есть неопределенное поведение, на которое, как и на любой другой тип неопределенного поведения, нельзя полагаться.

3. @user253751: Есть способы определить.

4. @EricPostpischil Ненадежно.

5. @user253751: glibc предоставляет malloc_usable_size , и я считаю, что Windows предоставляет нечто подобное.

Ответ №1:

Вы путаете две совершенно разные вещи. Если вы запрашиваете malloc 1 байт, он может выделить страницу размером 4096 байт из операционной системы, но он может зарезервировать только 16 байт этого блока для этого вызова malloc . Следующий вызов malloc может получить еще 16 байт с той же страницы размером 4096 байт.

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

Ответ №2:

Вы предполагаете, что malloc для вашего выделения была запрошена только одна страница из ОС, а последующие страницы остались не сопоставленными. Это не обязательно верно. malloc обычно запрашивает много страниц за раз, а затем использует их для последующего выделения, если это возможно, делая меньше системных вызовов и уменьшая накладные расходы.

Таким образом, вполне вероятно, что несколько страниц, следующих за вашим выделением, также отображаются для вашего процесса, поэтому вы не получаете segfault. Вместо этого вы перезаписали память, которая может использоваться для чего-то важного.

Действительно, возможно, что malloc перед запуском был запрошен больший фрагмент main для внутренних структур библиотеки C, и что ваше выделение просто помещается в этот фрагмент. В моем тесте, используемом strace для просмотра brk() системного вызова, этот фрагмент имел размер 33 страницы (132 КБ), и ваш блок находится на первой странице этого фрагмента. Таким образом, ваш переполненный 80 КБ все еще находится в пределах этой отображенной области.

Итак, ответ на вопрос заголовка таков: «это зависит от того, где находится ваш блок на его странице, и какие окружающие страницы отображаются». Это, в свою очередь, зависит от точного используемого алгоритма malloc и схемы других распределений и освобождений, выполняемых вашим кодом или кодом библиотеки, до этого момента, ни один из которых вы не можете предсказать. В принципе возможно выяснить, какие страницы сопоставлены (например, в Linux путем синтаксического /proc/self/maps анализа), но это может непредсказуемо измениться, поскольку память выделяется и освобождается вашим кодом или библиотечными функциями. Так что практический ответ таков: «вы не знаете».

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

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

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