Подтвердить что ты не робот

Содержит ли malloc() смежный блок памяти?

У меня есть код, написанный очень старым школьным программистом:-). это похоже на это

typedef struct ts_request
{ 
  ts_request_buffer_header_def header; 
  char                         package[1]; 
} ts_request_def; 

ts_request_def* request_buffer = 
malloc(sizeof(ts_request_def) + (2 * 1024 * 1024));

программист в основном работает над концепцией переполнения буфера. Я знаю, что код выглядит изворотливым. поэтому мои вопросы:

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

  • Выполняя бесплатный (request_buffer), он освободит все байты, выделенные malloc i.e sizeof (ts_request_def) + (2 * 1024 * 1024), или только байты размера структуры sizeof (ts_request_def)

  • Вы видите какие-либо очевидные проблемы с этим подходом, мне нужно обсудить это с моим боссом и хотелось бы указать на любые лазейки с этим подходом.

4b9b3361

Ответ 1

Чтобы ответить на ваши пронумерованные точки.

  • Да.
  • Все байты. Malloc/free не знает и не заботится о типе объекта, просто размер.
  • Это, строго говоря, поведение undefined, но общий трюк, поддерживаемый многими реализациями. Ниже приведены другие альтернативы.

Последний стандарт C, ISO/IEC 9899: 1999 (неофициально C99), позволяет гибкие элементы массива.

Примером этого может быть:

int main(void)
{       
    struct { size_t x; char a[]; } *p;
    p = malloc(sizeof *p + 100);
    if (p)
    {
        /* You can now access up to p->a[99] safely */
    }
}

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

Кроме того, gcc позволяет массивы нулевой длины как расширение. Матрицы с нулевой длиной являются незаконными в стандартном C, но gcc представила эту функцию до того, как C99 предоставил нам гибкие элементы массива.

В ответе на комментарий, я объясню, почему сниппет ниже технически undefined. Номера разделов, которые я цитирую, относятся к C99 (ISO/IEC 9899: 1999)

struct {
    char arr[1];
} *x;
x = malloc(sizeof *x + 1024);
x->arr[23] = 42;

Во-первых, 6.5.2.1 # 2 показывает, что [i] идентична (* ((a) + (i))), поэтому x- > arr [23] эквивалентно (* ((x- > arr ) + (23))). Теперь, 6.5.6 # 8 (при добавлении указателя и целого) говорит:

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

По этой причине, поскольку x- > arr [23] не входит в массив, поведение undefined. Вы все еще можете подумать, что все в порядке, потому что malloc() подразумевает, что массив теперь расширен, но это не так. Информационное приложение J.2 (в котором перечислены примеры поведения undefined), дает дополнительное разъяснение с помощью примера:

Индекс массива выходит за пределы диапазона, даже если объект, по-видимому, доступен с помощью (как в выражении lvalue a [1] [7], учитывая объявление int a [4] [5]) (6.5.6).

Ответ 2

3 - Это довольно распространенный трюк для выделения динамического массива в конце структуры. Альтернативой было бы поместить указатель в структуру, а затем распределить массив по отдельности и не забывать также ее освобождать. То, что размер привязан к 2mb, кажется немного необычным, хотя.

Ответ 3

1) Да, да, или malloc завершится неудачно, если не будет достаточно большого достаточно непрерывного блока. (Ошибка с malloc вернет указатель NULL)

2) Да, будет. Распределение внутренней памяти будет отслеживать объем памяти, выделенный этим значением указателя, и освободить все его.

3) Это немного языковой взлом, и немного сомнительно об этом. Он по-прежнему подвержен переполнениям буфера, просто может нанять злоумышленников немного дольше, чтобы найти полезную нагрузку, которая вызовет его. Стоимость "защиты" также довольно внушительна (вам действительно нужно > 2 мб на буфер запросов?). Это также очень уродливо, хотя ваш босс не может оценить этот аргумент:)

Ответ 4

Это стандартный трюк C и не опасен для любого другого буфера.

Если вы пытаетесь показать своему боссу, что вы умнее "очень старого школьного программиста", этот код не относится к вам. Старая школа не обязательно плохая. Кажется, парень "старой школы" знает достаточно об управлении памятью;)

Ответ 5

Я не думаю, что существующие ответы вполне соответствуют сути этого вопроса. Вы говорите, что программист старой школы делает что-то подобное:

typedef struct ts_request
{ 
  ts_request_buffer_header_def header; 
  char                         package[1]; 
} ts_request_def;

