Чтобы добавить к вашему исследованию:
Если программирование на платформе linux, поддерживающей вызов mprotect
, и при работе только с динамическим распределением c, вы можете перераспределить память как минимум более чем на 1 page и mprotect
память после памяти, которую вы возвращаете как динамически выделенная.
Например:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <stdint.h>
#include <stdalign.h>
#include <sys/mman.h>
#include <stddef.h>
#include <signal.h>
#if PROTECT_ME
void *malloc2(size_t size) {
const size_t maxalign = _Alignof(max_align_t);
assert(size < getpagesize() - sizeof(size_t) - maxalign); // roughly, could be TODO
// allocate memory
char * const pnt = mmap(NULL, getpagesize() + 1,
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
assert(pnt != (void*)-1);
assert((uintptr_t)pnt % getpagesize() == 0);
// calculate a pointer right after the returned pointer that has
// to be aligned
char * const end = pnt + getpagesize();
char * const beg = end - size - (size % maxalign);
// I store size in the memory before the data
memcpy(beg - sizeof(size), &size, sizeof(size));
// protect the data behind the pointer
assert((uintptr_t)end % getpagesize() == 0);
const int err = mprotect(end, 1, 0);
assert(err == 0);
return beg;
}
void free2(void *beg) {
// extract size for memory preceding the data
size_t size;
memcpy(&size, beg - sizeof(size), sizeof(size));
// calculate mmap returned pointer
const size_t maxalign = _Alignof(max_align_t);
char * const end = beg + size + (size % maxalign);
char * const pnt = end - getpagesize();
assert((uintptr_t)pnt % getpagesize() == 0);
int err = munmap(pnt, getpagesize() * 2);
assert(err == 0);
}
void signal_segv(int a) {
fprintf(stderr, "The process had written to a bad place\n");
abort();
}
__attribute__((__constructor__))
void _init_me(void) {
signal(SIGSEGV, signal_segv);
}
#define malloc malloc2
#define free free2
#endif
int main() {
int *a = malloc(5 * sizeof(*a));
for (int i = 0; i < 10; ++i) {
a[i] = i; // will write out-of-bounds for a array when i == 5
fprintf(stderr, "a[%d]=%d\n", i, a[i]);
}
free(a);
}
Компиляция с gcc
и работа без защиты:
$ gcc 1.c && ./a.out
a[0]=0
a[1]=1
a[2]=2
a[3]=3
a[4]=4
a[5]=5
a[6]=6
a[7]=7
a[8]=8
a[9]=9
Но с включенной защитой на платформе SIGSEGV должен генерироваться при записи "достаточно" (выравнивания) за пределы массива:
$ gcc -DPROTECT_ME=1 1.c && ./a.out
a[0]=0
a[1]=1
a[2]=2
a[3]=3
a[4]=4
a[5]=5
The process had written to a bad place
Aborted (core dumped)