Skip to content

Commit 2a2a872

Browse files
committed
Ensure that reserved/trampoline memory is disjoint
(at the page level). Previously, trampolines might sometimes be allocated on writable pages, i.e., when the page also hosted a writable reservation.
1 parent dbe2497 commit 2a2a872

10 files changed

Lines changed: 108 additions & 41 deletions

File tree

doc/e9patch-programming-guide.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,12 @@ of the frontend, and E9Patch will simply execute the template "as-is".
340340
The `"reserve"` message is useful for reserving sections of the
341341
patched program's virtual address space and (optionally) initializing
342342
it with data.
343-
Reserved addresses will not be used to host trampolines.
343+
The reserved address range will not be used to host trampolines.
344+
345+
Note that the reserved address range will be implicitly rounded to the
346+
nearest page boundary.
347+
This means that trampoline and reserved memory will be disjoint at the
348+
page level.
344349

345350
#### Parameters:
346351

src/e9patch/e9api.cpp

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -145,10 +145,7 @@ static void queueFlush(Binary *B, intptr_t cursor)
145145
{
146146
// Options entry
147147
char * const *argv = entry.argv;
148-
int argc;
149-
for (argc = 0; argv[argc] != nullptr; argc++)
150-
;
151-
parseOptions(argc, argv, /*api=*/true);
148+
parseOptions(argv, /*api=*/true);
152149
delete[] argv;
153150
switch (B->mode)
154151
{
@@ -619,17 +616,33 @@ static void parseReserve(Binary *B, const Message &msg)
619616
if (bytes != nullptr)
620617
{
621618
bytes->preload = true;
622-
const Alloc *A = allocate(B, address, address, bytes, nullptr);
623-
if (A == nullptr)
624-
error("failed to reserve address space at address "
625-
ADDRESS_FORMAT, ADDRESS(address));
626619
length = getTrampolineSize(B, bytes, nullptr);
627-
debug("reserved address space [prot=%c%c%c, size=%zu, bytes="
628-
ADDRESS_FORMAT ".." ADDRESS_FORMAT "]",
629-
(protection & PROT_READ? 'r': '-'),
630-
(protection & PROT_WRITE? 'w': '-'),
631-
(protection & PROT_EXEC? 'x': '-'), length, ADDRESS(address),
632-
ADDRESS(address + (intptr_t)length));
620+
size_t length_lo = address % PAGE_SIZE;
621+
size_t length_hi = PAGE_SIZE - (length_lo + length) % PAGE_SIZE;
622+
intptr_t address_lo = address - length_lo;
623+
intptr_t address_hi = address + length;
624+
const unsigned num_trampolines = 3;
625+
Trampoline *trampolines[num_trampolines] =
626+
{makePadding(length_lo), bytes, makePadding(length_hi)};
627+
intptr_t addrs[num_trampolines] =
628+
{address_lo, address, address_hi};
629+
size_t lens[num_trampolines] = {length_lo, length, length_hi};
630+
for (unsigned i = 0; i < num_trampolines; i++)
631+
{
632+
if (trampolines[i] == nullptr)
633+
continue;
634+
const Alloc *A = allocate(B, addrs[i], addrs[i], trampolines[i],
635+
nullptr);
636+
if (A == nullptr)
637+
error("failed to reserve address space at address "
638+
ADDRESS_FORMAT, ADDRESS(addrs[i]));
639+
debug("reserved address space [prot=%c%c%c, size=%zu, bytes="
640+
ADDRESS_FORMAT ".." ADDRESS_FORMAT "]",
641+
(protection & PROT_READ? 'r': '-'),
642+
(protection & PROT_WRITE? 'w': '-'),
643+
(protection & PROT_EXEC? 'x': '-'), lens[i], ADDRESS(addrs[i]),
644+
ADDRESS(addrs[i] + (intptr_t)lens[i]));
645+
}
633646
}
634647
if (have_length)
635648
{
@@ -713,8 +726,16 @@ static void parseOptions(Binary *B, const Message &msg)
713726
if (dup)
714727
error("failed to parse \"options\" message (id=%u); duplicate "
715728
"parameters detected", msg.id);
716-
PatchEntry entry(argv);
717-
B->Q.push_front(entry);
729+
if (B->cursor == INTPTR_MAX)
730+
{
731+
parseOptions(argv, /*api=*/true);
732+
delete[] argv;
733+
}
734+
else
735+
{
736+
PatchEntry entry(argv);
737+
B->Q.push_front(entry);
738+
}
718739
}
719740

720741
/*

src/e9patch/e9elf.cpp

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -337,10 +337,13 @@ size_t emitElf(Binary *B, const MappingSet &mappings, size_t mapping_size)
337337
}
338338
std::vector<Bounds> bounds;
339339
intptr_t ub = INTPTR_MIN;
340+
// level 0 == non-trampoline mappings (reserves, refactors), default mmap()
341+
// level 1 == trampoline mappings, user mmap() can be used.
340342
for (unsigned i = 0; i < 2; i++)
341343
{
342-
config->maps[i] = (uint32_t)(size - config_offset);
343-
bool preload = (i == 0);
344+
unsigned level = i;
345+
config->maps[level] = (uint32_t)(size - config_offset);
346+
bool preload = (level == 0);
344347
for (auto *mapping: mappings)
345348
{
346349
if (preload)
@@ -361,18 +364,34 @@ size_t emitElf(Binary *B, const MappingSet &mappings, size_t mapping_size)
361364
size_t len = b.ub - b.lb;
362365
off_t offset = offset_0 + b.lb;
363366

364-
debug("load trampoline: mmap(addr=" ADDRESS_FORMAT
367+
const char *name = (level == 0? "reserve": "trampoline");
368+
debug("load %s: mmap(addr=" ADDRESS_FORMAT
365369
",size=%zu,offset=+%zu,prot=%c%c%c)",
366-
ADDRESS(base), len, offset_0, (r? 'r': '-'),
370+
name, ADDRESS(base), len, offset_0, (r? 'r': '-'),
367371
(w? 'w': '-'), (x? 'x': '-'));
368372
stat_num_virtual_bytes += len;
369373

370374
size += emitLoaderMap(data + size, base, len, offset,
371375
r, w, x, &ub);
372-
config->num_maps[i]++;
376+
config->num_maps[level]++;
373377
}
374378
}
375379
}
380+
if (level == 0)
381+
{
382+
// Emit refactorings at level 0.
383+
for (const auto &refactor: refactors)
384+
{
385+
debug("load refactor: mmap(addr=" ADDRESS_FORMAT
386+
",size=%zu,offset=+%zd,prot=r-x)",
387+
ADDRESS(refactor.addr), refactor.size,
388+
refactor.patched.offset);
389+
size += emitLoaderMap(data + size, refactor.addr,
390+
refactor.size, refactor.patched.offset, /*r=*/true,
391+
/*w=*/false, /*x=*/true, nullptr);
392+
config->num_maps[level]++;
393+
}
394+
}
376395
}
377396
if (ub > option_loader_base)
378397
{
@@ -383,16 +402,6 @@ size_t emitElf(Binary *B, const MappingSet &mappings, size_t mapping_size)
383402
"exceed maximum mapping address (0x%lx) (see `--mem-ub')",
384403
option_loader_base, ub);
385404
}
386-
for (const auto &refactor: refactors)
387-
{
388-
debug("load refactoring: mmap(" ADDRESS_FORMAT ", %zu, "
389-
"PROT_READ | PROT_WRITE | 0, MAP_FIXED | MAP_PRIVATE, fd, +%zd)",
390-
ADDRESS(refactor.addr), refactor.size, refactor.patched.offset);
391-
size += emitLoaderMap(data + size, refactor.addr, refactor.size,
392-
refactor.patched.offset, /*r=*/true, /*w=*/false, /*x=*/true,
393-
nullptr);
394-
config->num_maps[1]++;
395-
}
396405
intptr_t entry = (option_loader_base + (size - config_offset));
397406
if (option_trap_entry)
398407
data[size++] = /*int3=*/0xCC;

