diff --git a/tools/libxc/Makefile b/tools/libxc/Makefile index 876c148..1a5249c 100644 --- a/tools/libxc/Makefile +++ b/tools/libxc/Makefile @@ -52,8 +52,13 @@ endif vpath %.c ../../xen/common/libelf CFLAGS += -I../../xen/common/libelf -GUEST_SRCS-y += libelf-tools.c libelf-loader.c -GUEST_SRCS-y += libelf-dominfo.c libelf-relocate.c +ELF_SRCS-y += libelf-tools.c libelf-loader.c +ELF_SRCS-y += libelf-dominfo.c + +GUEST_SRCS-y += $(ELF_SRCS-y) + +$(patsubst %.c,%.o,$(ELF_SRCS-y)): CFLAGS += -Wno-pointer-sign +$(patsubst %.c,%.opic,$(ELF_SRCS-y)): CFLAGS += -Wno-pointer-sign # new domain builder GUEST_SRCS-y += xc_dom_core.c xc_dom_boot.c diff --git a/tools/libxc/ia64/xc_ia64_dom_fwloader.c b/tools/libxc/ia64/xc_ia64_dom_fwloader.c index cdf3333..dbd3349 100644 --- a/tools/libxc/ia64/xc_ia64_dom_fwloader.c +++ b/tools/libxc/ia64/xc_ia64_dom_fwloader.c @@ -60,6 +60,8 @@ static int xc_dom_load_fw_kernel(struct xc_dom_image *dom) unsigned long i; dest = xc_dom_vaddr_to_ptr(dom, dom->kernel_seg.vstart); + if ( dest == NULL ) + return -1; memcpy(dest, dom->kernel_blob, FW_SIZE); /* Synchronize cache. */ diff --git a/tools/libxc/xc_cpuid_x86.c b/tools/libxc/xc_cpuid_x86.c index 0882ce6..da435ce 100644 --- a/tools/libxc/xc_cpuid_x86.c +++ b/tools/libxc/xc_cpuid_x86.c @@ -589,6 +589,8 @@ static int xc_cpuid_do_domctl( static char *alloc_str(void) { char *s = malloc(33); + if ( s == NULL ) + return s; memset(s, 0, 33); return s; } @@ -600,6 +602,8 @@ void xc_cpuid_to_str(const unsigned int *regs, char **strs) for ( i = 0; i < 4; i++ ) { strs[i] = alloc_str(); + if ( strs[i] == NULL ) + continue; for ( j = 0; j < 32; j++ ) strs[i][j] = !!((regs[i] & (1U << (31 - j)))) ? '1' : '0'; } @@ -680,7 +684,7 @@ int xc_cpuid_check( const char **config, char **config_transformed) { - int i, j; + int i, j, rc; unsigned int regs[4]; memset(config_transformed, 0, 4 * sizeof(*config_transformed)); @@ -692,6 +696,11 @@ int xc_cpuid_check( if ( config[i] == NULL ) continue; config_transformed[i] = alloc_str(); + if ( config_transformed[i] == NULL ) + { + rc = -ENOMEM; + goto fail_rc; + } for ( j = 0; j < 32; j++ ) { unsigned char val = !!((regs[i] & (1U << (31 - j)))); @@ -708,12 +717,14 @@ int xc_cpuid_check( return 0; fail: + rc = -EPERM; + fail_rc: for ( i = 0; i < 4; i++ ) { free(config_transformed[i]); config_transformed[i] = NULL; } - return -EPERM; + return rc; } /* @@ -758,6 +769,11 @@ int xc_cpuid_set( } config_transformed[i] = alloc_str(); + if ( config_transformed[i] == NULL ) + { + rc = -ENOMEM; + goto fail; + } for ( j = 0; j < 32; j++ ) { diff --git a/tools/libxc/xc_dom.h b/tools/libxc/xc_dom.h index 6a72aa9..d801f66 100644 --- a/tools/libxc/xc_dom.h +++ b/tools/libxc/xc_dom.h @@ -140,9 +140,10 @@ struct xc_dom_image { struct xc_dom_loader { char *name; - int (*probe) (struct xc_dom_image * dom); - int (*parser) (struct xc_dom_image * dom); - int (*loader) (struct xc_dom_image * dom); + /* Sadly the error returns from these functions are not consistent: */ + elf_negerrnoval (*probe) (struct xc_dom_image * dom); + elf_negerrnoval (*parser) (struct xc_dom_image * dom); + elf_errorstatus (*loader) (struct xc_dom_image * dom); struct xc_dom_loader *next; }; @@ -275,27 +276,50 @@ int xc_dom_alloc_segment(struct xc_dom_image *dom, void *xc_dom_pfn_to_ptr(struct xc_dom_image *dom, xen_pfn_t first, xen_pfn_t count); +void *xc_dom_pfn_to_ptr_retcount(struct xc_dom_image *dom, xen_pfn_t first, + xen_pfn_t count, xen_pfn_t *count_out); void xc_dom_unmap_one(struct xc_dom_image *dom, xen_pfn_t pfn); void xc_dom_unmap_all(struct xc_dom_image *dom); -static inline void *xc_dom_seg_to_ptr(struct xc_dom_image *dom, - struct xc_dom_seg *seg) +static inline void *xc_dom_seg_to_ptr_pages(struct xc_dom_image *dom, + struct xc_dom_seg *seg, + xen_pfn_t *pages_out) { xen_vaddr_t segsize = seg->vend - seg->vstart; unsigned int page_size = XC_DOM_PAGE_SIZE(dom); xen_pfn_t pages = (segsize + page_size - 1) / page_size; + void *retval; + + retval = xc_dom_pfn_to_ptr(dom, seg->pfn, pages); - return xc_dom_pfn_to_ptr(dom, seg->pfn, pages); + *pages_out = retval ? pages : 0; + return retval; +} + +static inline void *xc_dom_seg_to_ptr(struct xc_dom_image *dom, + struct xc_dom_seg *seg) +{ + xen_pfn_t dummy; + + return xc_dom_seg_to_ptr_pages(dom, seg, &dummy); } static inline void *xc_dom_vaddr_to_ptr(struct xc_dom_image *dom, - xen_vaddr_t vaddr) + xen_vaddr_t vaddr, + size_t *safe_region_out) { unsigned int page_size = XC_DOM_PAGE_SIZE(dom); xen_pfn_t page = (vaddr - dom->parms.virt_base) / page_size; unsigned int offset = (vaddr - dom->parms.virt_base) % page_size; - void *ptr = xc_dom_pfn_to_ptr(dom, page, 0); - return (ptr ? (ptr + offset) : NULL); + xen_pfn_t safe_region_count; + void *ptr; + + *safe_region_out = 0; + ptr = xc_dom_pfn_to_ptr_retcount(dom, page, 0, &safe_region_count); + if ( ptr == NULL ) + return ptr; + *safe_region_out = (safe_region_count << XC_DOM_PAGE_SHIFT(dom)) - offset; + return ptr; } static inline int xc_dom_feature_translated(struct xc_dom_image *dom) @@ -307,6 +331,8 @@ static inline xen_pfn_t xc_dom_p2m_host(struct xc_dom_image *dom, xen_pfn_t pfn) { if (dom->shadow_enabled) return pfn; + if (pfn >= dom->total_pages) + return INVALID_MFN; return dom->p2m_host[pfn]; } @@ -315,6 +341,8 @@ static inline xen_pfn_t xc_dom_p2m_guest(struct xc_dom_image *dom, { if (xc_dom_feature_translated(dom)) return pfn; + if (pfn >= dom->total_pages) + return INVALID_MFN; return dom->p2m_host[pfn]; } diff --git a/tools/libxc/xc_dom_binloader.c b/tools/libxc/xc_dom_binloader.c index 769e97d..553b366 100644 --- a/tools/libxc/xc_dom_binloader.c +++ b/tools/libxc/xc_dom_binloader.c @@ -123,10 +123,13 @@ static struct xen_bin_image_table *find_table(struct xc_dom_image *dom) uint32_t *probe_ptr; uint32_t *probe_end; + if ( dom->kernel_size < sizeof(*table) ) + return NULL; probe_ptr = dom->kernel_blob; - probe_end = dom->kernel_blob + dom->kernel_size - sizeof(*table); - if ( (void*)probe_end > (dom->kernel_blob + 8192) ) + if ( dom->kernel_size > (8192 + sizeof(*table)) ) probe_end = dom->kernel_blob + 8192; + else + probe_end = dom->kernel_blob + dom->kernel_size - sizeof(*table); for ( table = NULL; probe_ptr < probe_end; probe_ptr++ ) { @@ -249,6 +252,7 @@ static int xc_dom_load_bin_kernel(struct xc_dom_image *dom) char *image = dom->kernel_blob; char *dest; size_t image_size = dom->kernel_size; + size_t dest_size; uint32_t start_addr; uint32_t load_end_addr; uint32_t bss_end_addr; @@ -272,7 +276,29 @@ static int xc_dom_load_bin_kernel(struct xc_dom_image *dom) DOMPRINTF(" text_size: 0x%" PRIx32 "", text_size); DOMPRINTF(" bss_size: 0x%" PRIx32 "", bss_size); - dest = xc_dom_vaddr_to_ptr(dom, dom->kernel_seg.vstart); + dest = xc_dom_vaddr_to_ptr(dom, dom->kernel_seg.vstart, &dest_size); + if ( dest == NULL ) + { + DOMPRINTF("%s: xc_dom_vaddr_to_ptr(dom, dom->kernel_seg.vstart)" + " => NULL", __FUNCTION__); + return -EINVAL; + } + + if ( dest_size < text_size || + dest_size - text_size < bss_size ) + { + DOMPRINTF("%s: mapped region is too small for image", __FUNCTION__); + return -EINVAL; + } + + if ( image_size < skip || + image_size - skip < text_size ) + { + DOMPRINTF("%s: image is too small for declared text size", + __FUNCTION__); + return -EINVAL; + } + memcpy(dest, image + skip, text_size); memset(dest + text_size, 0, bss_size); diff --git a/tools/libxc/xc_dom_core.c b/tools/libxc/xc_dom_core.c index 2a01d7c..e79e38d 100644 --- a/tools/libxc/xc_dom_core.c +++ b/tools/libxc/xc_dom_core.c @@ -120,9 +120,17 @@ void *xc_dom_malloc(struct xc_dom_image *dom, size_t size) { struct xc_dom_mem *block; + if ( size > SIZE_MAX - sizeof(*block) ) + { + DOMPRINTF("%s: unreasonable allocation size", __FUNCTION__); + return NULL; + } block = malloc(sizeof(*block) + size); if ( block == NULL ) + { + DOMPRINTF("%s: allocation failed", __FUNCTION__); return NULL; + } memset(block, 0, sizeof(*block) + size); block->next = dom->memblocks; dom->memblocks = block; @@ -138,7 +146,10 @@ void *xc_dom_malloc_page_aligned(struct xc_dom_image *dom, size_t size) block = malloc(sizeof(*block)); if ( block == NULL ) + { + DOMPRINTF("%s: allocation failed", __FUNCTION__); return NULL; + } memset(block, 0, sizeof(*block)); block->mmap_len = size; block->mmap_ptr = mmap(NULL, block->mmap_len, @@ -146,6 +157,7 @@ void *xc_dom_malloc_page_aligned(struct xc_dom_image *dom, size_t size) -1, 0); if ( block->mmap_ptr == MAP_FAILED ) { + DOMPRINTF("%s: mmap failed", __FUNCTION__); free(block); return NULL; } @@ -202,6 +214,7 @@ void *xc_dom_malloc_filemap(struct xc_dom_image *dom, close(fd); if ( block != NULL ) free(block); + DOMPRINTF("%s: failed (on file `%s')", __FUNCTION__, filename); return NULL; } @@ -271,6 +284,11 @@ size_t xc_dom_check_gzip(xc_interface *xch, void *blob, size_t ziplen) unsigned char *gzlen; size_t unziplen; + if ( ziplen < 6 ) + /* Too small. We need (i.e. the subsequent code relies on) + * 2 bytes for the magic number plus 4 bytes length. */ + return 0; + if ( strncmp(blob, "\037\213", 2) ) /* not gzipped */ return 0; @@ -351,10 +369,19 @@ int xc_dom_try_gunzip(struct xc_dom_image *dom, void **blob, size_t * size) void *xc_dom_pfn_to_ptr(struct xc_dom_image *dom, xen_pfn_t pfn, xen_pfn_t count) { + xen_pfn_t count_out_dummy; + return xc_dom_pfn_to_ptr_retcount(dom, pfn, count, &count_out_dummy); +} + +void *xc_dom_pfn_to_ptr_retcount(struct xc_dom_image *dom, xen_pfn_t pfn, + xen_pfn_t count, xen_pfn_t *count_out) +{ struct xc_dom_phys *phys; unsigned int page_shift = XC_DOM_PAGE_SHIFT(dom); char *mode = "unset"; + *count_out = 0; + if ( pfn > dom->total_pages || /* multiple checks to avoid overflows */ count > dom->total_pages || pfn > dom->total_pages - count ) @@ -384,6 +411,7 @@ void *xc_dom_pfn_to_ptr(struct xc_dom_image *dom, xen_pfn_t pfn, phys->count); return NULL; } + *count_out = count; } else { @@ -391,6 +419,9 @@ void *xc_dom_pfn_to_ptr(struct xc_dom_image *dom, xen_pfn_t pfn, just hand out a pointer to it */ if ( pfn < phys->first ) continue; + if ( pfn >= phys->first + phys->count ) + continue; + *count_out = phys->count - (pfn - phys->first); } return phys->ptr + ((pfn - phys->first) << page_shift); } @@ -478,7 +509,8 @@ int xc_dom_alloc_segment(struct xc_dom_image *dom, seg->vstart = start; seg->pfn = (seg->vstart - dom->parms.virt_base) / page_size; - if ( pages > dom->total_pages || /* double test avoids overflow probs */ + if ( pages > dom->total_pages || /* multiple test avoids overflow probs */ + seg->pfn > dom->total_pages || pages > dom->total_pages - seg->pfn) { xc_dom_panic(dom->xch, XC_OUT_OF_MEMORY, @@ -855,6 +887,12 @@ int xc_dom_build_image(struct xc_dom_image *dom) ramdisklen) != 0 ) goto err; ramdiskmap = xc_dom_seg_to_ptr(dom, &dom->ramdisk_seg); + if ( ramdiskmap == NULL ) + { + DOMPRINTF("%s: xc_dom_seg_to_ptr(dom, &dom->ramdisk_seg) => NULL", + __FUNCTION__); + goto err; + } if ( unziplen ) { if ( xc_dom_do_gunzip(dom->xch, diff --git a/tools/libxc/xc_dom_elfloader.c b/tools/libxc/xc_dom_elfloader.c index 2e69559..be58276 100644 --- a/tools/libxc/xc_dom_elfloader.c +++ b/tools/libxc/xc_dom_elfloader.c @@ -28,13 +28,14 @@ #include "xg_private.h" #include "xc_dom.h" +#include "xc_bitops.h" #define XEN_VER "xen-3.0" /* ------------------------------------------------------------------------ */ static void log_callback(struct elf_binary *elf, void *caller_data, - int iserr, const char *fmt, va_list al) { + bool iserr, const char *fmt, va_list al) { xc_interface *xch = caller_data; xc_reportv(xch, @@ -46,7 +47,7 @@ static void log_callback(struct elf_binary *elf, void *caller_data, void xc_elf_set_logfile(xc_interface *xch, struct elf_binary *elf, int verbose) { - elf_set_log(elf, log_callback, xch, verbose); + elf_set_log(elf, log_callback, xch, verbose /* convert to bool */); } /* ------------------------------------------------------------------------ */ @@ -84,7 +85,7 @@ static char *xc_dom_guest_type(struct xc_dom_image *dom, /* ------------------------------------------------------------------------ */ /* parse elf binary */ -static int check_elf_kernel(struct xc_dom_image *dom, int verbose) +static elf_negerrnoval check_elf_kernel(struct xc_dom_image *dom, bool verbose) { if ( dom->kernel_blob == NULL ) { @@ -95,7 +96,7 @@ static int check_elf_kernel(struct xc_dom_image *dom, int verbose) return -EINVAL; } - if ( !elf_is_elfbinary(dom->kernel_blob) ) + if ( !elf_is_elfbinary(dom->kernel_blob, dom->kernel_size) ) { if ( verbose ) xc_dom_panic(dom->xch, @@ -106,20 +107,21 @@ static int check_elf_kernel(struct xc_dom_image *dom, int verbose) return 0; } -static int xc_dom_probe_elf_kernel(struct xc_dom_image *dom) +static elf_negerrnoval xc_dom_probe_elf_kernel(struct xc_dom_image *dom) { return check_elf_kernel(dom, 0); } -static int xc_dom_load_elf_symtab(struct xc_dom_image *dom, - struct elf_binary *elf, int load) +static elf_errorstatus xc_dom_load_elf_symtab(struct xc_dom_image *dom, + struct elf_binary *elf, bool load) { struct elf_binary syms; - const elf_shdr *shdr, *shdr2; + ELF_HANDLE_DECL(elf_shdr) shdr; ELF_HANDLE_DECL(elf_shdr) shdr2; xen_vaddr_t symtab, maxaddr; - char *hdr; + elf_ptrval hdr; size_t size; - int h, count, type, i, tables = 0; + unsigned h, count, type, i, tables = 0; + unsigned long *strtab_referenced = NULL; if ( elf_swap(elf) ) { @@ -130,31 +132,48 @@ static int xc_dom_load_elf_symtab(struct xc_dom_image *dom, if ( load ) { + char *hdr_ptr; + size_t allow_size; + if ( !dom->bsd_symtab_start ) return 0; size = dom->kernel_seg.vend - dom->bsd_symtab_start; - hdr = xc_dom_vaddr_to_ptr(dom, dom->bsd_symtab_start); - *(int *)hdr = size - sizeof(int); + hdr_ptr = xc_dom_vaddr_to_ptr(dom, dom->bsd_symtab_start, &allow_size); + if ( hdr_ptr == NULL ) + { + DOMPRINTF("%s/load: xc_dom_vaddr_to_ptr(dom,dom->bsd_symtab_start" + " => NULL", __FUNCTION__); + return -1; + } + elf->caller_xdest_base = hdr_ptr; + elf->caller_xdest_size = allow_size; + hdr = ELF_REALPTR2PTRVAL(hdr_ptr); + elf_store_val(elf, unsigned, hdr, size - sizeof(unsigned)); } else { - size = sizeof(int) + elf_size(elf, elf->ehdr) + + char *hdr_ptr; + + size = sizeof(unsigned) + elf_size(elf, elf->ehdr) + elf_shdr_count(elf) * elf_size(elf, shdr); - hdr = xc_dom_malloc(dom, size); - if ( hdr == NULL ) + hdr_ptr = xc_dom_malloc(dom, size); + if ( hdr_ptr == NULL ) return 0; - dom->bsd_symtab_start = elf_round_up(&syms, dom->kernel_seg.vend); + elf->caller_xdest_base = hdr_ptr; + elf->caller_xdest_size = size; + hdr = ELF_REALPTR2PTRVAL(hdr_ptr); + dom->bsd_symtab_start = elf_round_up(elf, dom->kernel_seg.vend); } - memcpy(hdr + sizeof(int), - elf->image, + elf_memcpy_safe(elf, hdr + sizeof(unsigned), + ELF_IMAGE_BASE(elf), elf_size(elf, elf->ehdr)); - memcpy(hdr + sizeof(int) + elf_size(elf, elf->ehdr), - elf->image + elf_uval(elf, elf->ehdr, e_shoff), + elf_memcpy_safe(elf, hdr + sizeof(unsigned) + elf_size(elf, elf->ehdr), + ELF_IMAGE_BASE(elf) + elf_uval(elf, elf->ehdr, e_shoff), elf_shdr_count(elf) * elf_size(elf, shdr)); if ( elf_64bit(elf) ) { - Elf64_Ehdr *ehdr = (Elf64_Ehdr *)(hdr + sizeof(int)); + Elf64_Ehdr *ehdr = (Elf64_Ehdr *)(hdr + sizeof(unsigned)); ehdr->e_phoff = 0; ehdr->e_phentsize = 0; ehdr->e_phnum = 0; @@ -163,19 +182,42 @@ static int xc_dom_load_elf_symtab(struct xc_dom_image *dom, } else { - Elf32_Ehdr *ehdr = (Elf32_Ehdr *)(hdr + sizeof(int)); + Elf32_Ehdr *ehdr = (Elf32_Ehdr *)(hdr + sizeof(unsigned)); ehdr->e_phoff = 0; ehdr->e_phentsize = 0; ehdr->e_phnum = 0; ehdr->e_shoff = elf_size(elf, elf->ehdr); ehdr->e_shstrndx = SHN_UNDEF; } - if ( elf_init(&syms, hdr + sizeof(int), size - sizeof(int)) ) + if ( elf->caller_xdest_size < sizeof(unsigned) ) + { + DOMPRINTF("%s/%s: header size %"PRIx64" too small", + __FUNCTION__, load ? "load" : "parse", + (uint64_t)elf->caller_xdest_size); + return -1; + } + if ( elf_init(&syms, elf->caller_xdest_base + sizeof(unsigned), + elf->caller_xdest_size - sizeof(unsigned)) ) return -1; + /* + * The caller_xdest_{base,size} and dest_{base,size} need to + * remain valid so long as each struct elf_image does. The + * principle we adopt is that these values are set when the + * memory is allocated or mapped, and cleared when (and if) + * they are unmapped. + * + * Mappings of the guest are normally undone by xc_dom_unmap_all + * (directly or via xc_dom_release). We do not explicitly clear + * these because in fact that happens only at the end of + * xc_dom_boot_image, at which time all of these ELF loading + * functions have returned. No relevant struct elf_binary* + * escapes this file. + */ + xc_elf_set_logfile(dom->xch, &syms, 1); - symtab = dom->bsd_symtab_start + sizeof(int); + symtab = dom->bsd_symtab_start + sizeof(unsigned); maxaddr = elf_round_up(&syms, symtab + elf_size(&syms, syms.ehdr) + elf_shdr_count(&syms) * elf_size(&syms, shdr)); @@ -186,27 +228,40 @@ static int xc_dom_load_elf_symtab(struct xc_dom_image *dom, symtab, maxaddr); count = elf_shdr_count(&syms); + /* elf_shdr_count guarantees that count is reasonable */ + + strtab_referenced = xc_dom_malloc(dom, bitmap_size(count)); + if ( strtab_referenced == NULL ) + return -1; + bitmap_clear(strtab_referenced, count); + /* Note the symtabs @h linked to by any strtab @i. */ + for ( i = 0; i < count; i++ ) + { + shdr2 = elf_shdr_by_index(&syms, i); + if ( elf_uval(&syms, shdr2, sh_type) == SHT_SYMTAB ) + { + h = elf_uval(&syms, shdr2, sh_link); + if (h < count) + set_bit(h, strtab_referenced); + } + } + for ( h = 0; h < count; h++ ) { shdr = elf_shdr_by_index(&syms, h); + if ( !elf_access_ok(elf, ELF_HANDLE_PTRVAL(shdr), 1) ) + /* input has an insane section header count field */ + break; type = elf_uval(&syms, shdr, sh_type); if ( type == SHT_STRTAB ) { - /* Look for a strtab @i linked to symtab @h. */ - for ( i = 0; i < count; i++ ) - { - shdr2 = elf_shdr_by_index(&syms, i); - if ( (elf_uval(&syms, shdr2, sh_type) == SHT_SYMTAB) && - (elf_uval(&syms, shdr2, sh_link) == h) ) - break; - } /* Skip symtab @h if we found no corresponding strtab @i. */ - if ( i == count ) + if ( !test_bit(h, strtab_referenced) ) { if ( elf_64bit(&syms) ) - *(Elf64_Off*)(&shdr->e64.sh_offset) = 0; + elf_store_field(elf, shdr, e64.sh_offset, 0); else - *(Elf32_Off*)(&shdr->e32.sh_offset) = 0; + elf_store_field(elf, shdr, e32.sh_offset, 0); continue; } } @@ -215,13 +270,13 @@ static int xc_dom_load_elf_symtab(struct xc_dom_image *dom, { /* Mangled to be based on ELF header location. */ if ( elf_64bit(&syms) ) - *(Elf64_Off*)(&shdr->e64.sh_offset) = maxaddr - symtab; + elf_store_field(elf, shdr, e64.sh_offset, maxaddr - symtab); else - *(Elf32_Off*)(&shdr->e32.sh_offset) = maxaddr - symtab; + elf_store_field(elf, shdr, e32.sh_offset, maxaddr - symtab); size = elf_uval(&syms, shdr, sh_size); maxaddr = elf_round_up(&syms, maxaddr + size); tables++; - DOMPRINTF("%s: h=%d %s, size=0x%zx, maxaddr=0x%" PRIx64 "", + DOMPRINTF("%s: h=%u %s, size=0x%zx, maxaddr=0x%" PRIx64 "", __FUNCTION__, h, type == SHT_SYMTAB ? "symtab" : "strtab", size, maxaddr); @@ -229,7 +284,7 @@ static int xc_dom_load_elf_symtab(struct xc_dom_image *dom, if ( load ) { shdr2 = elf_shdr_by_index(elf, h); - memcpy((void*)elf_section_start(&syms, shdr), + elf_memcpy_safe(elf, elf_section_start(&syms, shdr), elf_section_start(elf, shdr2), size); } @@ -237,11 +292,18 @@ static int xc_dom_load_elf_symtab(struct xc_dom_image *dom, /* Name is NULL. */ if ( elf_64bit(&syms) ) - *(Elf64_Word*)(&shdr->e64.sh_name) = 0; + elf_store_field(elf, shdr, e64.sh_name, 0); else - *(Elf32_Word*)(&shdr->e32.sh_name) = 0; + elf_store_field(elf, shdr, e32.sh_name, 0); } + if ( elf_check_broken(&syms) ) + DOMPRINTF("%s: symbols ELF broken: %s", __FUNCTION__, + elf_check_broken(&syms)); + if ( elf_check_broken(elf) ) + DOMPRINTF("%s: ELF broken: %s", __FUNCTION__, + elf_check_broken(elf)); + if ( tables == 0 ) { DOMPRINTF("%s: no symbol table present", __FUNCTION__); @@ -253,16 +315,22 @@ static int xc_dom_load_elf_symtab(struct xc_dom_image *dom, return 0; } -static int xc_dom_parse_elf_kernel(struct xc_dom_image *dom) +static elf_errorstatus xc_dom_parse_elf_kernel(struct xc_dom_image *dom) + /* + * This function sometimes returns -1 for error and sometimes + * an errno value. ?!?! + */ { struct elf_binary *elf; - int rc; + elf_errorstatus rc; rc = check_elf_kernel(dom, 1); if ( rc != 0 ) return rc; elf = xc_dom_malloc(dom, sizeof(*elf)); + if ( elf == NULL ) + return -1; dom->private_loader = elf; rc = elf_init(elf, dom->kernel_blob, dom->kernel_size); xc_elf_set_logfile(dom->xch, elf, 1); @@ -274,23 +342,27 @@ static int xc_dom_parse_elf_kernel(struct xc_dom_image *dom) } /* Find the section-header strings table. */ - if ( elf->sec_strtab == NULL ) + if ( ELF_PTRVAL_INVALID(elf->sec_strtab) ) { xc_dom_panic(dom->xch, XC_INVALID_KERNEL, "%s: ELF image" " has no shstrtab", __FUNCTION__); - return -EINVAL; + rc = -EINVAL; + goto out; } /* parse binary and get xen meta info */ elf_parse_binary(elf); if ( (rc = elf_xen_parse(elf, &dom->parms)) != 0 ) - return rc; + { + goto out; + } if ( elf_xen_feature_get(XENFEAT_dom0, dom->parms.f_required) ) { xc_dom_panic(dom->xch, XC_INVALID_KERNEL, "%s: Kernel does not" " support unprivileged (DomU) operation", __FUNCTION__); - return -EINVAL; + rc = -EINVAL; + goto out; } /* find kernel segment */ @@ -304,15 +376,30 @@ static int xc_dom_parse_elf_kernel(struct xc_dom_image *dom) DOMPRINTF("%s: %s: 0x%" PRIx64 " -> 0x%" PRIx64 "", __FUNCTION__, dom->guest_type, dom->kernel_seg.vstart, dom->kernel_seg.vend); - return 0; + rc = 0; +out: + if ( elf_check_broken(elf) ) + DOMPRINTF("%s: ELF broken: %s", __FUNCTION__, + elf_check_broken(elf)); + + return rc; } -static int xc_dom_load_elf_kernel(struct xc_dom_image *dom) +static elf_errorstatus xc_dom_load_elf_kernel(struct xc_dom_image *dom) { struct elf_binary *elf = dom->private_loader; - int rc; + elf_errorstatus rc; + xen_pfn_t pages; + + elf->dest_base = xc_dom_seg_to_ptr_pages(dom, &dom->kernel_seg, &pages); + if ( elf->dest_base == NULL ) + { + DOMPRINTF("%s: xc_dom_vaddr_to_ptr(dom,dom->kernel_seg)" + " => NULL", __FUNCTION__); + return -1; + } + elf->dest_size = pages * XC_DOM_PAGE_SIZE(dom); - elf->dest = xc_dom_seg_to_ptr(dom, &dom->kernel_seg); rc = elf_load_binary(elf); if ( rc < 0 ) { diff --git a/tools/libxc/xc_dom_ia64.c b/tools/libxc/xc_dom_ia64.c index dcd1523..076821c 100644 --- a/tools/libxc/xc_dom_ia64.c +++ b/tools/libxc/xc_dom_ia64.c @@ -60,6 +60,12 @@ int start_info_ia64(struct xc_dom_image *dom) DOMPRINTF_CALLED(dom->xch); + if ( start_info == NULL ) + { + DOMPRINTF("%s: xc_dom_pfn_to_ptr failed on start_info", __FUNCTION__); + return -1; /* our caller throws away our return value :-/ */ + } + memset(start_info, 0, sizeof(*start_info)); sprintf(start_info->magic, dom->guest_type); start_info->flags = dom->flags; @@ -182,6 +188,12 @@ int arch_setup_meminit(struct xc_dom_image *dom) /* setup initial p2m */ dom->p2m_host = xc_dom_malloc(dom, sizeof(xen_pfn_t) * nbr); + if ( dom->p2m_host == NULL ) + { + DOMPRINTF("%s: xc_dom_malloc failed for p2m_host", + __FUNCTION__); + return -1; + } for ( pfn = 0; pfn < nbr; pfn++ ) dom->p2m_host[pfn] = start + pfn; diff --git a/tools/libxc/xc_dom_x86.c b/tools/libxc/xc_dom_x86.c index 0cf1687..448d9a1 100644 --- a/tools/libxc/xc_dom_x86.c +++ b/tools/libxc/xc_dom_x86.c @@ -144,6 +144,9 @@ static int setup_pgtables_x86_32(struct xc_dom_image *dom) xen_vaddr_t addr; xen_pfn_t pgpfn; + if ( l2tab == NULL ) + goto pfn_error; + for ( addr = dom->parms.virt_base; addr < dom->virt_pgtab_end; addr += PAGE_SIZE_X86 ) { @@ -151,6 +154,8 @@ static int setup_pgtables_x86_32(struct xc_dom_image *dom) { /* get L1 tab, make L2 entry */ l1tab = xc_dom_pfn_to_ptr(dom, l1pfn, 1); + if ( l1tab == NULL ) + goto pfn_error; l2off = l2_table_offset_i386(addr); l2tab[l2off] = pfn_to_paddr(xc_dom_p2m_guest(dom, l1pfn)) | L2_PROT; @@ -169,6 +174,11 @@ static int setup_pgtables_x86_32(struct xc_dom_image *dom) l1tab = NULL; } return 0; + +pfn_error: + xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, + "%s: xc_dom_pfn_to_ptr failed", __FUNCTION__); + return -EINVAL; } /* @@ -219,6 +229,12 @@ static xen_pfn_t move_l3_below_4G(struct xc_dom_image *dom, goto out; l3tab = xc_dom_pfn_to_ptr(dom, l3pfn, 1); + if ( l3tab == NULL ) + { + DOMPRINTF("%s: xc_dom_pfn_to_ptr(dom, l3pfn, 1) => NULL", + __FUNCTION__); + return l3mfn; /* our one call site will call xc_dom_panic and fail */ + } memset(l3tab, 0, XC_DOM_PAGE_SIZE(dom)); DOMPRINTF("%s: successfully relocated L3 below 4G. " @@ -262,6 +278,8 @@ static int setup_pgtables_x86_32_pae(struct xc_dom_image *dom) } l3tab = xc_dom_pfn_to_ptr(dom, l3pfn, 1); + if ( l3tab == NULL ) + goto pfn_error; for ( addr = dom->parms.virt_base; addr < dom->virt_pgtab_end; addr += PAGE_SIZE_X86 ) @@ -270,6 +288,8 @@ static int setup_pgtables_x86_32_pae(struct xc_dom_image *dom) { /* get L2 tab, make L3 entry */ l2tab = xc_dom_pfn_to_ptr(dom, l2pfn, 1); + if ( l2tab == NULL ) + goto pfn_error; l3off = l3_table_offset_pae(addr); l3tab[l3off] = pfn_to_paddr(xc_dom_p2m_guest(dom, l2pfn)) | L3_PROT; @@ -280,6 +300,8 @@ static int setup_pgtables_x86_32_pae(struct xc_dom_image *dom) { /* get L1 tab, make L2 entry */ l1tab = xc_dom_pfn_to_ptr(dom, l1pfn, 1); + if ( l1tab == NULL ) + goto pfn_error; l2off = l2_table_offset_pae(addr); l2tab[l2off] = pfn_to_paddr(xc_dom_p2m_guest(dom, l1pfn)) | L2_PROT; @@ -306,6 +328,11 @@ static int setup_pgtables_x86_32_pae(struct xc_dom_image *dom) l3tab[3] = pfn_to_paddr(xc_dom_p2m_guest(dom, l2pfn)) | L3_PROT; } return 0; + +pfn_error: + xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, + "%s: xc_dom_pfn_to_ptr failed", __FUNCTION__); + return -EINVAL; } #undef L1_PROT @@ -344,6 +371,9 @@ static int setup_pgtables_x86_64(struct xc_dom_image *dom) uint64_t addr; xen_pfn_t pgpfn; + if ( l4tab == NULL ) + goto pfn_error; + for ( addr = dom->parms.virt_base; addr < dom->virt_pgtab_end; addr += PAGE_SIZE_X86 ) { @@ -351,6 +381,8 @@ static int setup_pgtables_x86_64(struct xc_dom_image *dom) { /* get L3 tab, make L4 entry */ l3tab = xc_dom_pfn_to_ptr(dom, l3pfn, 1); + if ( l3tab == NULL ) + goto pfn_error; l4off = l4_table_offset_x86_64(addr); l4tab[l4off] = pfn_to_paddr(xc_dom_p2m_guest(dom, l3pfn)) | L4_PROT; @@ -361,6 +393,8 @@ static int setup_pgtables_x86_64(struct xc_dom_image *dom) { /* get L2 tab, make L3 entry */ l2tab = xc_dom_pfn_to_ptr(dom, l2pfn, 1); + if ( l2tab == NULL ) + goto pfn_error; l3off = l3_table_offset_x86_64(addr); l3tab[l3off] = pfn_to_paddr(xc_dom_p2m_guest(dom, l2pfn)) | L3_PROT; @@ -373,6 +407,8 @@ static int setup_pgtables_x86_64(struct xc_dom_image *dom) { /* get L1 tab, make L2 entry */ l1tab = xc_dom_pfn_to_ptr(dom, l1pfn, 1); + if ( l1tab == NULL ) + goto pfn_error; l2off = l2_table_offset_x86_64(addr); l2tab[l2off] = pfn_to_paddr(xc_dom_p2m_guest(dom, l1pfn)) | L2_PROT; @@ -393,6 +429,11 @@ static int setup_pgtables_x86_64(struct xc_dom_image *dom) l1tab = NULL; } return 0; + +pfn_error: + xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, + "%s: xc_dom_pfn_to_ptr failed", __FUNCTION__); + return -EINVAL; } #undef L1_PROT @@ -410,6 +451,8 @@ static int alloc_magic_pages(struct xc_dom_image *dom) if ( xc_dom_alloc_segment(dom, &dom->p2m_seg, "phys2mach", 0, p2m_size) ) return -1; dom->p2m_guest = xc_dom_seg_to_ptr(dom, &dom->p2m_seg); + if ( dom->p2m_guest == NULL ) + return -1; /* allocate special pages */ dom->start_info_pfn = xc_dom_alloc_page(dom, "start info"); @@ -434,6 +477,12 @@ static int start_info_x86_32(struct xc_dom_image *dom) DOMPRINTF_CALLED(dom->xch); + if ( start_info == NULL ) + { + DOMPRINTF("%s: xc_dom_pfn_to_ptr failed on start_info", __FUNCTION__); + return -1; /* our caller throws away our return value :-/ */ + } + memset(start_info, 0, sizeof(*start_info)); strncpy(start_info->magic, dom->guest_type, sizeof(start_info->magic)); start_info->magic[sizeof(start_info->magic) - 1] = '\0'; @@ -474,6 +523,12 @@ static int start_info_x86_64(struct xc_dom_image *dom) DOMPRINTF_CALLED(dom->xch); + if ( start_info == NULL ) + { + DOMPRINTF("%s: xc_dom_pfn_to_ptr failed on start_info", __FUNCTION__); + return -1; /* our caller throws away our return value :-/ */ + } + memset(start_info, 0, sizeof(*start_info)); strncpy(start_info->magic, dom->guest_type, sizeof(start_info->magic)); start_info->magic[sizeof(start_info->magic) - 1] = '\0'; @@ -725,6 +780,9 @@ int arch_setup_meminit(struct xc_dom_image *dom) } dom->p2m_host = xc_dom_malloc(dom, sizeof(xen_pfn_t) * dom->total_pages); + if ( dom->p2m_host == NULL ) + return -EINVAL; + if ( dom->superpages ) { int count = dom->total_pages >> SUPERPAGE_PFN_SHIFT; diff --git a/tools/libxc/xc_domain_restore.c b/tools/libxc/xc_domain_restore.c index b4c0b10..f9ed6b2 100644 --- a/tools/libxc/xc_domain_restore.c +++ b/tools/libxc/xc_domain_restore.c @@ -1180,6 +1180,11 @@ static int apply_batch(xc_interface *xch, uint32_t dom, struct restore_ctx *ctx, /* Map relevant mfns */ pfn_err = calloc(j, sizeof(*pfn_err)); + if ( pfn_err == NULL ) + { + PERROR("allocation for pfn_err failed"); + return -1; + } region_base = xc_map_foreign_bulk( xch, dom, PROT_WRITE, region_mfn, pfn_err, j); @@ -1556,6 +1561,12 @@ int xc_domain_restore(xc_interface *xch, int io_fd, uint32_t dom, mfn = ctx->p2m[pfn]; buf = xc_map_foreign_range(xch, dom, PAGE_SIZE, PROT_READ | PROT_WRITE, mfn); + if ( buf == NULL ) + { + ERROR("xc_map_foreign_range for generation id" + " buffer failed"); + goto out; + } generationid = *(unsigned long long *)(buf + offset); *(unsigned long long *)(buf + offset) = generationid + 1; @@ -1713,6 +1724,11 @@ int xc_domain_restore(xc_interface *xch, int io_fd, uint32_t dom, l3tab = (uint64_t *) xc_map_foreign_range(xch, dom, PAGE_SIZE, PROT_READ, ctx->p2m[i]); + if ( l3tab == NULL ) + { + PERROR("xc_map_foreign_range failed (for l3tab)"); + goto out; + } for ( j = 0; j < 4; j++ ) l3ptes[j] = l3tab[j]; @@ -1739,6 +1755,11 @@ int xc_domain_restore(xc_interface *xch, int io_fd, uint32_t dom, l3tab = (uint64_t *) xc_map_foreign_range(xch, dom, PAGE_SIZE, PROT_READ | PROT_WRITE, ctx->p2m[i]); + if ( l3tab == NULL ) + { + PERROR("xc_map_foreign_range failed (for l3tab, 2nd)"); + goto out; + } for ( j = 0; j < 4; j++ ) l3tab[j] = l3ptes[j]; @@ -1909,6 +1930,12 @@ int xc_domain_restore(xc_interface *xch, int io_fd, uint32_t dom, SET_FIELD(ctxt, user_regs.edx, mfn); start_info = xc_map_foreign_range( xch, dom, PAGE_SIZE, PROT_READ | PROT_WRITE, mfn); + if ( start_info == NULL ) + { + PERROR("xc_map_foreign_range failed (for start_info)"); + goto out; + } + SET_FIELD(start_info, nr_pages, dinfo->p2m_size); SET_FIELD(start_info, shared_info, shared_info_frame<pstart >> PAGE_SHIFT) + i]; - elf->dest = xc_map_foreign_ranges( + elf->dest_base = xc_map_foreign_ranges( xch, dom, pages << PAGE_SHIFT, PROT_READ | PROT_WRITE, 1 << PAGE_SHIFT, entries, pages); - if ( elf->dest == NULL ) + if ( elf->dest_base == NULL ) goto err; + elf->dest_size = pages * PAGE_SIZE; - elf->dest += elf->pstart & (PAGE_SIZE - 1); + ELF_ADVANCE_DEST(elf, elf->pstart & (PAGE_SIZE - 1)); /* Load the initial elf image. */ rc = elf_load_binary(elf); if ( rc < 0 ) PERROR("Failed to load elf binary\n"); - munmap(elf->dest, pages << PAGE_SHIFT); - elf->dest = NULL; + munmap(elf->dest_base, pages << PAGE_SHIFT); + elf->dest_base = NULL; + elf->dest_size = 0; err: free(entries); @@ -401,11 +403,16 @@ static int setup_guest(xc_interface *xch, munmap(page0, PAGE_SIZE); } + if ( elf_check_broken(&elf) ) + ERROR("HVM ELF broken: %s", elf_check_broken(&elf)); + free(page_array); return 0; error_out: free(page_array); + if ( elf_check_broken(&elf) ) + ERROR("HVM ELF broken, failing: %s", elf_check_broken(&elf)); return -1; } diff --git a/tools/libxc/xc_linux_osdep.c b/tools/libxc/xc_linux_osdep.c index 787e742..98e041c 100644 --- a/tools/libxc/xc_linux_osdep.c +++ b/tools/libxc/xc_linux_osdep.c @@ -378,6 +378,8 @@ static void *linux_privcmd_map_foreign_range(xc_interface *xch, xc_osdep_handle num = (size + XC_PAGE_SIZE - 1) >> XC_PAGE_SHIFT; arr = calloc(num, sizeof(xen_pfn_t)); + if ( arr == NULL ) + return NULL; for ( i = 0; i < num; i++ ) arr[i] = mfn + i; @@ -402,6 +404,8 @@ static void *linux_privcmd_map_foreign_ranges(xc_interface *xch, xc_osdep_handle num_per_entry = chunksize >> XC_PAGE_SHIFT; num = num_per_entry * nentries; arr = calloc(num, sizeof(xen_pfn_t)); + if ( arr == NULL ) + return NULL; for ( i = 0; i < nentries; i++ ) for ( j = 0; j < num_per_entry; j++ ) diff --git a/tools/libxc/xc_offline_page.c b/tools/libxc/xc_offline_page.c index 089a361..36b9812 100644 --- a/tools/libxc/xc_offline_page.c +++ b/tools/libxc/xc_offline_page.c @@ -714,6 +714,11 @@ int xc_exchange_page(xc_interface *xch, int domid, xen_pfn_t mfn) new_p = xc_map_foreign_range(xch, domid, PAGE_SIZE, PROT_READ|PROT_WRITE, new_mfn); + if ( new_p == NULL ) + { + ERROR("failed to map new_p for copy, guest may be broken?"); + goto failed; + } memcpy(new_p, backup, PAGE_SIZE); munmap(new_p, PAGE_SIZE); mops.arg1.mfn = new_mfn; diff --git a/tools/libxc/xc_private.c b/tools/libxc/xc_private.c index 3e03a91..848ceed 100644 --- a/tools/libxc/xc_private.c +++ b/tools/libxc/xc_private.c @@ -771,6 +771,8 @@ const char *xc_strerror(xc_interface *xch, int errcode) errbuf = pthread_getspecific(errbuf_pkey); if (errbuf == NULL) { errbuf = malloc(XS_BUFSIZE); + if ( errbuf == NULL ) + return "(failed to allocate errbuf)"; pthread_setspecific(errbuf_pkey, errbuf); } diff --git a/tools/libxc/xenctrl.h b/tools/libxc/xenctrl.h index b7741ca..8952048 100644 --- a/tools/libxc/xenctrl.h +++ b/tools/libxc/xenctrl.h @@ -1778,7 +1778,7 @@ int xc_cpuid_set(xc_interface *xch, int xc_cpuid_apply_policy(xc_interface *xch, domid_t domid); void xc_cpuid_to_str(const unsigned int *regs, - char **strs); + char **strs); /* some strs[] may be NULL if ENOMEM */ int xc_mca_op(xc_interface *xch, struct xen_mc *mc); #endif diff --git a/tools/xcutils/readnotes.c b/tools/xcutils/readnotes.c index c926186..5fa445e 100644 --- a/tools/xcutils/readnotes.c +++ b/tools/xcutils/readnotes.c @@ -61,51 +61,56 @@ struct setup_header { } __attribute__((packed)); static void print_string_note(const char *prefix, struct elf_binary *elf, - const elf_note *note) + ELF_HANDLE_DECL(elf_note) note) { - printf("%s: %s\n", prefix, (char*)elf_note_desc(elf, note)); + printf("%s: %s\n", prefix, elf_strfmt(elf, elf_note_desc(elf, note))); } static void print_numeric_note(const char *prefix, struct elf_binary *elf, - const elf_note *note) + ELF_HANDLE_DECL(elf_note) note) { uint64_t value = elf_note_numeric(elf, note); - int descsz = elf_uval(elf, note, descsz); + unsigned descsz = elf_uval(elf, note, descsz); printf("%s: %#*" PRIx64 " (%d bytes)\n", prefix, 2+2*descsz, value, descsz); } static void print_l1_mfn_valid_note(const char *prefix, struct elf_binary *elf, - const elf_note *note) + ELF_HANDLE_DECL(elf_note) note) { - int descsz = elf_uval(elf, note, descsz); - const uint32_t *desc32 = elf_note_desc(elf, note); - const uint64_t *desc64 = elf_note_desc(elf, note); + unsigned descsz = elf_uval(elf, note, descsz); + elf_ptrval desc = elf_note_desc(elf, note); /* XXX should be able to cope with a list of values. */ switch ( descsz / 2 ) { case 8: printf("%s: mask=%#"PRIx64" value=%#"PRIx64"\n", prefix, - desc64[0], desc64[1]); + elf_access_unsigned(elf, desc, 0, 8), + elf_access_unsigned(elf, desc, 8, 8)); break; case 4: printf("%s: mask=%#"PRIx32" value=%#"PRIx32"\n", prefix, - desc32[0],desc32[1]); + (uint32_t)elf_access_unsigned(elf, desc, 0, 4), + (uint32_t)elf_access_unsigned(elf, desc, 4, 4)); break; } } -static int print_notes(struct elf_binary *elf, const elf_note *start, const elf_note *end) +static unsigned print_notes(struct elf_binary *elf, ELF_HANDLE_DECL(elf_note) start, ELF_HANDLE_DECL(elf_note) end) { - const elf_note *note; - int notes_found = 0; + ELF_HANDLE_DECL(elf_note) note; + unsigned notes_found = 0; + const char *this_note_name; - for ( note = start; note < end; note = elf_note_next(elf, note) ) + for ( note = start; ELF_HANDLE_PTRVAL(note) < ELF_HANDLE_PTRVAL(end); note = elf_note_next(elf, note) ) { - if (0 != strcmp(elf_note_name(elf, note), "Xen")) + this_note_name = elf_note_name(elf, note); + if (NULL == this_note_name) + continue; + if (0 != strcmp(this_note_name, "Xen")) continue; notes_found++; @@ -156,7 +161,7 @@ static int print_notes(struct elf_binary *elf, const elf_note *start, const elf_ break; default: printf("unknown note type %#x\n", - (int)elf_uval(elf, note, type)); + (unsigned)elf_uval(elf, note, type)); break; } } @@ -166,12 +171,13 @@ static int print_notes(struct elf_binary *elf, const elf_note *start, const elf_ int main(int argc, char **argv) { const char *f; - int fd,h,size,usize,count; + int fd; + unsigned h,size,usize,count; void *image,*tmp; struct stat st; struct elf_binary elf; - const elf_shdr *shdr; - int notes_found = 0; + ELF_HANDLE_DECL(elf_shdr) shdr; + unsigned notes_found = 0; struct setup_header *hdr; uint64_t payload_offset, payload_length; @@ -257,7 +263,7 @@ int main(int argc, char **argv) count = elf_phdr_count(&elf); for ( h=0; h < count; h++) { - const elf_phdr *phdr; + ELF_HANDLE_DECL(elf_phdr) phdr; phdr = elf_phdr_by_index(&elf, h); if (elf_uval(&elf, phdr, p_type) != PT_NOTE) continue; @@ -269,8 +275,8 @@ int main(int argc, char **argv) continue; notes_found = print_notes(&elf, - elf_segment_start(&elf, phdr), - elf_segment_end(&elf, phdr)); + ELF_MAKE_HANDLE(elf_note, elf_segment_start(&elf, phdr)), + ELF_MAKE_HANDLE(elf_note, elf_segment_end(&elf, phdr))); } if ( notes_found == 0 ) @@ -278,13 +284,13 @@ int main(int argc, char **argv) count = elf_shdr_count(&elf); for ( h=0; h < count; h++) { - const elf_shdr *shdr; + ELF_HANDLE_DECL(elf_shdr) shdr; shdr = elf_shdr_by_index(&elf, h); if (elf_uval(&elf, shdr, sh_type) != SHT_NOTE) continue; notes_found = print_notes(&elf, - elf_section_start(&elf, shdr), - elf_section_end(&elf, shdr)); + ELF_MAKE_HANDLE(elf_note, elf_section_start(&elf, shdr)), + ELF_MAKE_HANDLE(elf_note, elf_section_end(&elf, shdr))); if ( notes_found ) fprintf(stderr, "using notes from SHT_NOTE section\n"); @@ -292,8 +298,12 @@ int main(int argc, char **argv) } shdr = elf_shdr_by_name(&elf, "__xen_guest"); - if (shdr) - printf("__xen_guest: %s\n", (char*)elf_section_start(&elf, shdr)); + if (ELF_HANDLE_VALID(shdr)) + printf("__xen_guest: %s\n", + elf_strfmt(&elf, elf_section_start(&elf, shdr))); + + if (elf_check_broken(&elf)) + printf("warning: broken ELF: %s\n", elf_check_broken(&elf)); return 0; } diff --git a/xen/arch/arm/kernel.c b/xen/arch/arm/kernel.c index 2d56130..dec0519 100644 --- a/xen/arch/arm/kernel.c +++ b/xen/arch/arm/kernel.c @@ -146,6 +146,8 @@ static int kernel_try_elf_prepare(struct kernel_info *info) { int rc; + memset(&info->elf.elf, 0, sizeof(info->elf.elf)); + info->kernel_order = get_order_from_bytes(KERNEL_FLASH_SIZE); info->kernel_img = alloc_xenheap_pages(info->kernel_order, 0); if ( info->kernel_img == NULL ) @@ -160,7 +162,7 @@ static int kernel_try_elf_prepare(struct kernel_info *info) #endif elf_parse_binary(&info->elf.elf); if ( (rc = elf_xen_parse(&info->elf.elf, &info->elf.parms)) != 0 ) - return rc; + goto err; /* * TODO: can the ELF header be used to find the physical address @@ -169,7 +171,18 @@ static int kernel_try_elf_prepare(struct kernel_info *info) info->entry = info->elf.parms.virt_entry; info->load = kernel_elf_load; + if ( elf_check_broken(&info->elf.elf) ) + printk("Xen: warning: ELF kernel broken: %s\n", + elf_check_broken(&info->elf.elf)); + return 0; + +err: + if ( elf_check_broken(&info->elf.elf) ) + printk("Xen: ELF kernel broken: %s\n", + elf_check_broken(&info->elf.elf)); + + return rc; } int kernel_prepare(struct kernel_info *info) diff --git a/xen/arch/x86/bzimage.c b/xen/arch/x86/bzimage.c index 5adc223..3600dca 100644 --- a/xen/arch/x86/bzimage.c +++ b/xen/arch/x86/bzimage.c @@ -220,7 +220,7 @@ unsigned long __init bzimage_headroom(char *image_start, image_length = hdr->payload_length; } - if ( elf_is_elfbinary(image_start) ) + if ( elf_is_elfbinary(image_start, image_length) ) return 0; orig_image_len = image_length; @@ -251,7 +251,7 @@ int __init bzimage_parse(char *image_base, char **image_start, unsigned long *im *image_len = hdr->payload_length; } - if ( elf_is_elfbinary(*image_start) ) + if ( elf_is_elfbinary(*image_start, *image_len) ) return 0; BUG_ON(!(image_base < *image_start)); diff --git a/xen/arch/x86/domain_build.c b/xen/arch/x86/domain_build.c index 469d363..0dbec96 100644 --- a/xen/arch/x86/domain_build.c +++ b/xen/arch/x86/domain_build.c @@ -374,7 +374,7 @@ int __init construct_dom0( #endif elf_parse_binary(&elf); if ( (rc = elf_xen_parse(&elf, &parms)) != 0 ) - return rc; + goto out; /* compatibility check */ compatible = 0; @@ -413,14 +413,16 @@ int __init construct_dom0( if ( !compatible ) { printk("Mismatch between Xen and DOM0 kernel\n"); - return -EINVAL; + rc = -EINVAL; + goto out; } if ( parms.elf_notes[XEN_ELFNOTE_SUPPORTED_FEATURES].type != XEN_ENT_NONE && !test_bit(XENFEAT_dom0, parms.f_supported) ) { printk("Kernel does not support Dom0 operation\n"); - return -EINVAL; + rc = -EINVAL; + goto out; } #if defined(__x86_64__) @@ -734,7 +736,8 @@ int __init construct_dom0( (v_end > HYPERVISOR_COMPAT_VIRT_START(d)) ) { printk("DOM0 image overlaps with Xen private area.\n"); - return -EINVAL; + rc = -EINVAL; + goto out; } if ( is_pv_32on64_domain(d) ) @@ -908,12 +911,13 @@ int __init construct_dom0( write_ptbase(v); /* Copy the OS image and free temporary buffer. */ - elf.dest = (void*)vkern_start; + elf.dest_base = (void*)vkern_start; + elf.dest_size = vkern_end - vkern_start; rc = elf_load_binary(&elf); if ( rc < 0 ) { printk("Failed to load the kernel binary\n"); - return rc; + goto out; } bootstrap_map(NULL); @@ -924,7 +928,8 @@ int __init construct_dom0( { write_ptbase(current); printk("Invalid HYPERCALL_PAGE field in ELF notes.\n"); - return -1; + rc = -1; + goto out; } hypercall_page_initialise( d, (void *)(unsigned long)parms.virt_hypercall); @@ -1271,9 +1276,19 @@ int __init construct_dom0( BUG_ON(rc != 0); - iommu_dom0_init(dom0); + if ( elf_check_broken(&elf) ) + printk(" Xen warning: dom0 kernel broken ELF: %s\n", + elf_check_broken(&elf)); + iommu_dom0_init(dom0); return 0; + +out: + if ( elf_check_broken(&elf) ) + printk(" Xen dom0 kernel broken ELF: %s\n", + elf_check_broken(&elf)); + + return rc; } /* diff --git a/xen/common/libelf/Makefile b/xen/common/libelf/Makefile index 18dc8e2..5bf8f76 100644 --- a/xen/common/libelf/Makefile +++ b/xen/common/libelf/Makefile @@ -2,6 +2,8 @@ obj-bin-y := libelf.o SECTIONS := text data $(SPECIAL_DATA_SECTIONS) +CFLAGS += -Wno-pointer-sign + libelf.o: libelf-temp.o Makefile $(OBJCOPY) $(foreach s,$(SECTIONS),--rename-section .$(s)=.init.$(s)) $< $@ diff --git a/xen/common/libelf/libelf-dominfo.c b/xen/common/libelf/libelf-dominfo.c index 523837f..412ea70 100644 --- a/xen/common/libelf/libelf-dominfo.c +++ b/xen/common/libelf/libelf-dominfo.c @@ -29,22 +29,22 @@ static const char *const elf_xen_feature_names[] = { [XENFEAT_pae_pgdir_above_4gb] = "pae_pgdir_above_4gb", [XENFEAT_dom0] = "dom0" }; -static const int elf_xen_features = +static const unsigned elf_xen_features = sizeof(elf_xen_feature_names) / sizeof(elf_xen_feature_names[0]); -int elf_xen_parse_features(const char *features, +elf_errorstatus elf_xen_parse_features(const char *features, uint32_t *supported, uint32_t *required) { - char feature[64]; - int pos, len, i; + unsigned char feature[64]; + unsigned pos, len, i; if ( features == NULL ) return 0; for ( pos = 0; features[pos] != '\0'; pos += len ) { - memset(feature, 0, sizeof(feature)); + elf_memset_unchecked(feature, 0, sizeof(feature)); for ( len = 0;; len++ ) { if ( len >= sizeof(feature)-1 ) @@ -94,14 +94,14 @@ int elf_xen_parse_features(const char *features, /* ------------------------------------------------------------------------ */ /* xen elf notes */ -int elf_xen_parse_note(struct elf_binary *elf, +elf_errorstatus elf_xen_parse_note(struct elf_binary *elf, struct elf_dom_parms *parms, - const elf_note *note) + ELF_HANDLE_DECL(elf_note) note) { /* *INDENT-OFF* */ static const struct { char *name; - int str; + bool str; } note_desc[] = { [XEN_ELFNOTE_ENTRY] = { "ENTRY", 0}, [XEN_ELFNOTE_HYPERCALL_PAGE] = { "HYPERCALL_PAGE", 0}, @@ -125,7 +125,7 @@ int elf_xen_parse_note(struct elf_binary *elf, const char *str = NULL; uint64_t val = 0; unsigned int i; - int type = elf_uval(elf, note, type); + unsigned type = elf_uval(elf, note, type); if ( (type >= sizeof(note_desc) / sizeof(note_desc[0])) || (note_desc[type].name == NULL) ) @@ -137,7 +137,10 @@ int elf_xen_parse_note(struct elf_binary *elf, if ( note_desc[type].str ) { - str = elf_note_desc(elf, note); + str = elf_strval(elf, elf_note_desc(elf, note)); + if (str == NULL) + /* elf_strval will mark elf broken if it fails so no need to log */ + return 0; elf_msg(elf, "%s: %s = \"%s\"\n", __FUNCTION__, note_desc[type].name, str); parms->elf_notes[type].type = XEN_ENT_STR; @@ -213,23 +216,37 @@ int elf_xen_parse_note(struct elf_binary *elf, return 0; } -static int elf_xen_parse_notes(struct elf_binary *elf, +#define ELF_NOTE_INVALID (~0U) + +static unsigned elf_xen_parse_notes(struct elf_binary *elf, struct elf_dom_parms *parms, - const void *start, const void *end) + elf_ptrval start, + elf_ptrval end, + unsigned *total_note_count) { - int xen_elfnotes = 0; - const elf_note *note; + unsigned xen_elfnotes = 0; + ELF_HANDLE_DECL(elf_note) note; + const char *note_name; parms->elf_note_start = start; parms->elf_note_end = end; - for ( note = parms->elf_note_start; - (void *)note < parms->elf_note_end; + for ( note = ELF_MAKE_HANDLE(elf_note, parms->elf_note_start); + ELF_HANDLE_PTRVAL(note) < parms->elf_note_end; note = elf_note_next(elf, note) ) { - if ( strcmp(elf_note_name(elf, note), "Xen") ) + if ( *total_note_count >= ELF_MAX_TOTAL_NOTE_COUNT ) + { + elf_mark_broken(elf, "too many ELF notes"); + break; + } + (*total_note_count)++; + note_name = elf_note_name(elf, note); + if ( note_name == NULL ) + continue; + if ( strcmp(note_name, "Xen") ) continue; if ( elf_xen_parse_note(elf, parms, note) ) - return -1; + return ELF_NOTE_INVALID; xen_elfnotes++; } return xen_elfnotes; @@ -238,48 +255,49 @@ static int elf_xen_parse_notes(struct elf_binary *elf, /* ------------------------------------------------------------------------ */ /* __xen_guest section */ -int elf_xen_parse_guest_info(struct elf_binary *elf, +elf_errorstatus elf_xen_parse_guest_info(struct elf_binary *elf, struct elf_dom_parms *parms) { - const char *h; - char name[32], value[128]; - int len; + elf_ptrval h; + unsigned char name[32], value[128]; + unsigned len; h = parms->guest_info; - while ( *h ) +#define STAR(h) (elf_access_unsigned(elf, (h), 0, 1)) + while ( STAR(h) ) { - memset(name, 0, sizeof(name)); - memset(value, 0, sizeof(value)); + elf_memset_unchecked(name, 0, sizeof(name)); + elf_memset_unchecked(value, 0, sizeof(value)); for ( len = 0;; len++, h++ ) { if ( len >= sizeof(name)-1 ) break; - if ( *h == '\0' ) + if ( STAR(h) == '\0' ) break; - if ( *h == ',' ) + if ( STAR(h) == ',' ) { h++; break; } - if ( *h == '=' ) + if ( STAR(h) == '=' ) { h++; for ( len = 0;; len++, h++ ) { if ( len >= sizeof(value)-1 ) break; - if ( *h == '\0' ) + if ( STAR(h) == '\0' ) break; - if ( *h == ',' ) + if ( STAR(h) == ',' ) { h++; break; } - value[len] = *h; + value[len] = STAR(h); } break; } - name[len] = *h; + name[len] = STAR(h); } elf_msg(elf, "%s: %s=\"%s\"\n", __FUNCTION__, name, value); @@ -325,12 +343,13 @@ int elf_xen_parse_guest_info(struct elf_binary *elf, /* ------------------------------------------------------------------------ */ /* sanity checks */ -static int elf_xen_note_check(struct elf_binary *elf, +static elf_errorstatus elf_xen_note_check(struct elf_binary *elf, struct elf_dom_parms *parms) { - if ( (parms->elf_note_start == NULL) && (parms->guest_info == NULL) ) + if ( (ELF_PTRVAL_INVALID(parms->elf_note_start)) && + (ELF_PTRVAL_INVALID(parms->guest_info)) ) { - int machine = elf_uval(elf, elf->ehdr, e_machine); + unsigned machine = elf_uval(elf, elf->ehdr, e_machine); if ( (machine == EM_386) || (machine == EM_X86_64) ) { elf_err(elf, "%s: ERROR: Not a Xen-ELF image: " @@ -368,7 +387,7 @@ static int elf_xen_note_check(struct elf_binary *elf, return 0; } -static int elf_xen_addr_calc_check(struct elf_binary *elf, +static elf_errorstatus elf_xen_addr_calc_check(struct elf_binary *elf, struct elf_dom_parms *parms) { if ( (parms->elf_paddr_offset != UNSET_ADDR) && @@ -454,15 +473,16 @@ static int elf_xen_addr_calc_check(struct elf_binary *elf, /* ------------------------------------------------------------------------ */ /* glue it all together ... */ -int elf_xen_parse(struct elf_binary *elf, +elf_errorstatus elf_xen_parse(struct elf_binary *elf, struct elf_dom_parms *parms) { - const elf_shdr *shdr; - const elf_phdr *phdr; - int xen_elfnotes = 0; - int i, count, rc; + ELF_HANDLE_DECL(elf_shdr) shdr; + ELF_HANDLE_DECL(elf_phdr) phdr; + unsigned xen_elfnotes = 0; + unsigned i, count, more_notes; + unsigned total_note_count = 0; - memset(parms, 0, sizeof(*parms)); + elf_memset_unchecked(parms, 0, sizeof(*parms)); parms->virt_base = UNSET_ADDR; parms->virt_entry = UNSET_ADDR; parms->virt_hypercall = UNSET_ADDR; @@ -475,6 +495,9 @@ int elf_xen_parse(struct elf_binary *elf, for ( i = 0; i < count; i++ ) { phdr = elf_phdr_by_index(elf, i); + if ( !elf_access_ok(elf, ELF_HANDLE_PTRVAL(phdr), 1) ) + /* input has an insane program header count field */ + break; if ( elf_uval(elf, phdr, p_type) != PT_NOTE ) continue; @@ -485,13 +508,14 @@ int elf_xen_parse(struct elf_binary *elf, if (elf_uval(elf, phdr, p_offset) == 0) continue; - rc = elf_xen_parse_notes(elf, parms, + more_notes = elf_xen_parse_notes(elf, parms, elf_segment_start(elf, phdr), - elf_segment_end(elf, phdr)); - if ( rc == -1 ) + elf_segment_end(elf, phdr), + &total_note_count); + if ( more_notes == ELF_NOTE_INVALID ) return -1; - xen_elfnotes += rc; + xen_elfnotes += more_notes; } /* @@ -504,21 +528,25 @@ int elf_xen_parse(struct elf_binary *elf, for ( i = 0; i < count; i++ ) { shdr = elf_shdr_by_index(elf, i); + if ( !elf_access_ok(elf, ELF_HANDLE_PTRVAL(shdr), 1) ) + /* input has an insane section header count field */ + break; if ( elf_uval(elf, shdr, sh_type) != SHT_NOTE ) continue; - rc = elf_xen_parse_notes(elf, parms, + more_notes = elf_xen_parse_notes(elf, parms, elf_section_start(elf, shdr), - elf_section_end(elf, shdr)); + elf_section_end(elf, shdr), + &total_note_count); - if ( rc == -1 ) + if ( more_notes == ELF_NOTE_INVALID ) return -1; - if ( xen_elfnotes == 0 && rc > 0 ) + if ( xen_elfnotes == 0 && more_notes > 0 ) elf_msg(elf, "%s: using notes from SHT_NOTE section\n", __FUNCTION__); - xen_elfnotes += rc; + xen_elfnotes += more_notes; } } @@ -528,20 +556,15 @@ int elf_xen_parse(struct elf_binary *elf, */ if ( xen_elfnotes == 0 ) { - count = elf_shdr_count(elf); - for ( i = 0; i < count; i++ ) + shdr = elf_shdr_by_name(elf, "__xen_guest"); + if ( ELF_HANDLE_VALID(shdr) ) { - shdr = elf_shdr_by_name(elf, "__xen_guest"); - if ( shdr ) - { - parms->guest_info = elf_section_start(elf, shdr); - parms->elf_note_start = NULL; - parms->elf_note_end = NULL; - elf_msg(elf, "%s: __xen_guest: \"%s\"\n", __FUNCTION__, - parms->guest_info); - elf_xen_parse_guest_info(elf, parms); - break; - } + parms->guest_info = elf_section_start(elf, shdr); + parms->elf_note_start = ELF_INVALID_PTRVAL; + parms->elf_note_end = ELF_INVALID_PTRVAL; + elf_msg(elf, "%s: __xen_guest: \"%s\"\n", __FUNCTION__, + elf_strfmt(elf, parms->guest_info)); + elf_xen_parse_guest_info(elf, parms); } } diff --git a/xen/common/libelf/libelf-loader.c b/xen/common/libelf/libelf-loader.c index ab58b8b..e2e75af 100644 --- a/xen/common/libelf/libelf-loader.c +++ b/xen/common/libelf/libelf-loader.c @@ -16,27 +16,33 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifdef __XEN__ +#include +#endif + #include "libelf-private.h" /* ------------------------------------------------------------------------ */ -int elf_init(struct elf_binary *elf, const char *image, size_t size) +elf_errorstatus elf_init(struct elf_binary *elf, const char *image_input, size_t size) { - const elf_shdr *shdr; + ELF_HANDLE_DECL(elf_shdr) shdr; uint64_t i, count, section, offset; - if ( !elf_is_elfbinary(image) ) + if ( !elf_is_elfbinary(image_input, size) ) { elf_err(elf, "%s: not an ELF binary\n", __FUNCTION__); return -1; } - memset(elf, 0, sizeof(*elf)); - elf->image = image; + elf_memset_unchecked(elf, 0, sizeof(*elf)); + elf->image_base = image_input; elf->size = size; - elf->ehdr = (elf_ehdr *)image; - elf->class = elf->ehdr->e32.e_ident[EI_CLASS]; - elf->data = elf->ehdr->e32.e_ident[EI_DATA]; + elf->ehdr = ELF_MAKE_HANDLE(elf_ehdr, (elf_ptrval)image_input); + elf->class = elf_uval_3264(elf, elf->ehdr, e32.e_ident[EI_CLASS]); + elf->data = elf_uval_3264(elf, elf->ehdr, e32.e_ident[EI_DATA]); + elf->caller_xdest_base = NULL; + elf->caller_xdest_size = 0; /* Sanity check phdr. */ offset = elf_uval(elf, elf->ehdr, e_phoff) + @@ -61,7 +67,7 @@ int elf_init(struct elf_binary *elf, const char *image, size_t size) /* Find section string table. */ section = elf_uval(elf, elf->ehdr, e_shstrndx); shdr = elf_shdr_by_index(elf, section); - if ( shdr != NULL ) + if ( ELF_HANDLE_VALID(shdr) ) elf->sec_strtab = elf_section_start(elf, shdr); /* Find symbol table and symbol string table. */ @@ -69,13 +75,16 @@ int elf_init(struct elf_binary *elf, const char *image, size_t size) for ( i = 0; i < count; i++ ) { shdr = elf_shdr_by_index(elf, i); + if ( !elf_access_ok(elf, ELF_HANDLE_PTRVAL(shdr), 1) ) + /* input has an insane section header count field */ + break; if ( elf_uval(elf, shdr, sh_type) != SHT_SYMTAB ) continue; elf->sym_tab = shdr; shdr = elf_shdr_by_index(elf, elf_uval(elf, shdr, sh_link)); - if ( shdr == NULL ) + if ( !ELF_HANDLE_VALID(shdr) ) { - elf->sym_tab = NULL; + elf->sym_tab = ELF_INVALID_HANDLE(elf_shdr); continue; } elf->sym_strtab = elf_section_start(elf, shdr); @@ -86,7 +95,7 @@ int elf_init(struct elf_binary *elf, const char *image, size_t size) } #ifndef __XEN__ -void elf_call_log_callback(struct elf_binary *elf, int iserr, +void elf_call_log_callback(struct elf_binary *elf, bool iserr, const char *fmt,...) { va_list al; @@ -101,36 +110,39 @@ void elf_call_log_callback(struct elf_binary *elf, int iserr, } void elf_set_log(struct elf_binary *elf, elf_log_callback *log_callback, - void *log_caller_data, int verbose) + void *log_caller_data, bool verbose) { elf->log_callback = log_callback; elf->log_caller_data = log_caller_data; elf->verbose = verbose; } -static int elf_load_image(void *dst, const void *src, uint64_t filesz, uint64_t memsz) +static elf_errorstatus elf_load_image(struct elf_binary *elf, + elf_ptrval dst, elf_ptrval src, + uint64_t filesz, uint64_t memsz) { - memcpy(dst, src, filesz); - memset(dst + filesz, 0, memsz - filesz); + elf_memcpy_safe(elf, dst, src, filesz); + elf_memset_safe(elf, dst + filesz, 0, memsz - filesz); return 0; } #else -#include void elf_set_verbose(struct elf_binary *elf) { elf->verbose = 1; } -static int elf_load_image(void *dst, const void *src, uint64_t filesz, uint64_t memsz) +static elf_errorstatus elf_load_image(struct elf_binary *elf, elf_ptrval dst, elf_ptrval src, uint64_t filesz, uint64_t memsz) { - int rc; + elf_errorstatus rc; if ( filesz > ULONG_MAX || memsz > ULONG_MAX ) return -1; - rc = raw_copy_to_guest(dst, src, filesz); + /* We trust the dom0 kernel image completely, so we don't care + * about overruns etc. here. */ + rc = raw_copy_to_guest(ELF_UNSAFE_PTR(dst), ELF_UNSAFE_PTR(src), filesz); if ( rc != 0 ) return -1; - rc = raw_clear_guest(dst + filesz, memsz - filesz); + rc = raw_clear_guest(ELF_UNSAFE_PTR(dst + filesz), memsz - filesz); if ( rc != 0 ) return -1; return 0; @@ -141,10 +153,10 @@ static int elf_load_image(void *dst, const void *src, uint64_t filesz, uint64_t void elf_parse_bsdsyms(struct elf_binary *elf, uint64_t pstart) { uint64_t sz; - const elf_shdr *shdr; - int i, type; + ELF_HANDLE_DECL(elf_shdr) shdr; + unsigned i, type; - if ( !elf->sym_tab ) + if ( !ELF_HANDLE_VALID(elf->sym_tab) ) return; pstart = elf_round_up(elf, pstart); @@ -161,7 +173,10 @@ void elf_parse_bsdsyms(struct elf_binary *elf, uint64_t pstart) for ( i = 0; i < elf_shdr_count(elf); i++ ) { shdr = elf_shdr_by_index(elf, i); - type = elf_uval(elf, (elf_shdr *)shdr, sh_type); + if ( !elf_access_ok(elf, ELF_HANDLE_PTRVAL(shdr), 1) ) + /* input has an insane section header count field */ + break; + type = elf_uval(elf, shdr, sh_type); if ( (type == SHT_STRTAB) || (type == SHT_SYMTAB) ) sz = elf_round_up(elf, sz + elf_uval(elf, shdr, sh_size)); } @@ -172,11 +187,13 @@ void elf_parse_bsdsyms(struct elf_binary *elf, uint64_t pstart) static void elf_load_bsdsyms(struct elf_binary *elf) { - elf_ehdr *sym_ehdr; + ELF_HANDLE_DECL(elf_ehdr) sym_ehdr; unsigned long sz; - char *maxva, *symbase, *symtab_addr; - elf_shdr *shdr; - int i, type; + elf_ptrval maxva; + elf_ptrval symbase; + elf_ptrval symtab_addr; + ELF_HANDLE_DECL(elf_shdr) shdr; + unsigned i, type; if ( !elf->bsd_symtab_pstart ) return; @@ -184,18 +201,18 @@ static void elf_load_bsdsyms(struct elf_binary *elf) #define elf_hdr_elm(_elf, _hdr, _elm, _val) \ do { \ if ( elf_64bit(_elf) ) \ - (_hdr)->e64._elm = _val; \ + elf_store_field(_elf, _hdr, e64._elm, _val); \ else \ - (_hdr)->e32._elm = _val; \ + elf_store_field(_elf, _hdr, e32._elm, _val); \ } while ( 0 ) symbase = elf_get_ptr(elf, elf->bsd_symtab_pstart); symtab_addr = maxva = symbase + sizeof(uint32_t); /* Set up Elf header. */ - sym_ehdr = (elf_ehdr *)symtab_addr; + sym_ehdr = ELF_MAKE_HANDLE(elf_ehdr, symtab_addr); sz = elf_uval(elf, elf->ehdr, e_ehsize); - memcpy(sym_ehdr, elf->ehdr, sz); + elf_memcpy_safe(elf, ELF_HANDLE_PTRVAL(sym_ehdr), ELF_HANDLE_PTRVAL(elf->ehdr), sz); maxva += sz; /* no round up */ elf_hdr_elm(elf, sym_ehdr, e_phoff, 0); @@ -204,37 +221,50 @@ do { \ elf_hdr_elm(elf, sym_ehdr, e_phnum, 0); /* Copy Elf section headers. */ - shdr = (elf_shdr *)maxva; + shdr = ELF_MAKE_HANDLE(elf_shdr, maxva); sz = elf_shdr_count(elf) * elf_uval(elf, elf->ehdr, e_shentsize); - memcpy(shdr, elf->image + elf_uval(elf, elf->ehdr, e_shoff), sz); - maxva = (char *)(long)elf_round_up(elf, (long)maxva + sz); + elf_memcpy_safe(elf, ELF_HANDLE_PTRVAL(shdr), + ELF_IMAGE_BASE(elf) + elf_uval(elf, elf->ehdr, e_shoff), + sz); + maxva = elf_round_up(elf, (unsigned long)maxva + sz); for ( i = 0; i < elf_shdr_count(elf); i++ ) { + elf_ptrval old_shdr_p; + elf_ptrval new_shdr_p; + type = elf_uval(elf, shdr, sh_type); if ( (type == SHT_STRTAB) || (type == SHT_SYMTAB) ) { - elf_msg(elf, "%s: shdr %i at 0x%p -> 0x%p\n", __func__, i, + elf_msg(elf, "%s: shdr %i at 0x%"ELF_PRPTRVAL" -> 0x%"ELF_PRPTRVAL"\n", __func__, i, elf_section_start(elf, shdr), maxva); sz = elf_uval(elf, shdr, sh_size); - memcpy(maxva, elf_section_start(elf, shdr), sz); + elf_memcpy_safe(elf, maxva, elf_section_start(elf, shdr), sz); /* Mangled to be based on ELF header location. */ elf_hdr_elm(elf, shdr, sh_offset, maxva - symtab_addr); - maxva = (char *)(long)elf_round_up(elf, (long)maxva + sz); + maxva = elf_round_up(elf, (unsigned long)maxva + sz); } - shdr = (elf_shdr *)((long)shdr + - (long)elf_uval(elf, elf->ehdr, e_shentsize)); + old_shdr_p = ELF_HANDLE_PTRVAL(shdr); + new_shdr_p = old_shdr_p + elf_uval(elf, elf->ehdr, e_shentsize); + if ( new_shdr_p <= old_shdr_p ) /* wrapped or stuck */ + { + elf_mark_broken(elf, "bad section header length"); + break; + } + if ( !elf_access_ok(elf, new_shdr_p, 1) ) /* outside image */ + break; + shdr = ELF_MAKE_HANDLE(elf_shdr, new_shdr_p); } /* Write down the actual sym size. */ - *(uint32_t *)symbase = maxva - symtab_addr; + elf_store_val(elf, uint32_t, symbase, maxva - symtab_addr); #undef elf_ehdr_elm } void elf_parse_binary(struct elf_binary *elf) { - const elf_phdr *phdr; + ELF_HANDLE_DECL(elf_phdr) phdr; uint64_t low = -1; uint64_t high = 0; uint64_t i, count, paddr, memsz; @@ -243,6 +273,9 @@ void elf_parse_binary(struct elf_binary *elf) for ( i = 0; i < count; i++ ) { phdr = elf_phdr_by_index(elf, i); + if ( !elf_access_ok(elf, ELF_HANDLE_PTRVAL(phdr), 1) ) + /* input has an insane program header count field */ + break; if ( !elf_phdr_is_loadable(elf, phdr) ) continue; paddr = elf_uval(elf, phdr, p_paddr); @@ -260,16 +293,25 @@ void elf_parse_binary(struct elf_binary *elf) __FUNCTION__, elf->pstart, elf->pend); } -int elf_load_binary(struct elf_binary *elf) +elf_errorstatus elf_load_binary(struct elf_binary *elf) { - const elf_phdr *phdr; + ELF_HANDLE_DECL(elf_phdr) phdr; uint64_t i, count, paddr, offset, filesz, memsz; - char *dest; + elf_ptrval dest; + /* + * Let bizarre ELFs write the output image up to twice; this + * calculation is just to ensure our copying loop is no worse than + * O(domain_size). + */ + uint64_t remain_allow_copy = (uint64_t)elf->dest_size * 2; count = elf_uval(elf, elf->ehdr, e_phnum); for ( i = 0; i < count; i++ ) { phdr = elf_phdr_by_index(elf, i); + if ( !elf_access_ok(elf, ELF_HANDLE_PTRVAL(phdr), 1) ) + /* input has an insane program header count field */ + break; if ( !elf_phdr_is_loadable(elf, phdr) ) continue; paddr = elf_uval(elf, phdr, p_paddr); @@ -277,9 +319,23 @@ int elf_load_binary(struct elf_binary *elf) filesz = elf_uval(elf, phdr, p_filesz); memsz = elf_uval(elf, phdr, p_memsz); dest = elf_get_ptr(elf, paddr); - elf_msg(elf, "%s: phdr %" PRIu64 " at 0x%p -> 0x%p\n", - __func__, i, dest, dest + filesz); - if ( elf_load_image(dest, elf->image + offset, filesz, memsz) != 0 ) + + /* + * We need to check that the input image doesn't have us copy + * the whole image zillions of times, as that could lead to + * O(n^2) time behaviour and possible DoS by a malicous ELF. + */ + if ( remain_allow_copy < memsz ) + { + elf_mark_broken(elf, "program segments total to more" + " than the input image size"); + break; + } + remain_allow_copy -= memsz; + + elf_msg(elf, "%s: phdr %" PRIu64 " at 0x%"ELF_PRPTRVAL" -> 0x%"ELF_PRPTRVAL"\n", + __func__, i, dest, (elf_ptrval)(dest + filesz)); + if ( elf_load_image(elf, dest, ELF_IMAGE_BASE(elf) + offset, filesz, memsz) != 0 ) return -1; } @@ -287,18 +343,18 @@ int elf_load_binary(struct elf_binary *elf) return 0; } -void *elf_get_ptr(struct elf_binary *elf, unsigned long addr) +elf_ptrval elf_get_ptr(struct elf_binary *elf, unsigned long addr) { - return elf->dest + addr - elf->pstart; + return ELF_REALPTR2PTRVAL(elf->dest_base) + addr - elf->pstart; } uint64_t elf_lookup_addr(struct elf_binary * elf, const char *symbol) { - const elf_sym *sym; + ELF_HANDLE_DECL(elf_sym) sym; uint64_t value; sym = elf_sym_by_name(elf, symbol); - if ( sym == NULL ) + if ( !ELF_HANDLE_VALID(sym) ) { elf_err(elf, "%s: not found: %s\n", __FUNCTION__, symbol); return -1; diff --git a/xen/common/libelf/libelf-private.h b/xen/common/libelf/libelf-private.h index 3ef753c..277be04 100644 --- a/xen/common/libelf/libelf-private.h +++ b/xen/common/libelf/libelf-private.h @@ -77,7 +77,7 @@ #define elf_err(elf, fmt, args ... ) \ elf_call_log_callback(elf, 1, fmt , ## args ); -void elf_call_log_callback(struct elf_binary*, int iserr, const char *fmt,...); +void elf_call_log_callback(struct elf_binary*, bool iserr, const char *fmt,...); #define safe_strcpy(d,s) \ do { strncpy((d),(s),sizeof((d))-1); \ @@ -86,6 +86,19 @@ do { strncpy((d),(s),sizeof((d))-1); \ #endif +#undef memcpy +#undef memset +#undef memmove +#undef strcpy + +#define memcpy MISTAKE_unspecified_memcpy +#define memset MISTAKE_unspecified_memset +#define memmove MISTAKE_unspecified_memmove +#define strcpy MISTAKE_unspecified_strcpy + /* This prevents libelf from using these undecorated versions + * of memcpy, memset, memmove and strcpy. Every call site + * must either use elf_mem*_unchecked, or elf_mem*_safe. */ + #endif /* __LIBELF_PRIVATE_H_ */ /* diff --git a/xen/common/libelf/libelf-relocate.c b/xen/common/libelf/libelf-relocate.c deleted file mode 100644 index 7ef4b01..0000000 --- a/xen/common/libelf/libelf-relocate.c +++ /dev/null @@ -1,372 +0,0 @@ -/* - * ELF relocation code (not used by xen kernel right now). - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "libelf-private.h" - -/* ------------------------------------------------------------------------ */ - -static const char *rel_names_i386[] = { - "R_386_NONE", - "R_386_32", - "R_386_PC32", - "R_386_GOT32", - "R_386_PLT32", - "R_386_COPY", - "R_386_GLOB_DAT", - "R_386_JMP_SLOT", - "R_386_RELATIVE", - "R_386_GOTOFF", - "R_386_GOTPC", - "R_386_32PLT", - "R_386_TLS_TPOFF", - "R_386_TLS_IE", - "R_386_TLS_GOTIE", - "R_386_TLS_LE", - "R_386_TLS_GD", - "R_386_TLS_LDM", - "R_386_16", - "R_386_PC16", - "R_386_8", - "R_386_PC8", - "R_386_TLS_GD_32", - "R_386_TLS_GD_PUSH", - "R_386_TLS_GD_CALL", - "R_386_TLS_GD_POP", - "R_386_TLS_LDM_32", - "R_386_TLS_LDM_PUSH", - "R_386_TLS_LDM_CALL", - "R_386_TLS_LDM_POP", - "R_386_TLS_LDO_32", - "R_386_TLS_IE_32", - "R_386_TLS_LE_32", - "R_386_TLS_DTPMOD32", - "R_386_TLS_DTPOFF32", - "R_386_TLS_TPOFF32", -}; - -static int elf_reloc_i386(struct elf_binary *elf, int type, - uint64_t addr, uint64_t value) -{ - void *ptr = elf_get_ptr(elf, addr); - uint32_t *u32; - - switch ( type ) - { - case 1 /* R_386_32 */ : - u32 = ptr; - *u32 += elf->reloc_offset; - break; - case 2 /* R_386_PC32 */ : - /* nothing */ - break; - default: - return -1; - } - return 0; -} - -/* ------------------------------------------------------------------------ */ - -static const char *rel_names_x86_64[] = { - "R_X86_64_NONE", - "R_X86_64_64", - "R_X86_64_PC32", - "R_X86_64_GOT32", - "R_X86_64_PLT32", - "R_X86_64_COPY", - "R_X86_64_GLOB_DAT", - "R_X86_64_JUMP_SLOT", - "R_X86_64_RELATIVE", - "R_X86_64_GOTPCREL", - "R_X86_64_32", - "R_X86_64_32S", - "R_X86_64_16", - "R_X86_64_PC16", - "R_X86_64_8", - "R_X86_64_PC8", - "R_X86_64_DTPMOD64", - "R_X86_64_DTPOFF64", - "R_X86_64_TPOFF64", - "R_X86_64_TLSGD", - "R_X86_64_TLSLD", - "R_X86_64_DTPOFF32", - "R_X86_64_GOTTPOFF", - "R_X86_64_TPOFF32", -}; - -static int elf_reloc_x86_64(struct elf_binary *elf, int type, - uint64_t addr, uint64_t value) -{ - void *ptr = elf_get_ptr(elf, addr); - uint64_t *u64; - uint32_t *u32; - int32_t *s32; - - switch ( type ) - { - case 1 /* R_X86_64_64 */ : - u64 = ptr; - value += elf->reloc_offset; - *u64 = value; - break; - case 2 /* R_X86_64_PC32 */ : - u32 = ptr; - *u32 = value - addr; - if ( *u32 != (uint32_t)(value - addr) ) - { - elf_err(elf, "R_X86_64_PC32 overflow: 0x%" PRIx32 - " != 0x%" PRIx32 "\n", - *u32, (uint32_t) (value - addr)); - return -1; - } - break; - case 10 /* R_X86_64_32 */ : - u32 = ptr; - value += elf->reloc_offset; - *u32 = value; - if ( *u32 != value ) - { - elf_err(elf, "R_X86_64_32 overflow: 0x%" PRIx32 - " != 0x%" PRIx64 "\n", - *u32, value); - return -1; - } - break; - case 11 /* R_X86_64_32S */ : - s32 = ptr; - value += elf->reloc_offset; - *s32 = value; - if ( *s32 != (int64_t) value ) - { - elf_err(elf, "R_X86_64_32S overflow: 0x%" PRIx32 - " != 0x%" PRIx64 "\n", - *s32, (int64_t) value); - return -1; - } - break; - default: - return -1; - } - return 0; -} - -/* ------------------------------------------------------------------------ */ - -static struct relocs { - const char **names; - int count; - int (*func) (struct elf_binary * elf, int type, uint64_t addr, - uint64_t value); -} relocs[] = -/* *INDENT-OFF* */ -{ - [EM_386] = { - .names = rel_names_i386, - .count = sizeof(rel_names_i386) / sizeof(rel_names_i386[0]), - .func = elf_reloc_i386, - }, - [EM_X86_64] = { - .names = rel_names_x86_64, - .count = sizeof(rel_names_x86_64) / sizeof(rel_names_x86_64[0]), - .func = elf_reloc_x86_64, - } -}; -/* *INDENT-ON* */ - -/* ------------------------------------------------------------------------ */ - -static const char *rela_name(int machine, int type) -{ - if ( machine > sizeof(relocs) / sizeof(relocs[0]) ) - return "unknown mach"; - if ( !relocs[machine].names ) - return "unknown mach"; - if ( type > relocs[machine].count ) - return "unknown rela"; - return relocs[machine].names[type]; -} - -static int elf_reloc_section(struct elf_binary *elf, - const elf_shdr * rels, - const elf_shdr * sect, const elf_shdr * syms) -{ - const void *ptr, *end; - const elf_shdr *shdr; - const elf_rela *rela; - const elf_rel *rel; - const elf_sym *sym; - uint64_t s_type; - uint64_t r_offset; - uint64_t r_info; - uint64_t r_addend; - int r_type, r_sym; - size_t rsize; - uint64_t shndx, sbase, addr, value; - const char *sname; - int machine; - - machine = elf_uval(elf, elf->ehdr, e_machine); - if ( (machine >= (sizeof(relocs) / sizeof(relocs[0]))) || - (relocs[machine].func == NULL) ) - { - elf_err(elf, "%s: can't handle machine %d\n", - __FUNCTION__, machine); - return -1; - } - if ( elf_swap(elf) ) - { - elf_err(elf, "%s: non-native byte order, relocation not supported\n", - __FUNCTION__); - return -1; - } - - s_type = elf_uval(elf, rels, sh_type); - rsize = (SHT_REL == s_type) ? elf_size(elf, rel) : elf_size(elf, rela); - ptr = elf_section_start(elf, rels); - end = elf_section_end(elf, rels); - - for ( ; ptr < end; ptr += rsize ) - { - switch ( s_type ) - { - case SHT_REL: - rel = ptr; - r_offset = elf_uval(elf, rel, r_offset); - r_info = elf_uval(elf, rel, r_info); - r_addend = 0; - break; - case SHT_RELA: - rela = ptr; - r_offset = elf_uval(elf, rela, r_offset); - r_info = elf_uval(elf, rela, r_info); - r_addend = elf_uval(elf, rela, r_addend); - break; - default: - /* can't happen */ - return -1; - } - if ( elf_64bit(elf) ) - { - r_type = ELF64_R_TYPE(r_info); - r_sym = ELF64_R_SYM(r_info); - } - else - { - r_type = ELF32_R_TYPE(r_info); - r_sym = ELF32_R_SYM(r_info); - } - - sym = elf_sym_by_index(elf, r_sym); - shndx = elf_uval(elf, sym, st_shndx); - switch ( shndx ) - { - case SHN_UNDEF: - sname = "*UNDEF*"; - sbase = 0; - break; - case SHN_COMMON: - elf_err(elf, "%s: invalid section: %" PRId64 "\n", - __FUNCTION__, shndx); - return -1; - case SHN_ABS: - sname = "*ABS*"; - sbase = 0; - break; - default: - shdr = elf_shdr_by_index(elf, shndx); - if ( shdr == NULL ) - { - elf_err(elf, "%s: invalid section: %" PRId64 "\n", - __FUNCTION__, shndx); - return -1; - } - sname = elf_section_name(elf, shdr); - sbase = elf_uval(elf, shdr, sh_addr); - } - - addr = r_offset; - value = elf_uval(elf, sym, st_value); - value += r_addend; - - if ( elf->log_callback && (elf->verbose > 1) ) - { - uint64_t st_name = elf_uval(elf, sym, st_name); - const char *name = st_name ? elf->sym_strtab + st_name : "*NONE*"; - - elf_msg(elf, - "%s: type %s [%d], off 0x%" PRIx64 ", add 0x%" PRIx64 "," - " sym %s [0x%" PRIx64 "], sec %s [0x%" PRIx64 "]" - " -> addr 0x%" PRIx64 " value 0x%" PRIx64 "\n", - __FUNCTION__, rela_name(machine, r_type), r_type, r_offset, - r_addend, name, elf_uval(elf, sym, st_value), sname, sbase, - addr, value); - } - - if ( relocs[machine].func(elf, r_type, addr, value) == -1 ) - { - elf_err(elf, "%s: unknown/unsupported reloc type %s [%d]\n", - __FUNCTION__, rela_name(machine, r_type), r_type); - return -1; - } - } - return 0; -} - -int elf_reloc(struct elf_binary *elf) -{ - const elf_shdr *rels, *sect, *syms; - uint64_t i, count, type; - - count = elf_shdr_count(elf); - for ( i = 0; i < count; i++ ) - { - rels = elf_shdr_by_index(elf, i); - type = elf_uval(elf, rels, sh_type); - if ( (type != SHT_REL) && (type != SHT_RELA) ) - continue; - - sect = elf_shdr_by_index(elf, elf_uval(elf, rels, sh_info)); - syms = elf_shdr_by_index(elf, elf_uval(elf, rels, sh_link)); - if ( NULL == sect || NULL == syms ) - continue; - - if ( !(elf_uval(elf, sect, sh_flags) & SHF_ALLOC) ) - { - elf_msg(elf, "%s: relocations for %s, skipping\n", - __FUNCTION__, elf_section_name(elf, sect)); - continue; - } - - elf_msg(elf, "%s: relocations for %s @ 0x%" PRIx64 "\n", - __FUNCTION__, elf_section_name(elf, sect), - elf_uval(elf, sect, sh_addr)); - if ( elf_reloc_section(elf, rels, sect, syms) != 0 ) - return -1; - } - return 0; -} - -/* - * Local variables: - * mode: C - * c-set-style: "BSD" - * c-basic-offset: 4 - * tab-width: 4 - * indent-tabs-mode: nil - * End: - */ diff --git a/xen/common/libelf/libelf-tools.c b/xen/common/libelf/libelf-tools.c index cb97908..e202249 100644 --- a/xen/common/libelf/libelf-tools.c +++ b/xen/common/libelf/libelf-tools.c @@ -20,201 +20,292 @@ /* ------------------------------------------------------------------------ */ -uint64_t elf_access_unsigned(struct elf_binary * elf, const void *ptr, - uint64_t offset, size_t size) +void elf_mark_broken(struct elf_binary *elf, const char *msg) { - int need_swap = elf_swap(elf); + if ( elf->broken == NULL ) + elf->broken = msg; +} + +const char *elf_check_broken(const struct elf_binary *elf) +{ + return elf->broken; +} + +static bool elf_ptrval_in_range(elf_ptrval ptrval, uint64_t size, + const void *region, uint64_t regionsize) + /* + * Returns true if the putative memory area [ptrval,ptrval+size> + * is completely inside the region [region,region+regionsize>. + * + * ptrval and size are the untrusted inputs to be checked. + * region and regionsize are trusted and must be correct and valid, + * although it is OK for region to perhaps be maliciously NULL + * (but not some other malicious value). + */ +{ + elf_ptrval regionp = (elf_ptrval)region; + + if ( (region == NULL) || + (ptrval < regionp) || /* start is before region */ + (ptrval > regionp + regionsize) || /* start is after region */ + (size > regionsize - (ptrval - regionp)) ) /* too big */ + return 0; + return 1; +} + +bool elf_access_ok(struct elf_binary * elf, + uint64_t ptrval, size_t size) +{ + if ( elf_ptrval_in_range(ptrval, size, elf->image_base, elf->size) ) + return 1; + if ( elf_ptrval_in_range(ptrval, size, elf->dest_base, elf->dest_size) ) + return 1; + if ( elf_ptrval_in_range(ptrval, size, + elf->caller_xdest_base, elf->caller_xdest_size) ) + return 1; + elf_mark_broken(elf, "out of range access"); + return 0; +} + +void elf_memcpy_safe(struct elf_binary *elf, elf_ptrval dst, + elf_ptrval src, size_t size) +{ + if ( elf_access_ok(elf, dst, size) && + elf_access_ok(elf, src, size) ) + { + /* use memmove because these checks do not prove that the + * regions don't overlap and overlapping regions grant + * permission for compiler malice */ + elf_memmove_unchecked(ELF_UNSAFE_PTR(dst), ELF_UNSAFE_PTR(src), size); + } +} + +void elf_memset_safe(struct elf_binary *elf, elf_ptrval dst, int c, size_t size) +{ + if ( elf_access_ok(elf, dst, size) ) + { + elf_memset_unchecked(ELF_UNSAFE_PTR(dst), c, size); + } +} + +uint64_t elf_access_unsigned(struct elf_binary * elf, elf_ptrval base, + uint64_t moreoffset, size_t size) +{ + elf_ptrval ptrval = base + moreoffset; + bool need_swap = elf_swap(elf); const uint8_t *u8; const uint16_t *u16; const uint32_t *u32; const uint64_t *u64; + if ( !elf_access_ok(elf, ptrval, size) ) + return 0; + switch ( size ) { case 1: - u8 = ptr + offset; + u8 = (const void*)ptrval; return *u8; case 2: - u16 = ptr + offset; + u16 = (const void*)ptrval; return need_swap ? bswap_16(*u16) : *u16; case 4: - u32 = ptr + offset; + u32 = (const void*)ptrval; return need_swap ? bswap_32(*u32) : *u32; case 8: - u64 = ptr + offset; + u64 = (const void*)ptrval; return need_swap ? bswap_64(*u64) : *u64; default: return 0; } } -int64_t elf_access_signed(struct elf_binary *elf, const void *ptr, - uint64_t offset, size_t size) -{ - int need_swap = elf_swap(elf); - const int8_t *s8; - const int16_t *s16; - const int32_t *s32; - const int64_t *s64; - - switch ( size ) - { - case 1: - s8 = ptr + offset; - return *s8; - case 2: - s16 = ptr + offset; - return need_swap ? bswap_16(*s16) : *s16; - case 4: - s32 = ptr + offset; - return need_swap ? bswap_32(*s32) : *s32; - case 8: - s64 = ptr + offset; - return need_swap ? bswap_64(*s64) : *s64; - default: - return 0; - } -} - uint64_t elf_round_up(struct elf_binary *elf, uint64_t addr) { - int elf_round = (elf_64bit(elf) ? 8 : 4) - 1; + uint64_t elf_round = (elf_64bit(elf) ? 8 : 4) - 1; return (addr + elf_round) & ~elf_round; } /* ------------------------------------------------------------------------ */ -int elf_shdr_count(struct elf_binary *elf) +unsigned elf_shdr_count(struct elf_binary *elf) { - return elf_uval(elf, elf->ehdr, e_shnum); + unsigned count = elf_uval(elf, elf->ehdr, e_shnum); + uint64_t max = elf->size / sizeof(Elf32_Shdr); + if (max > ~(unsigned)0) + max = ~(unsigned)0; /* Xen doesn't have limits.h :-/ */ + if (count > max) + { + elf_mark_broken(elf, "far too many section headers"); + count = max; + } + return count; } -int elf_phdr_count(struct elf_binary *elf) +unsigned elf_phdr_count(struct elf_binary *elf) { return elf_uval(elf, elf->ehdr, e_phnum); } -const elf_shdr *elf_shdr_by_name(struct elf_binary *elf, const char *name) +ELF_HANDLE_DECL(elf_shdr) elf_shdr_by_name(struct elf_binary *elf, const char *name) { uint64_t count = elf_shdr_count(elf); - const elf_shdr *shdr; + ELF_HANDLE_DECL(elf_shdr) shdr; const char *sname; - int i; + unsigned i; for ( i = 0; i < count; i++ ) { shdr = elf_shdr_by_index(elf, i); + if ( !elf_access_ok(elf, ELF_HANDLE_PTRVAL(shdr), 1) ) + /* input has an insane section header count field */ + break; sname = elf_section_name(elf, shdr); if ( sname && !strcmp(sname, name) ) return shdr; } - return NULL; + return ELF_INVALID_HANDLE(elf_shdr); } -const elf_shdr *elf_shdr_by_index(struct elf_binary *elf, int index) +ELF_HANDLE_DECL(elf_shdr) elf_shdr_by_index(struct elf_binary *elf, unsigned index) { uint64_t count = elf_shdr_count(elf); - const void *ptr; + elf_ptrval ptr; if ( index >= count ) - return NULL; + return ELF_INVALID_HANDLE(elf_shdr); - ptr = (elf->image + ptr = (ELF_IMAGE_BASE(elf) + elf_uval(elf, elf->ehdr, e_shoff) + elf_uval(elf, elf->ehdr, e_shentsize) * index); - return ptr; + return ELF_MAKE_HANDLE(elf_shdr, ptr); } -const elf_phdr *elf_phdr_by_index(struct elf_binary *elf, int index) +ELF_HANDLE_DECL(elf_phdr) elf_phdr_by_index(struct elf_binary *elf, unsigned index) { uint64_t count = elf_uval(elf, elf->ehdr, e_phnum); - const void *ptr; + elf_ptrval ptr; if ( index >= count ) - return NULL; + return ELF_INVALID_HANDLE(elf_phdr); - ptr = (elf->image + ptr = (ELF_IMAGE_BASE(elf) + elf_uval(elf, elf->ehdr, e_phoff) + elf_uval(elf, elf->ehdr, e_phentsize) * index); - return ptr; + return ELF_MAKE_HANDLE(elf_phdr, ptr); } -const char *elf_section_name(struct elf_binary *elf, const elf_shdr * shdr) + +const char *elf_section_name(struct elf_binary *elf, + ELF_HANDLE_DECL(elf_shdr) shdr) { - if ( elf->sec_strtab == NULL ) + if ( ELF_PTRVAL_INVALID(elf->sec_strtab) ) return "unknown"; - return elf->sec_strtab + elf_uval(elf, shdr, sh_name); + + return elf_strval(elf, elf->sec_strtab + elf_uval(elf, shdr, sh_name)); } -const void *elf_section_start(struct elf_binary *elf, const elf_shdr * shdr) +const char *elf_strval(struct elf_binary *elf, elf_ptrval start) { - return elf->image + elf_uval(elf, shdr, sh_offset); + uint64_t length; + + for ( length = 0; ; length++ ) { + if ( !elf_access_ok(elf, start + length, 1) ) + return NULL; + if ( !elf_access_unsigned(elf, start, length, 1) ) + /* ok */ + return ELF_UNSAFE_PTR(start); + if ( length >= ELF_MAX_STRING_LENGTH ) + { + elf_mark_broken(elf, "excessively long string"); + return NULL; + } + } } -const void *elf_section_end(struct elf_binary *elf, const elf_shdr * shdr) +const char *elf_strfmt(struct elf_binary *elf, elf_ptrval start) { - return elf->image + const char *str = elf_strval(elf, start); + + if ( str == NULL ) + return "(invalid)"; + return str; +} + +elf_ptrval elf_section_start(struct elf_binary *elf, ELF_HANDLE_DECL(elf_shdr) shdr) +{ + return ELF_IMAGE_BASE(elf) + elf_uval(elf, shdr, sh_offset); +} + +elf_ptrval elf_section_end(struct elf_binary *elf, ELF_HANDLE_DECL(elf_shdr) shdr) +{ + return ELF_IMAGE_BASE(elf) + elf_uval(elf, shdr, sh_offset) + elf_uval(elf, shdr, sh_size); } -const void *elf_segment_start(struct elf_binary *elf, const elf_phdr * phdr) +elf_ptrval elf_segment_start(struct elf_binary *elf, ELF_HANDLE_DECL(elf_phdr) phdr) { - return elf->image + elf_uval(elf, phdr, p_offset); + return ELF_IMAGE_BASE(elf) + + elf_uval(elf, phdr, p_offset); } -const void *elf_segment_end(struct elf_binary *elf, const elf_phdr * phdr) +elf_ptrval elf_segment_end(struct elf_binary *elf, ELF_HANDLE_DECL(elf_phdr) phdr) { - return elf->image + return ELF_IMAGE_BASE(elf) + elf_uval(elf, phdr, p_offset) + elf_uval(elf, phdr, p_filesz); } -const elf_sym *elf_sym_by_name(struct elf_binary *elf, const char *symbol) +ELF_HANDLE_DECL(elf_sym) elf_sym_by_name(struct elf_binary *elf, const char *symbol) { - const void *ptr = elf_section_start(elf, elf->sym_tab); - const void *end = elf_section_end(elf, elf->sym_tab); - const elf_sym *sym; + elf_ptrval ptr = elf_section_start(elf, elf->sym_tab); + elf_ptrval end = elf_section_end(elf, elf->sym_tab); + ELF_HANDLE_DECL(elf_sym) sym; uint64_t info, name; + const char *sym_name; for ( ; ptr < end; ptr += elf_size(elf, sym) ) { - sym = ptr; + sym = ELF_MAKE_HANDLE(elf_sym, ptr); info = elf_uval(elf, sym, st_info); name = elf_uval(elf, sym, st_name); if ( ELF32_ST_BIND(info) != STB_GLOBAL ) continue; - if ( strcmp(elf->sym_strtab + name, symbol) ) + sym_name = elf_strval(elf, elf->sym_strtab + name); + if ( sym_name == NULL ) /* out of range, oops */ + return ELF_INVALID_HANDLE(elf_sym); + if ( strcmp(sym_name, symbol) ) continue; return sym; } - return NULL; + return ELF_INVALID_HANDLE(elf_sym); } -const elf_sym *elf_sym_by_index(struct elf_binary *elf, int index) +ELF_HANDLE_DECL(elf_sym) elf_sym_by_index(struct elf_binary *elf, unsigned index) { - const void *ptr = elf_section_start(elf, elf->sym_tab); - const elf_sym *sym; + elf_ptrval ptr = elf_section_start(elf, elf->sym_tab); + ELF_HANDLE_DECL(elf_sym) sym; - sym = ptr + index * elf_size(elf, sym); + sym = ELF_MAKE_HANDLE(elf_sym, ptr + index * elf_size(elf, sym)); return sym; } -const char *elf_note_name(struct elf_binary *elf, const elf_note * note) +const char *elf_note_name(struct elf_binary *elf, ELF_HANDLE_DECL(elf_note) note) { - return (void *)note + elf_size(elf, note); + return elf_strval(elf, ELF_HANDLE_PTRVAL(note) + elf_size(elf, note)); } -const void *elf_note_desc(struct elf_binary *elf, const elf_note * note) +elf_ptrval elf_note_desc(struct elf_binary *elf, ELF_HANDLE_DECL(elf_note) note) { - int namesz = (elf_uval(elf, note, namesz) + 3) & ~3; + unsigned namesz = (elf_uval(elf, note, namesz) + 3) & ~3; - return (void *)note + elf_size(elf, note) + namesz; + return ELF_HANDLE_PTRVAL(note) + elf_size(elf, note) + namesz; } -uint64_t elf_note_numeric(struct elf_binary *elf, const elf_note * note) +uint64_t elf_note_numeric(struct elf_binary *elf, ELF_HANDLE_DECL(elf_note) note) { - const void *desc = elf_note_desc(elf, note); - int descsz = elf_uval(elf, note, descsz); + elf_ptrval desc = elf_note_desc(elf, note); + unsigned descsz = elf_uval(elf, note, descsz); switch (descsz) { @@ -228,11 +319,11 @@ uint64_t elf_note_numeric(struct elf_binary *elf, const elf_note * note) } } -uint64_t elf_note_numeric_array(struct elf_binary *elf, const elf_note *note, +uint64_t elf_note_numeric_array(struct elf_binary *elf, ELF_HANDLE_DECL(elf_note) note, unsigned int unitsz, unsigned int idx) { - const void *desc = elf_note_desc(elf, note); - int descsz = elf_uval(elf, note, descsz); + elf_ptrval desc = elf_note_desc(elf, note); + unsigned descsz = elf_uval(elf, note, descsz); if ( descsz % unitsz || idx >= descsz / unitsz ) return 0; @@ -248,24 +339,34 @@ uint64_t elf_note_numeric_array(struct elf_binary *elf, const elf_note *note, } } -const elf_note *elf_note_next(struct elf_binary *elf, const elf_note * note) +ELF_HANDLE_DECL(elf_note) elf_note_next(struct elf_binary *elf, ELF_HANDLE_DECL(elf_note) note) { - int namesz = (elf_uval(elf, note, namesz) + 3) & ~3; - int descsz = (elf_uval(elf, note, descsz) + 3) & ~3; + unsigned namesz = (elf_uval(elf, note, namesz) + 3) & ~3; + unsigned descsz = (elf_uval(elf, note, descsz) + 3) & ~3; + + elf_ptrval ptrval = ELF_HANDLE_PTRVAL(note) + + elf_size(elf, note) + namesz + descsz; - return (void *)note + elf_size(elf, note) + namesz + descsz; + if ( ( ptrval <= ELF_HANDLE_PTRVAL(note) || /* wrapped or stuck */ + !elf_access_ok(elf, ELF_HANDLE_PTRVAL(note), 1) ) ) + ptrval = ELF_MAX_PTRVAL; /* terminate caller's loop */ + + return ELF_MAKE_HANDLE(elf_note, ptrval); } /* ------------------------------------------------------------------------ */ -int elf_is_elfbinary(const void *image) +bool elf_is_elfbinary(const void *image_start, size_t image_size) { - const Elf32_Ehdr *ehdr = image; + const Elf32_Ehdr *ehdr = image_start; + + if ( image_size < sizeof(*ehdr) ) + return 0; return IS_ELF(*ehdr); } -int elf_phdr_is_loadable(struct elf_binary *elf, const elf_phdr * phdr) +bool elf_phdr_is_loadable(struct elf_binary *elf, ELF_HANDLE_DECL(elf_phdr) phdr) { uint64_t p_type = elf_uval(elf, phdr, p_type); uint64_t p_flags = elf_uval(elf, phdr, p_flags); diff --git a/xen/include/xen/libelf.h b/xen/include/xen/libelf.h index e8f6508..174f8da 100644 --- a/xen/include/xen/libelf.h +++ b/xen/include/xen/libelf.h @@ -29,6 +29,11 @@ #error define architectural endianness #endif +#include + +typedef int elf_errorstatus; /* 0: ok; -ve (normally -1): error */ +typedef int elf_negerrnoval; /* 0: ok; -EFOO: error */ + #undef ELFSIZE #include "elfstructs.h" #ifdef __XEN__ @@ -42,12 +47,98 @@ struct elf_binary; typedef void elf_log_callback(struct elf_binary*, void *caller_data, - int iserr, const char *fmt, va_list al); + bool iserr, const char *fmt, va_list al); + +#endif + +#define ELF_MAX_STRING_LENGTH 4096 +#define ELF_MAX_TOTAL_NOTE_COUNT 65536 + +/* ------------------------------------------------------------------------ */ + +/* Macros for accessing the input image and output area. */ + +/* + * We abstract away the pointerness of these pointers, replacing + * various void*, char* and struct* with the following: + * elf_ptrval A pointer to a byte; one can do pointer arithmetic + * on this. + * HANDLE A pointer to a struct. There is one of these types + * for each pointer type - that is, for each "structname". + * In the arguments to the various HANDLE macros, structname + * must be a single identifier which is a typedef. + * It is not permitted to do arithmetic on these + * pointers. In the current code attempts to do so will + * compile, but in the next patch this will become a + * compile error. + */ + +typedef uintptr_t elf_ptrval; + +#define ELF_REALPTR2PTRVAL(realpointer) ((elf_ptrval)(realpointer)) + /* Converts an actual C pointer into a PTRVAL */ + +#define ELF_HANDLE_DECL(structname) structname##_handle + /* Provides a type declaration for a HANDLE. */ +#ifdef __XEN__ +# define ELF_PRPTRVAL "lu" + /* + * PRIuPTR is misdefined in xen/include/xen/inttypes.h, on 32-bit, + * to "u", when in fact uintptr_t is an unsigned long. + */ +#else +# define ELF_PRPTRVAL PRIuPTR #endif + /* printf format a la PRId... for a PTRVAL */ + +#define ELF_DEFINE_HANDLE(structname) \ + typedef union { \ + elf_ptrval ptrval; \ + const structname *typeonly; /* for sizeof, offsetof, &c only */ \ + } structname##_handle; + /* + * This must be invoked for each HANDLE type to define + * the actual C type used for that kind of HANDLE. + */ + +#define ELF_MAKE_HANDLE(structname, ptrval) ((structname##_handle){ ptrval }) + /* Converts a PTRVAL to a HANDLE */ + +#define ELF_IMAGE_BASE(elf) ((elf_ptrval)(elf)->image_base) + /* Returns the base of the image as a PTRVAL. */ + +#define ELF_HANDLE_PTRVAL(handleval) ((handleval).ptrval) + /* Converts a HANDLE to a PTRVAL. */ + +#define ELF_UNSAFE_PTR(ptrval) ((void*)(elf_ptrval)(ptrval)) + /* + * Turns a PTRVAL into an actual C pointer. Before this is done + * the caller must have ensured that the PTRVAL does in fact point + * to a permissible location. + */ + +/* PTRVALs can be INVALID (ie, NULL). */ +#define ELF_INVALID_PTRVAL ((elf_ptrval)0) /* returns NULL PTRVAL */ +#define ELF_INVALID_HANDLE(structname) /* returns NULL handle */ \ + ELF_MAKE_HANDLE(structname, ELF_INVALID_PTRVAL) +#define ELF_PTRVAL_VALID(ptrval) (!!(ptrval)) /* } */ +#define ELF_HANDLE_VALID(handleval) (!!(handleval).ptrval) /* } predicates */ +#define ELF_PTRVAL_INVALID(ptrval) (!ELF_PTRVAL_VALID((ptrval))) /* } */ + +#define ELF_MAX_PTRVAL (~(elf_ptrval)0) + /* PTRVAL value guaranteed to compare > to any valid PTRVAL */ + +/* For internal use by other macros here */ +#define ELF__HANDLE_FIELD_TYPE(handleval, elm) \ + typeof((handleval).typeonly->elm) +#define ELF__HANDLE_FIELD_OFFSET(handleval, elm) \ + offsetof(typeof(*(handleval).typeonly),elm) + /* ------------------------------------------------------------------------ */ + typedef union { Elf32_Ehdr e32; Elf64_Ehdr e64; @@ -83,20 +174,32 @@ typedef union { Elf64_Note e64; } elf_note; +ELF_DEFINE_HANDLE(elf_ehdr) +ELF_DEFINE_HANDLE(elf_shdr) +ELF_DEFINE_HANDLE(elf_phdr) +ELF_DEFINE_HANDLE(elf_sym) +ELF_DEFINE_HANDLE(elf_note) + struct elf_binary { /* elf binary */ - const char *image; + const void *image_base; size_t size; char class; char data; - const elf_ehdr *ehdr; - const char *sec_strtab; - const elf_shdr *sym_tab; - const char *sym_strtab; + ELF_HANDLE_DECL(elf_ehdr) ehdr; + elf_ptrval sec_strtab; + ELF_HANDLE_DECL(elf_shdr) sym_tab; + uint64_t sym_strtab; /* loaded to */ - char *dest; + /* + * dest_base and dest_size are trusted and must be correct; + * whenever dest_size is not 0, both of these must be valid + * so long as the struct elf_binary is in use. + */ + char *dest_base; + size_t dest_size; uint64_t pstart; uint64_t pend; uint64_t reloc_offset; @@ -104,12 +207,22 @@ struct elf_binary { uint64_t bsd_symtab_pstart; uint64_t bsd_symtab_pend; + /* + * caller's other acceptable destination + * + * Again, these are trusted and must be valid (or 0) so long + * as the struct elf_binary is in use. + */ + void *caller_xdest_base; + uint64_t caller_xdest_size; + #ifndef __XEN__ /* misc */ elf_log_callback *log_callback; void *log_caller_data; #endif - int verbose; + bool verbose; + const char *broken; }; /* ------------------------------------------------------------------------ */ @@ -127,88 +240,145 @@ struct elf_binary { #define elf_lsb(elf) (ELFDATA2LSB == (elf)->data) #define elf_swap(elf) (NATIVE_ELFDATA != (elf)->data) -#define elf_uval(elf, str, elem) \ - ((ELFCLASS64 == (elf)->class) \ - ? elf_access_unsigned((elf), (str), \ - offsetof(typeof(*(str)),e64.elem), \ - sizeof((str)->e64.elem)) \ - : elf_access_unsigned((elf), (str), \ - offsetof(typeof(*(str)),e32.elem), \ - sizeof((str)->e32.elem))) - -#define elf_sval(elf, str, elem) \ - ((ELFCLASS64 == (elf)->class) \ - ? elf_access_signed((elf), (str), \ - offsetof(typeof(*(str)),e64.elem), \ - sizeof((str)->e64.elem)) \ - : elf_access_signed((elf), (str), \ - offsetof(typeof(*(str)),e32.elem), \ - sizeof((str)->e32.elem))) - -#define elf_size(elf, str) \ - ((ELFCLASS64 == (elf)->class) \ - ? sizeof((str)->e64) : sizeof((str)->e32)) +#define elf_uval_3264(elf, handle, elem) \ + elf_access_unsigned((elf), (handle).ptrval, \ + offsetof(typeof(*(handle).typeonly),elem), \ + sizeof((handle).typeonly->elem)) + +#define elf_uval(elf, handle, elem) \ + ((ELFCLASS64 == (elf)->class) \ + ? elf_uval_3264(elf, handle, e64.elem) \ + : elf_uval_3264(elf, handle, e32.elem)) + /* + * Reads an unsigned field in a header structure in the ELF. + * str is a HANDLE, and elem is the field name in it. + */ -uint64_t elf_access_unsigned(struct elf_binary *elf, const void *ptr, + +#define elf_size(elf, handle_or_handletype) ({ \ + typeof(handle_or_handletype) elf_size__dummy; \ + ((ELFCLASS64 == (elf)->class) \ + ? sizeof(elf_size__dummy.typeonly->e64) \ + : sizeof(elf_size__dummy.typeonly->e32)); \ +}) + /* + * Returns the size of the substructure for the appropriate 32/64-bitness. + * str should be a HANDLE. + */ + +uint64_t elf_access_unsigned(struct elf_binary *elf, elf_ptrval ptr, uint64_t offset, size_t size); -int64_t elf_access_signed(struct elf_binary *elf, const void *ptr, - uint64_t offset, size_t size); + /* Reads a field at arbitrary offset and alignemnt */ uint64_t elf_round_up(struct elf_binary *elf, uint64_t addr); +const char *elf_strval(struct elf_binary *elf, elf_ptrval start); + /* may return NULL if the string is out of range etc. */ + +const char *elf_strfmt(struct elf_binary *elf, elf_ptrval start); + /* like elf_strval but returns "(invalid)" instead of NULL */ + +void elf_memcpy_safe(struct elf_binary*, elf_ptrval dst, elf_ptrval src, size_t); +void elf_memset_safe(struct elf_binary*, elf_ptrval dst, int c, size_t); + /* + * Versions of memcpy and memset which arrange never to write + * outside permitted areas. + */ + +bool elf_access_ok(struct elf_binary * elf, + uint64_t ptrval, size_t size); + +#define elf_store_val(elf, type, ptr, val) \ + ({ \ + typeof(type) elf_store__val = (val); \ + elf_ptrval elf_store__targ = ptr; \ + if (elf_access_ok((elf), elf_store__targ, \ + sizeof(elf_store__val))) { \ + elf_memcpy_unchecked((void*)elf_store__targ, &elf_store__val, \ + sizeof(elf_store__val)); \ + } \ + }) \ + /* Stores a value at a particular PTRVAL. */ + +#define elf_store_field(elf, hdr, elm, val) \ + (elf_store_val((elf), ELF__HANDLE_FIELD_TYPE(hdr, elm), \ + ELF_HANDLE_PTRVAL(hdr) + ELF__HANDLE_FIELD_OFFSET(hdr, elm), \ + (val))) + /* Stores a 32/64-bit field. hdr is a HANDLE and elm is the field name. */ + + /* ------------------------------------------------------------------------ */ /* xc_libelf_tools.c */ -int elf_shdr_count(struct elf_binary *elf); -int elf_phdr_count(struct elf_binary *elf); +unsigned elf_shdr_count(struct elf_binary *elf); +unsigned elf_phdr_count(struct elf_binary *elf); -const elf_shdr *elf_shdr_by_name(struct elf_binary *elf, const char *name); -const elf_shdr *elf_shdr_by_index(struct elf_binary *elf, int index); -const elf_phdr *elf_phdr_by_index(struct elf_binary *elf, int index); +ELF_HANDLE_DECL(elf_shdr) elf_shdr_by_name(struct elf_binary *elf, const char *name); +ELF_HANDLE_DECL(elf_shdr) elf_shdr_by_index(struct elf_binary *elf, unsigned index); +ELF_HANDLE_DECL(elf_phdr) elf_phdr_by_index(struct elf_binary *elf, unsigned index); -const char *elf_section_name(struct elf_binary *elf, const elf_shdr * shdr); -const void *elf_section_start(struct elf_binary *elf, const elf_shdr * shdr); -const void *elf_section_end(struct elf_binary *elf, const elf_shdr * shdr); +const char *elf_section_name(struct elf_binary *elf, ELF_HANDLE_DECL(elf_shdr) shdr); /* might return NULL if inputs are invalid */ +elf_ptrval elf_section_start(struct elf_binary *elf, ELF_HANDLE_DECL(elf_shdr) shdr); +elf_ptrval elf_section_end(struct elf_binary *elf, ELF_HANDLE_DECL(elf_shdr) shdr); -const void *elf_segment_start(struct elf_binary *elf, const elf_phdr * phdr); -const void *elf_segment_end(struct elf_binary *elf, const elf_phdr * phdr); +elf_ptrval elf_segment_start(struct elf_binary *elf, ELF_HANDLE_DECL(elf_phdr) phdr); +elf_ptrval elf_segment_end(struct elf_binary *elf, ELF_HANDLE_DECL(elf_phdr) phdr); -const elf_sym *elf_sym_by_name(struct elf_binary *elf, const char *symbol); -const elf_sym *elf_sym_by_index(struct elf_binary *elf, int index); +ELF_HANDLE_DECL(elf_sym) elf_sym_by_name(struct elf_binary *elf, const char *symbol); +ELF_HANDLE_DECL(elf_sym) elf_sym_by_index(struct elf_binary *elf, unsigned index); -const char *elf_note_name(struct elf_binary *elf, const elf_note * note); -const void *elf_note_desc(struct elf_binary *elf, const elf_note * note); -uint64_t elf_note_numeric(struct elf_binary *elf, const elf_note * note); -uint64_t elf_note_numeric_array(struct elf_binary *, const elf_note *, +const char *elf_note_name(struct elf_binary *elf, ELF_HANDLE_DECL(elf_note) note); /* may return NULL */ +elf_ptrval elf_note_desc(struct elf_binary *elf, ELF_HANDLE_DECL(elf_note) note); +uint64_t elf_note_numeric(struct elf_binary *elf, ELF_HANDLE_DECL(elf_note) note); +uint64_t elf_note_numeric_array(struct elf_binary *, ELF_HANDLE_DECL(elf_note), unsigned int unitsz, unsigned int idx); -const elf_note *elf_note_next(struct elf_binary *elf, const elf_note * note); -int elf_is_elfbinary(const void *image); -int elf_phdr_is_loadable(struct elf_binary *elf, const elf_phdr * phdr); +/* + * If you use elf_note_next in a loop, you must put a nontrivial upper + * bound on the returned value as part of your loop condition. In + * some cases elf_note_next will substitute ELF_PTRVAL_MAX as return + * value to indicate that the iteration isn't going well (for example, + * the putative "next" value would be earlier in memory). In this + * case the caller's loop must terminate. Checking against the + * end of the notes segment with a strict inequality is sufficient. + */ +ELF_HANDLE_DECL(elf_note) elf_note_next(struct elf_binary *elf, ELF_HANDLE_DECL(elf_note) note); + +/* (Only) checks that the image has the right magic number. */ +bool elf_is_elfbinary(const void *image_start, size_t image_size); + +bool elf_phdr_is_loadable(struct elf_binary *elf, ELF_HANDLE_DECL(elf_phdr) phdr); /* ------------------------------------------------------------------------ */ /* xc_libelf_loader.c */ -int elf_init(struct elf_binary *elf, const char *image, size_t size); +elf_errorstatus elf_init(struct elf_binary *elf, const char *image, size_t size); + /* + * image and size must be correct. They will be recorded in + * *elf, and must remain valid while the elf is in use. + */ #ifdef __XEN__ void elf_set_verbose(struct elf_binary *elf); #else void elf_set_log(struct elf_binary *elf, elf_log_callback*, - void *log_caller_pointer, int verbose); + void *log_caller_pointer, bool verbose); #endif void elf_parse_binary(struct elf_binary *elf); -int elf_load_binary(struct elf_binary *elf); +elf_errorstatus elf_load_binary(struct elf_binary *elf); -void *elf_get_ptr(struct elf_binary *elf, unsigned long addr); +elf_ptrval elf_get_ptr(struct elf_binary *elf, unsigned long addr); uint64_t elf_lookup_addr(struct elf_binary *elf, const char *symbol); void elf_parse_bsdsyms(struct elf_binary *elf, uint64_t pstart); /* private */ +void elf_mark_broken(struct elf_binary *elf, const char *msg); +const char *elf_check_broken(const struct elf_binary *elf); /* NULL means OK */ + /* ------------------------------------------------------------------------ */ /* xc_libelf_relocate.c */ -int elf_reloc(struct elf_binary *elf); +elf_errorstatus elf_reloc(struct elf_binary *elf); /* ------------------------------------------------------------------------ */ /* xc_libelf_dominfo.c */ @@ -232,9 +402,9 @@ struct xen_elfnote { struct elf_dom_parms { /* raw */ - const char *guest_info; - const void *elf_note_start; - const void *elf_note_end; + elf_ptrval guest_info; + elf_ptrval elf_note_start; + elf_ptrval elf_note_end; struct xen_elfnote elf_notes[XEN_ELFNOTE_MAX + 1]; /* parsed */ @@ -242,8 +412,8 @@ struct elf_dom_parms { char guest_ver[16]; char xen_ver[16]; char loader[16]; - int pae; - int bsd_symtab; + int pae; /* some kind of enum apparently */ + bool bsd_symtab; uint64_t virt_base; uint64_t virt_entry; uint64_t virt_hypercall; @@ -273,10 +443,44 @@ int elf_xen_parse_features(const char *features, uint32_t *required); int elf_xen_parse_note(struct elf_binary *elf, struct elf_dom_parms *parms, - const elf_note *note); + ELF_HANDLE_DECL(elf_note) note); int elf_xen_parse_guest_info(struct elf_binary *elf, struct elf_dom_parms *parms); int elf_xen_parse(struct elf_binary *elf, struct elf_dom_parms *parms); +static inline void *elf_memcpy_unchecked(void *dest, const void *src, size_t n) + { return memcpy(dest, src, n); } +static inline void *elf_memmove_unchecked(void *dest, const void *src, size_t n) + { return memmove(dest, src, n); } +static inline void *elf_memset_unchecked(void *s, int c, size_t n) + { return memset(s, c, n); } + /* + * Unsafe versions of memcpy, memmove memset which take actual C + * pointers. These are just like the real functions. + * We provide these so that in libelf-private.h we can #define + * memcpy, memset and memmove to undefined MISTAKE things. + */ + + +/* Advances past amount bytes of the current destination area. */ +static inline void ELF_ADVANCE_DEST(struct elf_binary *elf, uint64_t amount) +{ + if ( elf->dest_base == NULL ) + { + elf_mark_broken(elf, "advancing in null image"); + } + else if ( elf->dest_size >= amount ) + { + elf->dest_base += amount; + elf->dest_size -= amount; + } + else + { + elf->dest_size = 0; + elf_mark_broken(elf, "advancing past end (image very short?)"); + } +} + + #endif /* __XEN_LIBELF_H__ */