Проверьте, находится ли адрес памяти в пределах стека текущего процесса.

Я добавляю функцию в свой отладчик (я использую Ptrace для управления отслеживаемым процессом, а также кодами libbfd/libopcodes), чтобы раскрутить стек и определить, существуют ли несоответствия между выделенным пространством стека каждого CALL и статически полученным размером локальной переменной, печать адреса и размера локального стека каждого кадра по пути.

Моя общая методология заключается в том, чтобы взять адрес в базовом указателе (EBP/RBP), увеличить указатель до значения, которое должно содержать сохраненный указатель кадра, разыменовать этот адрес, проверить его с помощью PTRACE_PEEKDATA и повторять до тех пор, пока я не разыменую адрес, занимающий область за пределами куча.

Я знаю, как проверять регистры сегментов кода/данных, но в идеале мне нужен метод, чтобы проверить, нахожусь ли я все еще внутри стека вызовов, даже если сегментация была изменена страницами памяти W ^ X или другим неисполняемым стеком.

Короче говоря, как я могу проверить (в общем случае), когда я вышел за пределы стека, не сработав при этом с общей ошибкой защиты?

(Кроме того, я понимаю, что действую исходя из предположения, что проверка сегмента страницы адреса является идеальной методологией здесь - возможно, существует другой более простой метод, чтобы определить, находится ли адрес в пространстве стека текущего процесса)


person ŹV -    schedule 04.04.2013    source источник
comment
Да, но в идеальном мире было бы портативное решение, которое не зависит от какой-либо информации из /proc.   -  person ŹV -    schedule 05.04.2013
comment
Стандарт C не предлагает каких-либо переносимых способов доступа к информации или функциям, специфичным для ОС или аппаратного обеспечения (за исключением, возможно, system()). Если ваша программа на C зависит от указанной информации/функциональности, она обречена на плохую переносимость. Вы можете несколько улучшить переносимость, если будете придерживаться функциональности POSIX, но это улучшение будет только улучшением для ОС, совместимых с POSIX. Для всех остальных это будет противоположностью улучшения.   -  person Alexey Frunze    schedule 05.04.2013
comment
Быть по сему. Какие-нибудь библиотеки абстрагируют это?   -  person ŹV -    schedule 05.04.2013
comment
Навскидку, не знаю. См. API и инструменты POSIX.   -  person Alexey Frunze    schedule 05.04.2013
comment
Вы не против превратить это в ответ?   -  person ŹV -    schedule 05.04.2013
comment
Если вы считаете, что это полезно, я могу превратить это в ответ. Я не был уверен, что вам нужно портативное решение, поскольку это, похоже, не указано в вопросе.   -  person Alexey Frunze    schedule 05.04.2013
comment
Переносимость является задним числом, решения для Linux здесь абсолютно достаточно.   -  person ŹV -    schedule 05.04.2013


Ответы (2)


Стандарт C не предлагает каких-либо переносимых способов доступа к информации или функциям, специфичным для ОС или аппаратного обеспечения (за исключением, возможно, system()). Если ваша программа на C зависит от указанной информации/функциональности, она обречена на плохую переносимость.

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

person Alexey Frunze    schedule 05.04.2013

Я посмотрел код команды pstack, которая печатает содержимое стека запущенный процесс. Этот код получает базовый адрес из структуры link_map и сохраняет его в поле l_addr. Это поле задается внутри функции readLinkMap():

static void readLinkMap(int pid, ElfN_Addr base, struct link_map *lm, 
                        char *name, unsigned int namelen)
{
  /* base address */
  lm->l_addr = (ElfN_Addr) ptrace(PTRACE_PEEKDATA, pid,
                                  base + offsetof(struct link_map,l_addr), 0);
  /* next element of link map chain */
  if (-1 != (long) lm->l_addr || !errno)
    lm->l_next = (struct link_map *) ptrace(PTRACE_PEEKDATA, pid,
                                            base + offsetof(struct link_map, l_next), 0);
  if ((-1 == (long) lm->l_addr || -1 == (long) lm->l_next) && errno) {
    perror("ptrace");
    quit("can't read target.");
  }

  loadString(pid, base + offsetof(struct link_map, l_name), name, namelen);
}
person ŹV -    schedule 05.04.2013