Skip to content
Snippets Groups Projects
Commit 10b6ed4a authored by jdh8d's avatar jdh8d
Browse files

No commit message

No commit message
parent d0a032c9
No related branches found
No related tags found
No related merge requests found
......@@ -5,12 +5,6 @@
/clean-all.sh -text
/get-peasoup-packages.sh -text
/irdb_vars -text
non_overlapping_heap/Makefile -text
non_overlapping_heap/noh.c -text
non_overlapping_heap/tests/Makefile -text
non_overlapping_heap/tests/inputfile -text
non_overlapping_heap/tests/test1.c -text
non_overlapping_heap/tests/test2.c -text
non_overlapping_libraries/Notes -text
non_overlapping_libraries/eglibc-2.19-debian-ubuntu-base.tar.gz -text
non_overlapping_libraries/ld-linux-x86-64.so.2 -text
......
CC=gcc
LD=ld
CFLAGS= -c -Wall -Werror -fPIC
LFLAGS= -shared -ldl -fPIC
all: noh.so
noh.so: noh.o
$(LD) $(LFLAGS) noh.o -o noh.so
noh.o: noh.c
$(CC) $(CFLAGS) noh.c
test:
make -C tests test
clean:
rm *.so *.o
#define _GNU_SOURCE
#include <errno.h>
#include <stddef.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <stdbool.h>
#include <fcntl.h>
/*
* The approach to getting non-overlapping heaps here is pretty simple - instead
* of simply allowing users to mmap in multiple variants and get the same
* result, we override the glibc mmap implementation (through LD_PRELOAD) and
* use our own instead, which allocates NUMVARIANTS multiples of the same amount
* of memory, and assigns a different section to each variant. This allows us to
* get most of the cases where memory is allocated, but not all of them. In some
* cases, programs will call mmap with specific memory addresses as the targets.
* This may indicate some sort of dependence on the memory layout, so we let the
* program have the same memory section in all variants. In others, programs may
* call mmap through a system call (which may even be in a non-transformed lib),
* which would require significantly more extensive analysis to catch and handle
* properly (and in many cases wouldn't be doable statically without solving the
* halting problem).
*
* There's four modes that this mmap function defined here uses. The first mode,
* which is slightly more complex, is for times when _init isn't run before mmap
* is called for the first time. This happens in cases where an LD_PRELOADed .so
* library calls mmap during its _init routine, such as in libheaprand.so. Since
* it is extremely difficult/impossible to guarantee that we run first, and also
* because dlsym requires mmap itself (and therefore deadlocks/infinite loops if
* it's being used from inside an mmap call).
*
* The second mode, which is, in contrast, extremely simple, is for when an mmap
* call has arguments that imply that our normal diversification technique would
* cause improper program behavior. This is essentially just a pass-through mode
* to the original glibc mmap, or our direct syscall of mmap in pre-init cases.
*
* The third mode is the normal diversification mode. This is pretty much just a
* wrapper for the other mmaps that allocates a larger region, uses mprotect and
* some math to disable all but a variant-indexed-part of it, and sends back the
* pointer to the remaining part of the region. This ensures that every version,
* regardless of index, doesn't overlap with any other version in this mode.
*
* The final mode is the probabalistic diversification mode. This mode, like the
* deterministic non-overlapping-heap mode, allocates additional memory regions.
* However, it selects the sub-region at pseudo-random, making it less likely an
* address will be mapped the same way in multiple variants. This is really just
* a fallback, though, since it's likely that a good number will overlap.
*/
int nthisvar = 0;
int nnumvar = 1;
int randfd = 0;
int pagesize = 0;
// yeah, it's a lot of extra allocation, but it's all virtual allocations that are immediately
// paged out - not actually a problem long-term in a 64-bit address space (generally)
#define PROB_NUM_VARIANTS 64
extern void* mmap(void*, size_t, int, int, int, off_t);
void* (*orig_mmap)(void*, size_t, int, int ,int, off_t) = NULL;
int get_config_vars(void) {
int fd = open("/variant_specific/nolnoh_config",O_RDONLY);
if(fd == -1)
return -1;
int bufsize = 17;
char buf[bufsize];
int i;
for(i=0;i<bufsize;i++) {
buf[i]='\0';
}
ssize_t nr = read(fd, buf, bufsize-1);
if(nr == 0) {
// we read 0 of the tokens from the file, since we read absolutely nothing
close(fd);
return 0;
}
nnumvar = atoi(buf);
// note that this currently caps at 512 variants, increase this number if necessary to support more
if(nnumvar <= 0 || nnumvar > 512) {
// wasn't a valid value
nnumvar = 0;
close(fd);
return 0;
}
int mode = 0;
// get the second number
for(i=0;i<bufsize;i++) {
if(buf[i] == '\0') {
close(fd);
return 1;
}
if(buf[i] == ' ' || buf[i] == '\t' || buf[i] == '\r' || buf[i] == '\n') {
mode = 1;
}
if(mode == 1 && buf[i] >= '0' && buf[i] <= '9') {
nthisvar = atoi(buf+i);
// now sanity check it
if(nthisvar >= nnumvar || nthisvar < 0) {
nthisvar = 0;
close(fd);
return 1;
}
close(fd);
return 2;
}
}
// we reached the end of the string in an unexpected manner; just say that the thing we have works
close(fd);
return 1;
}
void _init(void) {
orig_mmap = (void*(*)(void*, size_t, int, int, int, off_t)) dlsym(RTLD_NEXT, "mmap");
int parsed = get_config_vars();
#ifdef DEBUG
#ifdef MVEE_SAFE
printf("parsed was X\n");
#else
printf("parsed was %i\n",parsed);
#endif
#endif
if(parsed <= 0) {
//neither nnumvar nor nthivar are set
// if we don't know how many variants, try doing randomization of up to 64
if(randfd == 0) {
// run in probabalistic mode, since we didn't find the arguments for proper indexing
randfd = open("/dev/cfar_urandom",O_RDONLY);
}
// we're either going to use nthisvar in the ultimate failsafe mode where we don't do the randomization, or we're going to ignore it; either way, it's 0
nthisvar = 0;
// now check if it's still 0, or if it actually set up properly this time
if(randfd != 0) {
// we don't use nthisvar, just nnumvar and a random choice
nnumvar = PROB_NUM_VARIANTS;
} else {
// if we don't have our randomness source, and we don't have structured info, just behave as normal mmap
// (with the two extra mprotect calls, so syscall alignment is preserved)
nnumvar = 1;
}
} else if (parsed == 1) {
//nnumvar is set, but not nthisvar
// if we don't know how many variants, try doing randomization of up to 64
if(randfd == 0) {
// run in probabalistic mode, since we didn't find the arguments for proper indexing
randfd = open("/dev/cfar_urandom",O_RDONLY);
}
// we're either going to use nthisvar in the ultimate failsafe mode where we don't do the randomization, or we're going to ignore it; either way, it's 0
nthisvar = 0;
// if randfd is still 0, we just default to the safe case; in any case, we're done here
} else if (parsed == 2) {
//nthisvar and nnumvar have been set, we're done here
}
#ifdef DEBUG
printf("finished _init\n");
#endif
}
// rounding up to alignments is important and useful
size_t rounduptomultiple(size_t length, int roundto) {
size_t remainder = length % roundto;
if(remainder == 0) {
return length;
}
return length + roundto - remainder;
}
// we need page alignment for a few things - mmap works much better with it
size_t rounduptopagemultiple(size_t length) {
if(pagesize == 0) {
pagesize = getpagesize();
}
return rounduptomultiple(length, pagesize);
}
// the non-diversified part of the mapping - this basically acts as a call to mmap that works in a couple of odd additional contexts
void* actually_mmap(void* address, size_t length, int protect, int flags, int filedes, off_t offset) {
// ok, now we need to actually do the call
if(orig_mmap == NULL) {
/*
* NOTE: WE CANNOT DO THIS - causes deadlock, infinite loops, and unpredictability
*/
//orig_mmap = (void*(*)(void*, size_t, int, int, int, off_t)) dlsym(RTLD_NEXT, "mmap");
/*
* The reason is that if our _init function hasn't been called yet -
* which happens with surprising frequency - then the dlsym lookup
* eventually goes, acquires a lock, and calls mmap. This is bad, as
* that mmap call gets back here, and on the second pass, it deadlocks
* on the lock.
* Instead, we do essentially what the glibc mmap wrapper does here, and
* hope that no one is doing something esoteric that relies on any change
* in glibc's wrapper.
*/
#ifndef SYSCALL_MMAP2_UNIT
#define SYSCALL_MMAP2_UNIT 4096ULL
#endif
#define UNIT SYSCALL_MMAP2_UNIT
#define OFF_MASK ((-0x2000ULL << (8*sizeof(long)-1)) | (UNIT-1))
if (offset & OFF_MASK) {
errno = EINVAL;
return MAP_FAILED;
}
if (length >= PTRDIFF_MAX) {
errno = ENOMEM;
return MAP_FAILED;
}
#ifdef SYS_mmap2
return (void*)syscall(SYS_mmap2, address, length, protect, flags, filedes, offset/UNIT);
#else
return (void*)syscall(SYS_mmap, address, length, protect, flags, filedes, offset);
#endif
} else {
// it's actually been initialized, so use the original glibc/other mmap implementation - let's hope it plays nice with the hacky stuff above
return orig_mmap(address, length, protect, flags, filedes, offset);
}
}
void* mmap(void* address, size_t length, int protect, int flags, int filedes, off_t offset) {
#ifdef DEBUG
printf("entering mmap call\n");
#endif
// figure out what index we are in a randomized context
if(
(flags & MAP_FIXED) // must use normal mmap, since the program is now guaranteed the destination address or failure
|| (flags & MAP_SHARED) // we're sharing between multiple programs, which is complex enough as it is - alignment is important, and we'd have to do funky stuff to ensure non-overlappingness with this regardless
) {
#ifdef DEBUG
printf("non-modified path\n");
#endif
// don't modify the arguments - we unfortunately can't touch this much, since it's going to be mapped in a way that we can't nicely diversify (yet)
return actually_mmap(address, length, protect, flags, filedes, offset);
} else {
// we're diversified now - do fancier stuff
size_t alignedlength = length;
void* new_mapping = MAP_FAILED;
// get the nthisvar for this run
if(randfd != 0 && randfd != -1) {
#ifdef DEBUG
#ifdef MVEE_SAFE
printf("using randomization, is at X/X\n");
#else
printf("using randomization, is at %i/%i\n",nthisvar,nnumvar);
#endif
#endif
// we're in probabalistic mode
uint32_t target = 0;
read(randfd, &target, 4);
nthisvar = (int)(target % nnumvar);
#ifdef DEBUG
#ifdef MVEE_SAFE
printf("chose X/X\n");
#else
printf("chose %i/%i\n",nthisvar,nnumvar);
#endif
#endif
}
// branch on whether this is a file-backed allocation or not
if (flags & MAP_ANONYMOUS) {
#ifdef DEBUG
printf("using anonymous mapping path\n");
#endif
// if it's a non-file mapping, just allocate a larger area, and then use part of that
#if defined(MAP_ALIGN)
// gotta round up to the next barrier, but this only matters on solaris (in theory)
// support it as soon as it actually comes up
length = rounduptomultiple(length, address);
#endif
// we want to allocate a bit more space - this is so that each variant gets its own part of the allocated segment, in a non-overlapping fashion
// let's keep page alignment the same
alignedlength = rounduptopagemultiple(length);
length = alignedlength * nnumvar;
new_mapping = actually_mmap(address, length, protect, flags, filedes, offset);
// now that we've done the call, handle the result and return
if(new_mapping == MAP_FAILED) {
// it failed, and if we were to retry, it would diverge anyway - just return the failure
// alternatively, we had to do just a normal mmap, so just return the pointer
return new_mapping;
}
// in the event that we got the memory and such, mprotect the other portions so that they won't be able to be accesssed improperly
mprotect(new_mapping, alignedlength * nthisvar, PROT_NONE);
mprotect(new_mapping + alignedlength * (nthisvar+1), alignedlength * (nnumvar - (nthisvar + 1)), PROT_NONE);
#ifdef DEBUG
#ifdef MVEE_SAFE
printf("returning new mapping at X\n");
#else
printf("returning new mapping at %p\n", new_mapping + alignedlength * nthisvar);
#endif
#endif
return new_mapping + alignedlength * nthisvar;
} else {
#ifdef DEBUG
printf("performing tricky file mapping approach\n");
#endif
// for file mappings, handle them by repeated allocation of the required size and selection of the currect index
int i=0;
for(i=0; i < nnumvar; i++) {
if(i == nthisvar) {
new_mapping = actually_mmap(address, length, protect, flags, filedes, offset);
} else {
// ignored
actually_mmap(address, length, PROT_NONE, flags | MAP_ANONYMOUS, -1, 0);
}
}
return new_mapping;
}
}
}
all: test
define maketest
Test$(1): test$(1).c
gcc -fno-stack-protector -o Test$(1) test$(1).c
testTest$(1): Test$(1)
./Test$(1)
LD_PRELOAD=../noh.so setarch $$$$(uname -m) --addr-no-randomize ./Test$(1)
LD_PRELOAD=../noh.so setarch $$$$(uname -m) --addr-no-randomize ./Test$(1)
NUMVARIANTS=4 VARIANTINDEX=0 LD_PRELOAD=../noh.so setarch $$$$(uname -m) --addr-no-randomize ./Test$(1)
NUMVARIANTS=4 VARIANTINDEX=1 LD_PRELOAD=../noh.so setarch $$$$(uname -m) --addr-no-randomize ./Test$(1)
NUMVARIANTS=4 VARIANTINDEX=2 LD_PRELOAD=../noh.so setarch $$$$(uname -m) --addr-no-randomize ./Test$(1)
NUMVARIANTS=4 VARIANTINDEX=3 LD_PRELOAD=../noh.so setarch $$$$(uname -m) --addr-no-randomize ./Test$(1)
endef
tests=1 2
$(foreach i,$(tests),$(eval $(call maketest,$(i))))
clean:
rm Test*
test: $(foreach i,$(tests),testTest$(i))
test passed!
#include <sys/mman.h>
#include <stdio.h>
int main() {
void* foobar = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
printf("%p\n",foobar);
return 0;
}
#include <sys/mman.h>
#include <stdio.h>
#include <fcntl.h>
int main() {
int fd = open("./inputfile", O_RDONLY);
void* foobar = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
printf("%p\n",foobar);
printf("%s\n",(char*)foobar);
return 0;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment