reset_load_command_pointers_test.c [plain text]
#include "test_main.h"
#include "stuff/breakout.h"
#include <mach-o/loader.h>
#include <stdlib.h>
#include <string.h>
static void mh_add_buffer(struct mach_header* mh,
struct load_command** lcmds,
void* q,
uint32_t size)
{
*lcmds = reallocf(*lcmds, mh->sizeofcmds + size);
unsigned char* p = (unsigned char*)*lcmds;
memcpy(&(p[mh->sizeofcmds]), q, size);
mh->sizeofcmds += size;
}
static void mh_add_load_command(struct mach_header* mh,
struct load_command** lcmds,
void* lc,
uint32_t size)
{
mh_add_buffer(mh, lcmds, lc, size);
mh->ncmds += 1;
}
static void mh_reset(void* mh_void, struct load_command** lcmds)
{
struct mach_header* mh = (struct mach_header*)mh_void;
if (*lcmds)
free(*lcmds);
*lcmds = NULL;
mh->ncmds = 0;
mh->sizeofcmds = 0;
}
static uint32_t random32(void)
{
return arc4random();
}
static uint64_t random64(void)
{
uint64_t r = arc4random();
return (r << 32) | arc4random();
}
static struct linkedit_data_command
random_ledata_cmd(uint32_t cmd)
{
struct linkedit_data_command lc = {0};
lc.cmd = cmd;
lc.cmdsize = sizeof(lc);
lc.dataoff = random32();
lc.datasize = random32();
return lc;
}
enum index_type {
index_type_none,
index_type_seg_linkedit,
index_type_seg_bitcode,
index_type_symtab,
index_type_dysymtab,
index_type_dyldinfo,
index_type_tlhint,
index_type_prebind,
index_type_codesig,
index_type_splitseg,
index_type_funcstarts,
index_type_dice,
index_type_csdir,
index_type_ldhint,
index_type_trie,
index_type_chains,
index_type_encinfo,
};
static int check_loadcmd(const enum index_type type, const char* name,
const void* orig, void* copy, const uint32_t size,
const enum index_type except)
{
if (except == type)
return check_null(name, copy);
return check_memory(name, orig, copy, size);
}
#define UNINITIALIZED ((void*)0xA0A0A0A0)
static void test_reset_load_command_pointers(int is64, enum index_type except)
{
struct object _obj, *obj = &_obj;
struct mach_header mh32 = {0};
struct mach_header_64 mh64 = {0};
void* mh_void;
memset(obj, 0, sizeof(*obj));
if (is64)
obj->mh64 = mh_void = &mh64;
else
obj->mh = mh_void = &mh32;
obj->st = UNINITIALIZED;
obj->dyst = UNINITIALIZED;
obj->hints_cmd = UNINITIALIZED;
obj->cs = UNINITIALIZED;
obj->seg_bitcode = UNINITIALIZED;
obj->seg_bitcode64 = UNINITIALIZED;
obj->seg_linkedit = UNINITIALIZED;
obj->seg_linkedit64 = UNINITIALIZED;
obj->code_sig_cmd = UNINITIALIZED;
obj->split_info_cmd = UNINITIALIZED;
obj->func_starts_info_cmd = UNINITIALIZED;
obj->data_in_code_cmd = UNINITIALIZED;
obj->code_sign_drs_cmd = UNINITIALIZED;
obj->link_opt_hint_cmd = UNINITIALIZED;
obj->dyld_info = UNINITIALIZED;
obj->dyld_exports_trie = UNINITIALIZED;
obj->dyld_chained_fixups = UNINITIALIZED;
obj->encryption_info_command = UNINITIALIZED;
obj->encryption_info_command64 = UNINITIALIZED;
struct segment_command seg_linkedit32 = {0};
struct segment_command seg_bitcode32 = {0};
struct segment_command_64 seg_linkedit64 = {0};
struct segment_command_64 seg_bitcode64 = {0};
if (is64) {
if (except != index_type_seg_linkedit) {
strcpy(seg_linkedit64.segname, SEG_LINKEDIT);
seg_linkedit64.vmaddr = random64();
seg_linkedit64.vmsize = random64();
seg_linkedit64.fileoff = random64();
seg_linkedit64.filesize = random64();
seg_linkedit64.cmd = LC_SEGMENT_64;
seg_linkedit64.cmdsize = (sizeof(struct segment_command_64) +
sizeof(struct section_64) *
seg_linkedit64.nsects);
mh_add_load_command(mh_void, &obj->load_commands, &seg_linkedit64,
sizeof(seg_linkedit64));
}
if (except != index_type_seg_bitcode) {
strcpy(seg_bitcode64.segname, "__LLVM");
seg_bitcode64.vmaddr = random64();
seg_bitcode64.vmsize = random64();
seg_bitcode64.fileoff = random64();
seg_bitcode64.filesize = random64();
seg_bitcode64.cmd = LC_SEGMENT_64;
seg_bitcode64.cmdsize = (sizeof(struct segment_command_64) +
sizeof(struct section_64) *
seg_bitcode64.nsects);
mh_add_load_command(mh_void, &obj->load_commands, &seg_bitcode64,
sizeof(seg_bitcode64));
}
}
else {
if (except != index_type_seg_linkedit) {
strcpy(seg_linkedit32.segname, SEG_LINKEDIT);
seg_linkedit32.vmaddr = random32();
seg_linkedit32.vmsize = random32();
seg_linkedit32.fileoff = random32();
seg_linkedit32.filesize = random32();
seg_linkedit32.cmd = LC_SEGMENT;
seg_linkedit32.cmdsize = (sizeof(struct segment_command) +
sizeof(struct section) *
seg_linkedit32.nsects);
mh_add_load_command(mh_void, &obj->load_commands, &seg_linkedit32,
sizeof(seg_linkedit32));
}
if (except != index_type_seg_bitcode) {
strcpy(seg_bitcode32.segname, "__LLVM");
seg_bitcode32.vmaddr = random32();
seg_bitcode32.vmsize = random32();
seg_bitcode32.fileoff = random32();
seg_bitcode32.filesize = random32();
seg_bitcode32.cmd = LC_SEGMENT;
seg_bitcode32.cmdsize = (sizeof(struct segment_command) +
sizeof(struct section) *
seg_bitcode32.nsects);
mh_add_load_command(mh_void, &obj->load_commands, &seg_bitcode32,
sizeof(seg_bitcode32));
}
}
struct symtab_command st = {0};
if (except != index_type_symtab) {
st.cmd = LC_SYMTAB;
st.cmdsize = sizeof(st);
st.symoff = random32();
st.nsyms = random32();
st.stroff = random32();
st.strsize = random32();
mh_add_load_command(mh_void, &obj->load_commands, &st, sizeof(st));
}
struct dysymtab_command dyst = {0};
if (except != index_type_dysymtab) {
dyst.cmd = LC_DYSYMTAB;
dyst.cmdsize = sizeof(dyst);
dyst.ilocalsym = random32();
dyst.nlocalsym = random32();
dyst.iextdefsym = random32();
dyst.nextdefsym = random32();
dyst.iundefsym = random32();
dyst.nundefsym = random32();
dyst.tocoff = random32();
dyst.ntoc = random32();
dyst.modtaboff = random32();
dyst.nmodtab = random32();
dyst.extrefsymoff = random32();
dyst.nextrefsyms = random32();
dyst.indirectsymoff = random32();
dyst.nindirectsyms = random32();
dyst.extreloff = random32();
dyst.nextrel = random32();
dyst.locreloff = random32();
dyst.nlocrel = random32();
mh_add_load_command(mh_void, &obj->load_commands, &dyst, sizeof(dyst));
}
struct dyld_info_command di = {0};
if (except != index_type_dyldinfo) {
di.cmd = LC_DYLD_INFO_ONLY;
di.cmdsize = sizeof(di);
di.rebase_off = random32();
di.rebase_size = random32();
di.bind_off = random32();
di.bind_size = random32();
di.weak_bind_off = random32();
di.weak_bind_size = random32();
di.lazy_bind_off = random32();
di.lazy_bind_size = random32();
di.export_off = random32();
di.export_size = random32();
mh_add_load_command(mh_void, &obj->load_commands, &di, sizeof(di));
}
struct twolevel_hints_command tlhint = {0};
if (except != index_type_tlhint) {
tlhint.cmd = LC_TWOLEVEL_HINTS;
tlhint.cmdsize = sizeof(tlhint);
tlhint.offset = random32();
tlhint.nhints = random32();
mh_add_load_command(mh_void, &obj->load_commands, &tlhint,
sizeof(tlhint));
}
struct prebind_cksum_command pbcs = {0};
if (except != index_type_prebind) {
pbcs.cmd = LC_PREBIND_CKSUM;
pbcs.cmdsize = sizeof(pbcs);
pbcs.cksum = random32();
mh_add_load_command(mh_void, &obj->load_commands, &pbcs, sizeof(pbcs));
}
struct linkedit_data_command cs = random_ledata_cmd(LC_CODE_SIGNATURE);
if (except != index_type_codesig)
mh_add_load_command(mh_void, &obj->load_commands, &cs, sizeof(cs));
struct linkedit_data_command ss = random_ledata_cmd(LC_SEGMENT_SPLIT_INFO);
if (except != index_type_splitseg)
mh_add_load_command(mh_void, &obj->load_commands, &ss, sizeof(ss));
struct linkedit_data_command fs = random_ledata_cmd(LC_FUNCTION_STARTS);
if (except != index_type_funcstarts)
mh_add_load_command(mh_void, &obj->load_commands, &fs, sizeof(fs));
struct linkedit_data_command dice = random_ledata_cmd(LC_DATA_IN_CODE);
if (except != index_type_dice)
mh_add_load_command(mh_void, &obj->load_commands, &dice, sizeof(dice));
struct linkedit_data_command csdr =
random_ledata_cmd(LC_DYLIB_CODE_SIGN_DRS);
if (except != index_type_csdir)
mh_add_load_command(mh_void, &obj->load_commands, &csdr, sizeof(csdr));
struct linkedit_data_command ldhint =
random_ledata_cmd(LC_LINKER_OPTIMIZATION_HINT);
if (except != index_type_ldhint)
mh_add_load_command(mh_void, &obj->load_commands, &ldhint,
sizeof(ldhint));
struct linkedit_data_command et = random_ledata_cmd(LC_DYLD_EXPORTS_TRIE);
if (except != index_type_trie)
mh_add_load_command(mh_void, &obj->load_commands, &et, sizeof(et));
struct linkedit_data_command cf = random_ledata_cmd(LC_DYLD_CHAINED_FIXUPS);
if (except != index_type_chains)
mh_add_load_command(mh_void, &obj->load_commands, &cf, sizeof(cf));
struct encryption_info_command enc32 = {0};
struct encryption_info_command_64 enc64 = {0};
if (except != index_type_encinfo) {
if (is64) {
enc64.cmd = LC_ENCRYPTION_INFO_64;
enc64.cmdsize = sizeof(enc64);
enc64.cryptoff = random32();
enc64.cryptsize = random32();
enc64.cryptid = random32();
enc64.pad = random32();
mh_add_load_command(mh_void, &obj->load_commands, &enc64,
sizeof(enc64));
}
else {
enc32.cmd = LC_ENCRYPTION_INFO;
enc32.cmdsize = sizeof(enc32);
enc32.cryptoff = random32();
enc32.cryptsize = random32();
enc32.cryptid = random32();
mh_add_load_command(mh_void, &obj->load_commands, &enc32,
sizeof(enc32));
}
}
reset_load_command_pointers(obj);
if (is64) {
check_null("obj->seg_linkedit", obj->seg_linkedit);
check_null("obj->seg_bitcode", obj->seg_bitcode);
check_loadcmd(index_type_seg_linkedit, "linkedit", &seg_linkedit64,
obj->seg_linkedit64, sizeof(seg_linkedit64), except);
check_loadcmd(index_type_seg_bitcode, "bitcode", &seg_bitcode64,
obj->seg_bitcode64, sizeof(seg_bitcode64), except);
}
else {
check_null("obj->seg_linkedit64", obj->seg_linkedit64);
check_null("obj->seg_bitcode64", obj->seg_bitcode64);
check_loadcmd(index_type_seg_linkedit, "linkedit", &seg_linkedit32,
obj->seg_linkedit, sizeof(seg_linkedit32), except);
check_loadcmd(index_type_seg_bitcode, "bitcode", &seg_bitcode32,
obj->seg_bitcode, sizeof(seg_bitcode32), except);
}
check_loadcmd(index_type_symtab, "symtab", &st, obj->st,
sizeof(st), except);
check_loadcmd(index_type_dysymtab, "dysymtab", &dyst, obj->dyst,
sizeof(dyst), except);
check_loadcmd(index_type_dyldinfo, "dyld info", &di, obj->dyld_info,
sizeof(di), except);
check_loadcmd(index_type_tlhint, "two level hints", &tlhint, obj->hints_cmd,
sizeof(tlhint), except);
check_loadcmd(index_type_prebind, "prebind cksum", &pbcs, obj->cs,
sizeof(pbcs), except);
check_loadcmd(index_type_codesig, "code signature", &cs, obj->code_sig_cmd,
sizeof(cs), except);
check_loadcmd(index_type_splitseg, "split seg", &ss, obj->split_info_cmd,
sizeof(ss), except);
check_loadcmd(index_type_funcstarts, "function starts", &fs,
obj->func_starts_info_cmd, sizeof(fs), except);
check_loadcmd(index_type_dice, "data in code", &dice,
obj->data_in_code_cmd, sizeof(dice), except);
check_loadcmd(index_type_csdir, "codesign directives", &csdr,
obj->code_sign_drs_cmd, sizeof(csdr), except);
check_loadcmd(index_type_ldhint, "linker hints", &ldhint,
obj->link_opt_hint_cmd, sizeof(ldhint), except);
check_loadcmd(index_type_trie, "dyld exports trie", &et,
obj->dyld_exports_trie, sizeof(et), except);
check_loadcmd(index_type_chains, "dyld chained fixups", &cf,
obj->dyld_chained_fixups, sizeof(cf), except);
if (is64) {
check_null("obj->encryption_info_command",
obj->encryption_info_command);
check_loadcmd(index_type_encinfo, "encryption info", &enc64,
obj->encryption_info_command64, sizeof(enc64), except);
}
else {
check_null("obj->encryption_info_command64",
obj->encryption_info_command64);
check_loadcmd(index_type_encinfo, "encryption info", &enc32,
obj->encryption_info_command, sizeof(enc32), except);
}
mh_reset(mh_void, &obj->load_commands);
}
static void test_reset_all(uint32_t is64)
{
enum index_type exceptions[] = {
index_type_none,
index_type_seg_linkedit,
index_type_seg_bitcode,
index_type_symtab,
index_type_dysymtab,
index_type_dyldinfo,
index_type_tlhint,
index_type_prebind,
index_type_codesig,
index_type_splitseg,
index_type_funcstarts,
index_type_dice,
index_type_csdir,
index_type_ldhint,
index_type_trie,
index_type_chains,
index_type_encinfo,
};
uint32_t n = sizeof(exceptions) / sizeof(*exceptions);
for (uint32_t i = 0; i < n; ++i) {
test_reset_load_command_pointers(is64, exceptions[i]);
}
}
static void test_reset_32(void)
{
test_reset_all(0);
}
static void test_reset_64(void)
{
test_reset_all(1);
}
static int test_main(void)
{
int err = 0;
if (!err) err = test_add("test reset_load_command_pointers 32-bit",
test_reset_32);
if (!err) err = test_add("test reset_load_command_pointers 64-bit",
test_reset_64);
if (!err) err = test_add("test removing code signature load command",
test_reset_64);
return err;
}