darling-newlkm/darling/binfmt.c
2022-01-24 14:21:06 -05:00

958 lines
22 KiB
C

/*
* Darling Mach Linux Kernel Module
* Copyright (C) 2017-2018 Lubos Dolezel
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "binfmt.h"
#undef PAGE_MASK
#undef PAGE_SHIFT
#undef PAGE_SIZE
#include <mach-o/loader.h>
#include <mach-o/fat.h>
#undef __unused
#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0)
# include <linux/sched/task_stack.h>
#endif
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/uaccess.h>
#include <linux/string.h>
#include <asm/mman.h>
#include <asm/elf.h>
#include <linux/ptrace.h>
#include <linux/version.h>
#include <linux/coredump.h>
#include <linux/highmem.h>
#include <linux/mount.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,9)
#include <linux/sched/task_stack.h>
#endif
#include "debug_print.h"
#include "task_registry.h"
#include "commpage.h"
// To get LINUX_SIGRTMIN
#include <rtsig.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,11,0)
# define check_64bit_mode(regs) !test_thread_flag(TIF_IA32)
#else
# define check_64bit_mode(regs) any_64bit_mode(regs)
#endif
extern char* task_copy_vchroot_path(task_t t);
struct load_results
{
unsigned long mh;
unsigned long entry_point;
unsigned long stack_size;
unsigned long dyld_all_image_location;
unsigned long dyld_all_image_size;
uint8_t uuid[16];
unsigned long vm_addr_max;
bool _32on64;
int kernfd;
unsigned long base;
uint32_t bprefs[4];
char* root_path;
};
extern struct file* xnu_task_setup(void);
extern int commpage_install(struct file* xnu_task);
static int macho_load(struct linux_binprm* bprm);
static int macho_coredump(struct coredump_params* cprm);
static int test_load(struct linux_binprm* bprm);
static int test_load_fat(struct linux_binprm* bprm);
static int load_fat(struct linux_binprm* bprm, struct file* file, uint32_t arch, struct load_results* lr);
static int load32(struct linux_binprm* bprm, struct file* file, struct fat_arch* farch, bool expect_dylinker, struct load_results* lr);
static int load64(struct linux_binprm* bprm, struct file* file, struct fat_arch* farch, bool expect_dylinker, struct load_results* lr);
static int load(struct linux_binprm* bprm, struct file* file, uint32_t arch, struct load_results* lr);
static int native_prot(int prot);
static int setup_stack64(struct linux_binprm* bprm, struct load_results* lr);
static int setup_stack32(struct linux_binprm* bprm, struct load_results* lr);
static int setup_space(struct linux_binprm* bprm, struct load_results* lr);
static void process_special_env(struct linux_binprm* bprm, struct load_results* lr);
static void vchroot_detect(struct load_results* lr);
// #define PAGE_ALIGN(x) ((x) & ~(PAGE_SIZE-1))
#define PAGE_ROUNDUP(x) (((((x)-1) / PAGE_SIZE)+1) * PAGE_SIZE)
struct linux_binfmt macho_format = {
.module = THIS_MODULE,
.load_binary = macho_load,
.load_shlib = NULL,
#ifdef CONFIG_COREDUMP
.core_dump = macho_coredump,
#endif
.min_coredump = PAGE_SIZE
};
void macho_binfmt_init(void)
{
register_binfmt(&macho_format);
}
void macho_binfmt_exit(void)
{
unregister_binfmt(&macho_format);
}
int macho_load(struct linux_binprm* bprm)
{
int err;
struct load_results lr;
struct pt_regs* regs = current_pt_regs();
struct file* xnu_task;
// Zero this structure early
memset(&lr, 0, sizeof(lr));
// Do quick checks on the executable
err = test_load(bprm);
if (err)
goto out;
// Setup a new XNU task
xnu_task = xnu_task_setup();
if (IS_ERR(xnu_task))
{
err = PTR_ERR(xnu_task);
goto out;
}
// Block SIGNAL_SIGEXC_TOGGLE and SIGNAL_SIGEXC_THUPDATE.
// See sigexc.c in libsystem_kernel.
sigaddset(&current->blocked, LINUX_SIGRTMIN);
sigaddset(&current->blocked, LINUX_SIGRTMIN+1);
// Remove the running executable
// This is the point of no return.
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,8,0)
err = begin_new_exec(bprm);
#else
err = flush_old_exec(bprm);
#endif
if (err)
goto out;
err = load(bprm, bprm->file, 0, &lr);
if (err)
{
fput(xnu_task);
debug_msg("Binary failed to load: %d\n", err);
goto out;
}
set_binfmt(&macho_format);
current->mm->start_brk = current->mm->brk = PAGE_ALIGN(lr.vm_addr_max);
current->mm->start_stack = bprm->p;
// TODO: fill in start_code, end_code, start_data, end_data
// Map commpage
err = commpage_install(xnu_task);
lr.kernfd = get_unused_fd_flags(O_RDWR | O_CLOEXEC);
if (lr.kernfd >= 0)
fd_install(lr.kernfd, xnu_task);
else
err = lr.kernfd;
// The ref to the task is now held by the commpage mapping
// fput(xnu_task);
if (err != 0)
{
debug_msg("Failed to install commpage: %d\n", err);
send_sig(SIGKILL, current, 0);
return err;
}
// Setup the stack
if (lr._32on64)
setup_stack32(bprm, &lr);
else
setup_stack64(bprm, &lr);
// Set DYLD_INFO
darling_task_set_dyld_info(lr.dyld_all_image_location, lr.dyld_all_image_size);
// debug_msg("Entry point: %lx, stack: %lx, mh: %lx\n", (void*) lr.entry_point, (void*) bprm->p, (void*) lr.mh);
//unsigned int* pp = (unsigned int*)bprm->p;
//int i;
//for (i = 0; i < 30; i++)
//{
// debug_msg("sp @%p: 0x%x\n", pp, *pp);
// pp++;
//}
start_thread(regs, lr.entry_point, bprm->p);
out:
if (lr.root_path)
kfree(lr.root_path);
return err;
}
int setup_space(struct linux_binprm* bprm, struct load_results* lr)
{
int err;
// Explanation:
// Using STACK_TOP would cause the stack to be placed just above the commpage
// and would collide with it eventually.
unsigned long stackAddr = commpage_address(check_64bit_mode(current_pt_regs()));
setup_new_exec(bprm);
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,8,0)
install_exec_creds(bprm);
#endif
// TODO: Mach-O supports executable stacks
err = setup_arg_pages(bprm, stackAddr, EXSTACK_DISABLE_X);
if (err != 0)
return err;
// If vchroot is active in the current process, respect it
vchroot_detect(lr);
// Parse binprefs out of env, evaluate DYLD_ROOT_PATH
process_special_env(bprm, lr);
return 0;
}
static const char EXECUTABLE_PATH[] = "executable_path=";
int load(struct linux_binprm* bprm,
struct file* file,
uint32_t arch,
struct load_results* lr)
{
uint32_t magic = *(uint32_t*)bprm->buf;
if (magic == MH_MAGIC_64 || magic == MH_CIGAM_64)
{
// Make sure the loader has the right cputype
if (arch && ((struct mach_header*) bprm->buf)->cputype != arch)
return -ENOEXEC;
return load64(bprm, file, NULL, arch != 0, lr);
}
else if (magic == MH_MAGIC || magic == MH_CIGAM)
{
// Make sure the loader has the right cputype
if (arch && ((struct mach_header*) bprm->buf)->cputype != arch)
return -ENOEXEC;
// TODO: make process 32-bit
return load32(bprm, file, NULL, arch != 0, lr);
}
else if (magic == FAT_MAGIC || magic == FAT_CIGAM)
{
return load_fat(bprm, file, arch, lr);
}
else
return -ENOEXEC;
}
int test_load(struct linux_binprm* bprm)
{
uint32_t magic = *(uint32_t*)bprm->buf;
// TODO: This function should check if the dynamic loader is present and valid
if (magic == MH_MAGIC_64 || magic == MH_CIGAM_64)
{
struct mach_header_64* mh = (struct mach_header_64*) bprm->buf;
uint32_t filetype = mh->filetype;
// if (magic == MH_CIGAM_64)
// be32_to_cpus(&filetype);
if (filetype != MH_EXECUTE)
return -ENOEXEC;
#ifdef __x86_64__
uint32_t cputype = mh->cputype;
// if (magic == MH_CIGAM_64)
// be32_to_cpus(&cputype);
if ((cputype & ~CPU_ARCH_MASK) != CPU_TYPE_X86)
return -ENOEXEC;
#endif
return 0;
}
else if (magic == MH_MAGIC || magic == MH_CIGAM)
{
struct mach_header_64* mh = (struct mach_header_64*) bprm->buf;
if (mh->filetype != MH_EXECUTE)
return -ENOEXEC;
#ifdef __x86_64__
if ((mh->cputype & ~CPU_ARCH_MASK) != CPU_TYPE_X86)
return -ENOEXEC;
#endif
return 0;
}
else if (magic == FAT_MAGIC || magic == FAT_CIGAM)
{
return test_load_fat(bprm);
}
else
return -ENOEXEC;
}
int test_load_fat(struct linux_binprm* bprm)
{
struct fat_header* fhdr = (struct fat_header*) bprm->buf;
const bool swap = fhdr->magic == FAT_CIGAM;
u32 narch = fhdr->nfat_arch;
bool found_usable = false;
if (swap)
be32_to_cpus(&narch);
if (sizeof(*fhdr) + narch * sizeof(struct fat_arch) > sizeof(bprm->buf))
return -ENOEXEC;
uint32_t i;
for (i = 0; i < narch; i++)
{
struct fat_arch* arch;
u32 cputype;
arch = ((struct fat_arch*)(fhdr+1)) + i;
cputype = arch->cputype;
if (swap)
be32_to_cpus(&cputype);
#ifdef __x86_64__
if ((cputype & ~CPU_ARCH_MASK) == CPU_TYPE_X86)
{
found_usable = true;
break;
}
#endif
}
if (!found_usable)
return -ENOEXEC;
return 0;
}
int load_fat(struct linux_binprm* bprm,
struct file* file,
uint32_t forced_arch,
struct load_results* lr)
{
struct fat_header* fhdr = (struct fat_header*) bprm->buf;
const bool swap = fhdr->magic == FAT_CIGAM;
struct fat_arch* best_arch = NULL;
int bpref_index = -1;
// Here we assume that our current endianess is LE
// which is actually true for all of Darling's supported archs.
#define SWAP32(x) be32_to_cpus((u32*) &(x))
if (swap)
SWAP32(fhdr->nfat_arch);
if (sizeof(*fhdr) + fhdr->nfat_arch * sizeof(struct fat_arch) > sizeof(bprm->buf))
return -ENOEXEC;
uint32_t i;
for (i = 0; i < fhdr->nfat_arch; i++)
{
struct fat_arch* arch;
arch = ((struct fat_arch*)(fhdr+1)) + i;
if (swap)
{
SWAP32(arch->cputype);
SWAP32(arch->cpusubtype);
SWAP32(arch->offset);
SWAP32(arch->size);
SWAP32(arch->align);
}
if (!forced_arch)
{
int j;
for (j = 0; j < 4; j++)
{
if (lr->bprefs[j] && arch->cputype == lr->bprefs[j])
{
if (bpref_index == -1 || bpref_index > j)
{
best_arch = arch;
bpref_index = j;
break;
}
}
}
if (bpref_index == -1)
{
#if defined(__x86_64__)
if (arch->cputype == CPU_TYPE_X86_64)
best_arch = arch;
else if (best_arch == NULL && arch->cputype == CPU_TYPE_X86)
best_arch = arch;
#elif defined (__aarch64__)
#warning TODO: arm
#else
#error Unsupported CPU architecture
#endif
}
}
else
{
if (arch->cputype == forced_arch)
best_arch = arch;
}
}
if (best_arch == NULL)
return -ENOEXEC;
if (best_arch->cputype & CPU_ARCH_ABI64)
return load64(bprm, file, best_arch, forced_arch != 0, lr);
else
return load32(bprm, file, best_arch, forced_arch != 0, lr);
}
#define GEN_64BIT
#include "binfmt_loader.c"
#include "binfmt_stack.c"
#undef GEN_64BIT
#define GEN_32BIT
#include "binfmt_loader.c"
#include "binfmt_stack.c"
#undef GEN_32BIT
int native_prot(int prot)
{
int protOut = 0;
if (prot & VM_PROT_READ)
protOut |= PROT_READ;
if (prot & VM_PROT_WRITE)
protOut |= PROT_WRITE;
if (prot & VM_PROT_EXECUTE)
protOut |= PROT_EXEC;
return protOut;
}
void vchroot_detect(struct load_results* lr)
{
// Find out if the current process has an associated XNU task_t
task_t task = darling_task_get_current();
if (task)
lr->root_path = task_copy_vchroot_path(task);
}
// Given that there's no proper way of passing special parameters to the binary loader
// via execve(), we must do this via env variables
void process_special_env(struct linux_binprm* bprm, struct load_results* lr)
{
unsigned long p = current->mm->arg_start;
// Get past argv strings
int argc = bprm->argc;
while (argc--)
{
size_t len = strnlen_user((void __user*) p, MAX_ARG_STRLEN);
if (!len || len > MAX_ARG_STRLEN)
return;
p += len;
}
int envc = bprm->envc;
char* env_value = (char*) kmalloc(MAX_ARG_STRLEN, GFP_KERNEL);
while (envc--)
{
size_t len = strnlen_user((void __user*) p, MAX_ARG_STRLEN);
if (!len || len > MAX_ARG_STRLEN)
break;
if (copy_from_user(env_value, (void __user*) p, len) == 0)
{
printk(KERN_NOTICE "env var: %s\n", env_value);
if (strncmp(env_value, "__mldr_bprefs=", 14) == 0)
{
// NOTE: This env var will not be passed to userland, see setup_stack32/64
sscanf(env_value+14, "%x,%x,%x,%x", &lr->bprefs[0], &lr->bprefs[1], &lr->bprefs[2], &lr->bprefs[3]);
}
else if (strncmp(env_value, "DYLD_ROOT_PATH=", 15) == 0)
{
if (lr->root_path == NULL)
{
// len already includes NUL
lr->root_path = (char*) kmalloc(len - 15, GFP_KERNEL);
if (lr->root_path)
strcpy(lr->root_path, env_value + 15);
}
}
}
else
printk(KERN_NOTICE "Cannot get env var value\n");
p += len;
}
kfree(env_value);
}
// Copied from arch/x86/kernel/process_64.c
// Why on earth isn't this exported?!
#ifdef __x86_64__
static void
start_thread_common(struct pt_regs *regs, unsigned long new_ip,
unsigned long new_sp,
unsigned int _cs, unsigned int _ss, unsigned int _ds)
{
loadsegment(fs, 0);
loadsegment(es, _ds);
loadsegment(ds, _ds);
load_gs_index(0);
regs->ip = new_ip;
regs->sp = new_sp;
regs->cs = _cs;
regs->ss = _ss;
regs->flags = X86_EFLAGS_IF;
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,6,0)
force_iret();
#endif
}
void
start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp)
{
bool ia32 = !check_64bit_mode(regs);
start_thread_common(regs, new_ip, new_sp,
ia32 ? __USER32_CS : __USER_CS,
__USER_DS,
ia32 ? __USER_DS : 0);
}
#endif
//////////////////////////////////////////////////////////////////////////////
// CORE DUMPING SUPPORT //
//////////////////////////////////////////////////////////////////////////////
#ifdef CONFIG_COREDUMP
// Copied and adapted from mm/gup.c from get_dump_page() (not exported for LKMs)
static
struct page *macho_get_dump_page(unsigned long addr)
{
struct vm_area_struct *vma;
struct page *page;
if (get_user_pages(addr, 1, FOLL_FORCE | FOLL_DUMP | FOLL_GET, &page, &vma) < 1)
return NULL;
flush_cache_page(vma, addr, page_to_pfn(page));
return page;
}
struct thread_flavor
{
// preceded by struct thread_command and other flavors
uint32_t flavor;
uint32_t count;
char state[0];
// followed by x86_thread_state32_t, for example
};
static
void fill_thread_state32(x86_thread_state32_t* state, struct task_struct* task)
{
const struct pt_regs* regs = task_pt_regs(task);
state->eax = regs->ax;
state->ebx = regs->bx;
state->ecx = regs->cx;
state->edx = regs->dx;
state->esi = regs->si;
state->edi = regs->di;
state->eip = regs->ip;
state->ebp = regs->bp;
state->esp = regs->sp;
state->ss = regs->ss;
state->eflags = regs->flags;
state->cs = regs->cs;
}
static
void fill_float_state32(x86_float_state32_t* state, struct task_struct* task)
{
// TODO
memset(state, 0, sizeof(*state));
}
static
void fill_thread_state64(x86_thread_state64_t* state, struct task_struct* task)
{
const struct pt_regs* regs = task_pt_regs(task);
state->rax = regs->ax;
state->rbx = regs->bx;
state->rcx = regs->cx;
state->rdx = regs->dx;
state->rdi = regs->di;
state->rsi = regs->si;
state->rbp = regs->bp;
state->rsp = regs->sp;
state->r8 = regs->r8;
state->r9 = regs->r9;
state->r10 = regs->r10;
state->r11 = regs->r11;
state->r12 = regs->r12;
state->r13 = regs->r13;
state->r14 = regs->r14;
state->r15 = regs->r15;
state->rflags = regs->flags;
state->cs = regs->cs;
state->rip = regs->ip;
}
static
void fill_float_state64(x86_float_state64_t* state, struct task_struct* task)
{
// TODO
memset(state, 0, sizeof(*state));
}
static
bool macho_dump_headers32(struct coredump_params* cprm)
{
// Count memory segments and threads
unsigned int segs = current->mm->map_count;
unsigned int threads = 0; // = atomic_read(&current->mm->core_state->nr_threads); // doesn't seem to work?
struct core_thread* ct;
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,16,0)
struct core_state* cs = current->mm->core_state;
#else
struct core_state* cs = current->signal->core_state;
#endif
for (ct = &cs->dumper; ct != NULL; ct = ct->next)
threads++;
struct mach_header mh;
mh.magic = MH_MAGIC;
#ifdef __x86_64__
mh.cputype = CPU_TYPE_X86;
mh.cpusubtype = CPU_SUBTYPE_X86_ALL;
#else
#warning Missing code for this arch
#endif
mh.filetype = MH_CORE;
mh.ncmds = segs + threads;
const int statesize = sizeof(x86_thread_state32_t) + sizeof(x86_float_state32_t) + sizeof(struct thread_flavor)*2;
mh.sizeofcmds = segs * sizeof(struct segment_command) + threads * (sizeof(struct thread_command) + statesize);
if (!dump_emit(cprm, &mh, sizeof(mh)))
goto fail;
struct vm_area_struct* vma;
uint32_t file_offset = mh.sizeofcmds + sizeof(mh);
for (vma = current->mm->mmap; vma != NULL; vma = vma->vm_next)
{
struct segment_command sc;
sc.cmd = LC_SEGMENT;
sc.cmdsize = sizeof(sc);
sc.segname[0] = 0;
sc.nsects = 0;
sc.flags = 0;
sc.vmaddr = vma->vm_start;
sc.vmsize = vma->vm_end - vma->vm_start;
sc.fileoff = file_offset;
if (sc.vmaddr > 0) // avoid dumping the __PAGEZERO segment which may be really large
sc.filesize = sc.vmsize;
else
sc.filesize = 0;
sc.initprot = 0;
if (vma->vm_flags & VM_READ)
sc.initprot |= VM_PROT_READ;
if (vma->vm_flags & VM_WRITE)
sc.initprot |= VM_PROT_WRITE;
if (vma->vm_flags & VM_EXEC)
sc.initprot |= VM_PROT_EXECUTE;
sc.maxprot = sc.initprot;
if (!dump_emit(cprm, &sc, sizeof(sc)))
goto fail;
file_offset += sc.filesize;
}
const int memsize = sizeof(struct thread_command) + statesize;
uint8_t* buffer = kmalloc(memsize, GFP_KERNEL);
for (ct = &cs->dumper; ct != NULL; ct = ct->next)
{
struct thread_command* tc = (struct thread_command*) buffer;
struct thread_flavor* tf = (struct thread_flavor*)(tc+1);
tc->cmd = LC_THREAD;
tc->cmdsize = memsize;
// General registers
tf->flavor = x86_THREAD_STATE32;
tf->count = x86_THREAD_STATE32_COUNT;
fill_thread_state32((x86_thread_state32_t*) tf->state, ct->task);
// Float registers
tf = (struct thread_flavor*) (((char*) tf) + sizeof(x86_thread_state32_t));
tf->flavor = x86_FLOAT_STATE32;
tf->count = x86_FLOAT_STATE32_COUNT;
fill_float_state32((x86_float_state32_t*) tf->state, ct->task);
if (!dump_emit(cprm, buffer, memsize))
{
kfree(buffer);
goto fail;
}
}
kfree(buffer);
return true;
fail:
return false;
}
static
bool macho_dump_headers64(struct coredump_params* cprm)
{
// Count memory segments and threads
unsigned int segs = current->mm->map_count;
unsigned int threads = 0; // = atomic_read(&current->mm->core_state->nr_threads); // doesn't seem to work?
struct core_thread* ct;
struct mach_header_64 mh;
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,16,0)
struct core_state* cs = current->mm->core_state;
#else
struct core_state* cs = current->signal->core_state;
#endif
for (ct = &cs->dumper; ct != NULL; ct = ct->next)
threads++;
mh.magic = MH_MAGIC_64;
#ifdef __x86_64__
mh.cputype = CPU_TYPE_X86_64;
mh.cpusubtype = CPU_SUBTYPE_X86_64_ALL;
#else
#warning Missing code for this arch
#endif
mh.filetype = MH_CORE;
mh.ncmds = segs + threads;
debug_msg("CORE: threads: %d\n", threads);
const int statesize = sizeof(x86_thread_state32_t) + sizeof(x86_float_state64_t) + sizeof(struct thread_flavor)*2;
mh.sizeofcmds = segs * sizeof(struct segment_command_64) + threads * (sizeof(struct thread_command) + statesize);
mh.reserved = 0;
if (!dump_emit(cprm, &mh, sizeof(mh)))
goto fail;
struct vm_area_struct* vma;
uint32_t file_offset = mh.sizeofcmds + sizeof(mh);
for (vma = current->mm->mmap; vma != NULL; vma = vma->vm_next)
{
struct segment_command_64 sc;
sc.cmd = LC_SEGMENT_64;
sc.cmdsize = sizeof(sc);
sc.segname[0] = 0;
sc.nsects = 0;
sc.flags = 0;
sc.vmaddr = vma->vm_start;
sc.vmsize = vma->vm_end - vma->vm_start;
sc.fileoff = file_offset;
if (sc.vmaddr > 0) // avoid dumping the __PAGEZERO segment which may be really large
sc.filesize = sc.vmsize;
else
sc.filesize = 0;
sc.initprot = 0;
if (vma->vm_flags & VM_READ)
sc.initprot |= VM_PROT_READ;
if (vma->vm_flags & VM_WRITE)
sc.initprot |= VM_PROT_WRITE;
if (vma->vm_flags & VM_EXEC)
sc.initprot |= VM_PROT_EXECUTE;
sc.maxprot = sc.initprot;
if (!dump_emit(cprm, &sc, sizeof(sc)))
goto fail;
file_offset += sc.filesize;
}
const int memsize = sizeof(struct thread_command) + statesize;
uint8_t* buffer = kmalloc(memsize, GFP_KERNEL);
for (ct = &cs->dumper; ct != NULL; ct = ct->next)
{
struct thread_command* tc = (struct thread_command*) buffer;
struct thread_flavor* tf = (struct thread_flavor*)(tc+1);
tc->cmd = LC_THREAD;
tc->cmdsize = memsize;
// General registers
tf->flavor = x86_THREAD_STATE64;
tf->count = x86_THREAD_STATE64_COUNT;
fill_thread_state64((x86_thread_state64_t*) tf->state, ct->task);
// Float registers
tf = (struct thread_flavor*) (tf->state + sizeof(x86_thread_state64_t));
tf->flavor = x86_FLOAT_STATE64;
tf->count = x86_FLOAT_STATE64_COUNT;
fill_float_state64((x86_float_state64_t*) tf->state, ct->task);
if (!dump_emit(cprm, buffer, memsize))
{
kfree(buffer);
goto fail;
}
}
kfree(buffer);
return true;
fail:
return false;
}
int macho_coredump(struct coredump_params* cprm)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,8,0)
mm_segment_t fs = get_fs();
set_fs(KERNEL_DS);
#endif
// Write the Mach-O header and loader commands
if (!check_64bit_mode(current_pt_regs()))
{
// 32-bit executables
if (!macho_dump_headers32(cprm))
goto fail;
}
else
{
// 64-bit executables
if (!macho_dump_headers64(cprm))
goto fail;
}
// Dump memory contents
struct vm_area_struct* vma;
// Inspired by elf_core_dump()
for (vma = current->mm->mmap; vma != NULL; vma = vma->vm_next)
{
unsigned long addr;
if (vma->vm_start == 0)
continue; // skip __PAGEZERO dumping
for (addr = vma->vm_start; addr < vma->vm_end; addr += PAGE_SIZE)
{
struct page* page;
bool stop;
page = macho_get_dump_page(addr);
if (page)
{
void* kaddr = kmap(page);
stop = !dump_emit(cprm, kaddr, PAGE_SIZE);
kunmap(page);
put_page(page);
}
else
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,13,0)
dump_skip(cprm, PAGE_SIZE);
#else
stop = !dump_skip(cprm, PAGE_SIZE);
#endif
if (stop)
goto fail;
}
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,13,0)
dump_truncate(cprm);
#endif
fail:
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,8,0)
set_fs(fs);
#endif
return cprm->written > 0;
}
#else
#warning Core dumping not allowed by kernel config
#endif