Невозможно отучить людей изучать самые ненужные предметы.
Введение в CSS
Преимущества стилей
Добавления стилей
Типы носителей
Базовый синтаксис
Значения стилевых свойств
Селекторы тегов
Классы
CSS3
Надо знать обо всем понемножку, но все о немногом.
Идентификаторы
Контекстные селекторы
Соседние селекторы
Дочерние селекторы
Селекторы атрибутов
Универсальный селектор
Псевдоклассы
Псевдоэлементы
Кто умеет, тот делает. Кто не умеет, тот учит. Кто не умеет учить - становится деканом. (Т. Мартин)
Группирование
Наследование
Каскадирование
Валидация
Идентификаторы и классы
Написание эффективного кода
Вёрстка
Изображения
Текст
Цвет
Линии и рамки
Углы
Списки
Ссылки
Дизайны сайтов
Формы
Таблицы
CSS3
HTML5
Блог для вебмастеров
Новости мира Интернет
Сайтостроение
Ремонт и советы
Все новости
Справочник от А до Я
HTML, CSS, JavaScript
Афоризмы о учёбе
Статьи об афоризмах
Все Афоризмы
Помогли мы вам |
Практиковаться мы будем на реализации кучи ptmalloc2, которая сейчас используется по умолчанию в glibc, поэтому нам понадобятся машина с Linux и необходимый софт. Установим отладчик GDB, GCC (можно сразу поставить весь пакет build-essential) и отладочную версию библиотеки libc, позволяющую видеть подробную информацию о куче. Также поставим pwngdb и его зависимость — peda, чтобы получить удобные команды vmmap
, hexdump
, heapinfo
.
sudo apt install gdb build-essential libc6-dbg
git clone https://github.com/scwuaptx/Pwngdb.git ~/Pwngdb
cp~/Pwngdb/.gdbinit ~/
git clone https://github.com/longld/peda.git ~/peda
Для изучения работы наших тестовых программ понадобится знание базовых команд GDB:
r[un]
— запустить файл;b[reak] *0x1234
— поставить точку останова на адресе 0x1234
;b[reak] 123
— поставить точку останова на строке 123
текущего исходного файла;b[reak] basic.c:123
— поставить точку останова на строке 123
исходного файла basic.c
;c[ontinue]
— продолжить выполнение;s[tep]
— выполнить одну ассемблерную инструкцию;n[ext]
— выполнить одну строчку исходного файла;x/10xg 0x1234
— распечатать десять 8-байтных слов по адресу 0x1234
;p[rint] a
— распечатать значение переменной a
;p[rint] *((mchunkptr)0x555555756680)
— взять содержимое памяти по адресу 0x555555756680
как тип mchunkptr
, задереференсить его и распечатать;where
— показать, на какой строчке исходного кода находится выполнение программы.Команды peda и pwngdb:
vmmap
— вывести карту памяти;hexdump
— показать содержимое памяти по адресу в виде hexdump
;heapinfo
— посмотреть информацию о куче.Когда программа запрашивает буфер для данных (например, размером в 10 байт) с помощью malloc
, на самом деле выделяется больше памяти, так как для хранения метаданных необходимо дополнительное пространство. Такой кусок памяти, содержащий метаданные, называют чанком (chunk).
Структура чанка, используемая в ptmalloc2, приведена ниже. Из нее можно понять, что перед указателем на выделенный буфер памяти, который возвращается пользователю (mem
), располагаются еще два поля: размер чанка и размер предыдущего чанка.
chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of previous chunk,if unallocated (P clear) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of chunk,in bytes |A|M|P|
mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| User data starts here... .
..
.(malloc_usable_size()bytes).
. |
nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| (size of chunk,but used for application data) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of next chunk,in bytes |A|0|1|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Сам чанк имеет такую структуру:
struct malloc_chunk {
INTERNAL_SIZE_Tmchunk_prev_size; /* Size of previous chunk, if it is free. */
INTERNAL_SIZE_Tmchunk_size;/* Size in bytes, including overhead. */
struct malloc_chunk* fd;/* double links — used only if this chunk is free. */
struct malloc_chunk* bk;
/* Only used for large blocks: pointer to next larger size. */
struct malloc_chunk* fd_nextsize; /* double links — used only if this chunk is free. */
struct malloc_chunk* bk_nextsize;
};
typedef struct malloc_chunk* mchunkptr;
Чтобы получить из указателя на чанк (служебную структуру) указатель на буфер памяти, который можно использовать, к первому прибавляют значение 2*SIZE_SZ
. SIZE_SZ
. Для архитектуры x64 оно равно 8, а для x86 — 4. То есть на x64 user_mem
. И наоборот, чтобы из указателя, который вернул malloc
, получить указатель на чанк, необходимо вычесть 2*SIZE_SZ
из него. За это отвечают следующие макросы:
#define chunk2mem(p) ((void*)((char*)(p) + 2*SIZE_SZ))
#define mem2chunk(mem) ((mchunkptr)((char*)(mem) - 2*SIZE_SZ))
Важный момент: поле mchunk_prev_size
в следующем чанке используется для хранения пользовательских данных предыдущего чанка.
Старые менеджеры кучи использовали одну кучу на весь процесс и синхронизировали доступ к ней разных потоков с помощью мьютексов. Как несложно догадаться, положительно на производительности это не сказывалось. Ptmalloc2 использует арены — области памяти для того, чтобы каждый поток мог там хранить свою кучу.
Но поскольку на практике потоков в приложении может быть слишком много, максимальное количество создаваемых арен вычисляется по следующей формуле (n
— количество процессоров):
#define NARENAS_FROM_NCORES(n) ((n) * (**sizeof** (**long**) == 4 ? 2 : 8))
Пока количество арен меньше максимального, менеджер кучи создает новую арену на каждый новый поток. После этого, увы, нескольким потокам придется делить между собой одну арену.
Первая созданная менеджером кучи арена называется основной (main). Однопоточное приложение использует только основную арену.
Остановимся подробнее на флагах чанка. Поле размера предыдущего чанка (mchunk_size
), кроме собственно размера, хранит три флага: A
, M
, P
. Это возможно за счет выравнивания размера чанка. Так как размер чанка всегда кратен либо 8, либо 16 байтам, последние 3 бита размера не несут смысловой нагрузки, и их можно использовать для хранения флагов.
A
(NON_MAIN_ARENA): 0 — чанк был выделен из основной арены и основной кучи; 1 — чанк принадлежит одной из второстепенных арен. Когда приложение создает дополнительные потоки, каждый из них получает свою арену (грубо говоря, свою кучу). В чанках, выделяемых на этих аренах, установлен бит A
;M
(IS_MMAPPED): 1 — чанк получен с помощью вызова mmap
. Остальные флаги игнорируются, потому что данные чанки не располагаются в арене и к ним не примыкают другие чанки;P
(PREV_INUSE): 0 — предыдущий чанк не используется. Перед полем mchunk_size
располагается значение размера предыдущего чанка; 1 — предыдущий чанк используется. Перед полем mchunk_size
располагаются пользовательские данные.Для повышения быстродействия чанки используют повторно (и именно эту особенность учитывают при эксплуатации кучи). Ранее использованные и освобожденные чанки складывают в бины (bins). В нашей реализации кучи существует пять типов бинов:
fd_nextsize
и bk_nextsize
.
|
|