ts_request_buffer_def* request_buffer = 
malloc(sizeof(ts_request_def) + (2 * 1024 * 1024));

Я думаю, что вряд ли он это сделает, потому что если бы это то, что он хотел сделать, он мог бы сделать это с помощью упрощенного эквивалентного кода, который не нуждается в каких-либо трюках;

typedef struct ts_request
{ 
  ts_request_buffer_header_def header; 
  char                         package[2*1024*1024 + 1]; 
} ts_request_def;

ts_request_buffer_def* request_buffer = 
malloc(sizeof(ts_request_def));

Готов поспорить, что то, что он действительно делает, это что-то вроде этого:

typedef struct ts_request
{ 
  ts_request_buffer_header_def header; 
  char                         package[1]; // effectively package[x]
} ts_request_def;

ts_request_buffer_def* request_buffer = 
malloc( sizeof(ts_request_def) + x );

То, что он хочет достичь, - это распределение запроса с переменным размером пакета x. Конечно, незаконно объявлять размер массива переменной, поэтому он обходит это с помощью трюка. Похоже, он знает, что он делает со мной, трюк хорошо подходит к почтенному и практическому концу шкалы обмана C.

Ответ 6

Что касается № 3, без лишнего кода трудно ответить. Я не вижу в этом ничего плохого, если только это не происходит. Я имею в виду, что вы не хотите выделять 2 Мб фрагментов памяти все время. Вы также не хотите делать это без необходимости, например. если вы используете только 2k.

Тот факт, что вам это не нравится по какой-то причине, недостаточно для того, чтобы возражать против него, или оправдывать его полностью переписывание. Я бы внимательно посмотрел на использование, попытался понять, что думал первоначальный программист, внимательно посмотрите на переполнение буфера (как указывал workmad3) в коде, который использует эту память.

Есть много распространенных ошибок, которые вы можете найти. Например, проверяет ли код, чтобы убедиться, что malloc() преуспел?

Ответ 7

Эксплойт (вопрос 3) действительно зависит от интерфейса к вашей структуре. В контексте это распределение может иметь смысл, и без дополнительной информации невозможно сказать, защищено ли оно или нет.
Но если вы имеете в виду проблемы с распределением памяти, большей, чем структура, это отнюдь не плохой дизайн C (я бы даже не сказал, что эта старая школа...;))
Просто последнее замечание здесь - точка с наличием char [1] заключается в том, что конечный NULL всегда будет в объявленной структуре, то есть в буфере могут быть 2 * 1024 * 1024 символа, и у вас нет для учета NULL с помощью "+1". Может показаться маленьким подвигом, но я просто хотел указать.

Ответ 8

Я часто видел и использовал этот шаблон.

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

Доступ к элементам массива также немного более эффективен, но это все меньше и меньше для современных компьютеров.

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

Единственная потенциальная проблема, которую я вижу, заключается в том, что компилятор переставляет порядок хранения переменных-членов, поскольку этот трюк требует, чтобы поле пакета оставалось последним в хранилище. Я не знаю, запрещает ли C-подстановку замену.

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

Ответ 9

Да. malloc возвращает только один указатель - как он мог бы сообщить запрашивающему, что он выделил несколько несмежных блоков для удовлетворения запроса?

Ответ 10

Хотелось бы добавить, что это не так часто, но я мог бы также назвать его стандартной практикой, потому что Windows API полон такого использования.

Проверьте, например, общую структуру заголовка BITMAP.

http://msdn.microsoft.com/en-us/library/aa921550.aspx

Последний квадрат RBG представляет собой массив размером 1, который зависит именно от этой техники.

Ответ 11

В ответ на ваш третий вопрос.

free всегда освобождает всю память, выделенную одним выстрелом.

int* i = (int*) malloc(1024*2);

free(i+1024); // gives error because the pointer 'i' is offset

free(i); // releases all the 2KB memory

Ответ 13

Ответ на вопрос 1 и 2: Да

Об уродстве (т.е. вопросе 3), что программист пытается сделать с этой выделенной памятью?

Ответ 14

вещь, чтобы понять здесь, состоит в том, что malloc не видит вычислений, сделанных в этом

malloc(sizeof(ts_request_def) + (2 * 1024 * 1024));

Это то же самое, что и

  int sz = sizeof(ts_request_def) + (2 * 1024 * 1024);
   malloc(sz);

Вы можете подумать, что он выделяет 2 куска памяти, а в разуме они являются "структурой", "некоторыми буферами". Но malloc вообще этого не видит.