Skip to content

Instantly share code, notes, and snippets.

@philpennock
Last active December 1, 2017 20:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save philpennock/7227767 to your computer and use it in GitHub Desktop.
Save philpennock/7227767 to your computer and use it in GitHub Desktop.
// this is C99
// compile: gcc -std=c99 -ggdb -O3 -Wall guarded_memory_alloc_test.c
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#ifdef __GNUC__
#define PRINTF_FUNCTION(A,B) __attribute__((format(printf,A,B)))
#else
#define PRINTF_FUNCTION(A,B) /**/
#endif
static void PRINTF_FUNCTION(2, 3)
trace_realwithloc(const char *loc, const char *fmt, ...)
{
va_list ap;
fflush(NULL);
fprintf(stderr, "%s(): ", loc);
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fputc('\n', stderr);
fflush(stderr);
}
#define trace(format, ...) do { \
int saved_err = errno; \
trace_realwithloc(__func__, format, ## __VA_ARGS__); \
errno = saved_err; } while(0)
static void *
create_bounded_range(const size_t want)
{
size_t buffered_want, request_sz;
int page_sz;
void *allocated, *using;
if (want < 1) {
errno = EINVAL;
return NULL;
}
page_sz = getpagesize();
buffered_want = ((want + page_sz - 1) / page_sz) * page_sz;
request_sz = buffered_want + 2 * page_sz;
trace("want %#zx octets, page size %#x, padding to %#zx, requesting %#zx",
want, page_sz, buffered_want, request_sz);
allocated = mmap(NULL, request_sz, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
if (allocated == MAP_FAILED) {
trace("initial mmap(%zu) failed: %s", request_sz, strerror(errno));
return NULL;
}
trace("allocated at %p", allocated);
using = allocated + page_sz;
// I originally used munmap() here but four years later,
// realized that this is buggy: if the memory is not allocated, then
// the mmap call is free to allocate it later. It might not _remain_
// empty as a guard.
if (mprotect(allocated, page_sz, PROT_NONE) < 0) {
int se = errno;
trace("mprotect(first_page, PROT_NONE) failed: %s", strerror(errno));
if (munmap(allocated, request_sz) < 0) {
trace("additionally, munmap(allocation) failed: %s", strerror(errno));
} else {
trace("however, munmap(allocation) succeeded (is mprotect() not available?)");
}
errno = se;
return NULL;
}
if (mprotect(using + buffered_want, page_sz, PROT_NONE) < 0) {
int se = errno;
trace("mprotect(last_page, PROT_NONE) failed: %s", strerror(se));
if (munmap(allocated, request_sz) < 0) {
trace("additionally, munmap(allocation) failed: %s", strerror(errno));
}
errno = se;
return NULL;
}
trace("have marked PROT_NONE first and last pages of %#x octets each", page_sz);
trace("returning %p", using);
return using;
}
int
main(int argc, char **argv)
{
char *end;
int errcount = 0;
int start_tests = 1;
int last_allocated_index = -1;
size_t last_allocated_sz = 0;
int crash = 0;
void *got = NULL; // complaint might be used uninitialised is false
if (argc < 2) {
fprintf(stderr, "%s: need at least one size parameter\n", argv[0]);
return 1;
}
if (strcasecmp(argv[1], "crash") == 0) {
start_tests = 2;
crash = 1;
}
for (int i = start_tests; i < argc; ++i) {
if (*argv[i] == '\0') {
trace("skipping empty parameter #%d", i);
errcount++;
continue;
}
unsigned long ul;
ul = strtoul(argv[i], &end, 0);
if (*end == '\0') {
got = create_bounded_range((size_t)ul);
if (got == NULL) {
errcount++;
printf("%d: Allocation of %ld octets failed\n", i, ul);
} else {
printf("%d: Allocated space for %ld octets at %p\n", i, ul, got);
last_allocated_index = i;
last_allocated_sz = (size_t)ul;
}
} else {
errcount++;
trace("skipping parameter #%d because not numeric", i);
}
}
if (crash) {
if (last_allocated_index < 0) {
trace("no successful allocations, not forcing a crash");
return 1;
}
int page_sz = getpagesize();
size_t allocated = ((last_allocated_sz + page_sz - 1) / page_sz) * page_sz;
trace("will attempt to crash %#zx octet buffer allocated for size %#zx request",
allocated, last_allocated_sz);
trace("writing at [start] %p", got);
*(uint8_t*)got = 1;
trace("writing at [last] %p (%p + %#zx)", got+allocated-1, got, allocated-1);
*((uint8_t*)got+allocated-1) = 2;
trace("writing one past end (should crash) at %p (%p + %#zx)", got+allocated, got, allocated);
*((uint8_t*)got+allocated) = 3;
trace("oops, not crashed");
return 2;
}
return (errcount == 0) ? 0 : 1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment