From 8dc8fdd71f83b8b7b4b2928c5e11c807de8f0ea5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Thu, 29 Jun 2023 19:27:36 +0200 Subject: [PATCH] Fix some oob reads in xnu kernelcache A __DATA_CONST.__const section of e.g. size 1 could make these loops read beyond the end of the allocated buffer. --- librz/bin/p/bin_xnu_kernelcache.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/librz/bin/p/bin_xnu_kernelcache.c b/librz/bin/p/bin_xnu_kernelcache.c index 7fe8c210ff..88de181c03 100644 --- a/librz/bin/p/bin_xnu_kernelcache.c +++ b/librz/bin/p/bin_xnu_kernelcache.c @@ -1284,6 +1284,10 @@ typedef struct _r_sysent { ut16 sy_arg_bytes; } RSysEnt; +/** + * Search for the struct sysent sysent[] array of xnu and assign names to all syscall + * handlers referenced by it. + */ static RzList /**/ *resolve_syscalls(RzXNUKernelCacheObj *obj, ut64 enosys_addr) { struct section_t *sections = NULL; if (!(sections = MACH0_(get_sections)(obj->mach0))) { @@ -1294,8 +1298,7 @@ static RzList /**/ *resolve_syscalls(RzXNUKernelCacheObj *obj, ut RzSyscall *syscall = NULL; ut8 *data_const = NULL; ut64 data_const_offset = 0, data_const_size = 0, data_const_vaddr = 0; - int i = 0; - for (; !sections[i].last; i++) { + for (int i = 0; !sections[i].last; i++) { if (strstr(sections[i].name, "__DATA_CONST.__const")) { data_const_offset = sections[i].offset; data_const_size = sections[i].size; @@ -1318,7 +1321,7 @@ static RzList /**/ *resolve_syscalls(RzXNUKernelCacheObj *obj, ut ut8 *cursor = data_const; ut8 *end = data_const + data_const_size; - while (cursor < end) { + while (cursor + sizeof(ut64) <= end) { ut64 test = rz_read_le64(cursor); if (test == enosys_addr) { break; @@ -1336,6 +1339,7 @@ static RzList /**/ *resolve_syscalls(RzXNUKernelCacheObj *obj, ut ut64 x = rz_read_le64(cursor + 8); ut64 y = rz_read_le64(cursor + 16); + // rewind by sizeof(struct sysent) until finding something that is definitely not a struct sysent if (IS_KERNEL_ADDR(addr) && (x == 0 || IS_KERNEL_ADDR(x)) && (y != 0 && !IS_KERNEL_ADDR(y))) { @@ -1381,7 +1385,7 @@ static RzList /**/ *resolve_syscalls(RzXNUKernelCacheObj *obj, ut sym->type = "OBJECT"; rz_list_append(syscalls, sym); - i = 1; + int i = 1; cursor += 24; int num_syscalls = sdb_count(syscall->db); while (cursor < end && i < num_syscalls) { @@ -1495,7 +1499,7 @@ static RzList /**/ *resolve_mig_subsystem(RzXNUKernelCacheObj *ob ut8 *cursor = data_const; ut8 *end = data_const + data_const_size; - while (cursor < end) { + while (cursor + sizeof(ut64) * 2 <= end) { ut64 subs_p = K_PPTR(rz_read_le64(cursor)); if (subs_p < text_exec_vaddr || subs_p >= text_exec_vaddr + text_exec_size) { cursor += 8; @@ -1509,12 +1513,16 @@ static RzList /**/ *resolve_mig_subsystem(RzXNUKernelCacheObj *ob continue; } + ut8 *array_cursor = cursor + K_MIG_SUBSYSTEM_SIZE; + ut8 *end_array = array_cursor + n_routines * K_MIG_ROUTINE_SIZE; + if (end_array > end) { + cursor += 16; + continue; + } ut64 *routines = (ut64 *)malloc(n_routines * sizeof(ut64)); if (!routines) { goto beach; } - ut8 *array_cursor = cursor + K_MIG_SUBSYSTEM_SIZE; - ut8 *end_array = array_cursor + n_routines * K_MIG_ROUTINE_SIZE; bool is_consistent = true; int idx = 0; while (array_cursor < end_array) {