src/e9patch/e9json.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,36 @@ static Entry makeDebugEntry(void)
651651
return entry;
652652
}
653653

654+
/*
655+
* Create a ZEROES template entry.
656+
*/
657+
static Entry makeZeroesEntry(size_t len)
658+
{
659+
Entry entry;
660+
entry.kind = ENTRY_ZEROES;
661+
entry.length = (unsigned)len;
662+
return entry;
663+
}
664+
665+
/*
666+
* Make padding.
667+
*/
668+
Trampoline *makePadding(size_t len)
669+
{
670+
if (len == 0)
671+
return nullptr;
672+
size_t num_entries = 1;
673+
uint8_t *ptr =
674+
new uint8_t[sizeof(Trampoline) + num_entries * sizeof(Entry)];
675+
Trampoline *T = (Trampoline *)ptr;
676+
T->prot = PROT_READ | PROT_EXEC;
677+
T->num_entries = num_entries;
678+
T->preload = false;
679+
T->entries[0] = makeZeroesEntry(len);
680+
681+
return T;
682+
}
683+
654684
/*
655685
* Convert an integer.
656686
*/

src/e9patch/e9json.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,5 +112,6 @@ struct Message
112112

113113
bool getMessage(FILE *stream, size_t lineno, Message &msg);
114114
const char *getMethodString(Method method);
115+
Trampoline *makePadding(size_t size);
115116

116117
#endif

src/e9patch/e9mapping.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -157,15 +157,15 @@ static Key calculateKey(const Allocator &allocator, const size_t MAPPING_SIZE,
157157
if (a->T == nullptr)
158158
continue;
159159

160-
size_t preamble = a->lb - BASE;
160+
size_t prologue = a->lb - BASE;
161161
size_t postscript = (a->ub >= END? 0: END - a->ub);
162162

163-
preamble = preamble / UNIT_SIZE;
163+
prologue = prologue / UNIT_SIZE;
164164
postscript = postscript / UNIT_SIZE;
165-
assert(preamble < KEY_BITS);
165+
assert(prologue < KEY_BITS);
166166
assert(postscript < KEY_BITS);
167167

168-
Key tmp = (KEY_ONES << preamble);
168+
Key tmp = (KEY_ONES << prologue);
169169
tmp &= (KEY_ONES >> postscript);
170170
key |= tmp;
171171
}

src/e9patch/e9patch.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -371,8 +371,11 @@ enum Option
371371
/*
372372
* Parse options.
373373
*/
374-
void parseOptions(int argc, char * const argv[], bool api)
374+
void parseOptions(char * const argv[], bool api)
375375
{
376+
int argc;
377+
for (argc = 0; argv[argc] != nullptr; argc++)
378+
;
376379
const int req_arg = required_argument, opt_arg = optional_argument,
377380
no_arg = no_argument;
378381
static const struct option long_options[] =
@@ -619,7 +622,7 @@ int realMain(int argc, char **argv)
619622
if (getenv("E9PATCH_DEBUG") != nullptr)
620623
option_debug = true;
621624

622-
parseOptions(argc, argv);
625+
parseOptions(argv);
623626

624627
if (option_input != "-")
625628
{

src/e9patch/e9patch.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ extern size_t stat_num_physical_bytes;
542542
extern size_t stat_input_file_size;
543543
extern size_t stat_output_file_size;
544544

545-
extern void parseOptions(int argc, char * const argv[], bool api = false);
545+
extern void parseOptions(char * const argv[], bool api = false);
546546
extern void NO_RETURN error(const char *msg, ...);
547547
extern void warning(const char *msg, ...);
548548
extern void debugImpl(const char *msg, ...);

test/regtest/init_exe.exp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
1-
mmap() intercepted
21
init argv = "./init_exe.exe"
32
PASSED

test/regtest/init_pie.exp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
1-
mmap() intercepted
21
init argv = "./init_pie.exe"
32
PASSED

0 commit comments

Comments
 (0)