Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -3318,7 +3318,8 @@ net/vnet.c optional vimage net/zlib.c optional crypto | geom_uzip | ipsec | \ mxge | netgraph_deflate | \ - ddb_ctf | gzio | geom_uncompress + ddb_ctf | gzio | \ + geom_uncompress | kldgzelf net80211/ieee80211.c optional wlan net80211/ieee80211_acl.c optional wlan wlan_acl net80211/ieee80211_action.c optional wlan Index: sys/conf/options =================================================================== --- sys/conf/options +++ sys/conf/options @@ -131,6 +131,7 @@ GEOM_VOL opt_geom.h GEOM_ZERO opt_geom.h KDTRACE_HOOKS opt_kdtrace.h +KLDGZELF opt_kldgzelf.h KSTACK_MAX_PAGES KSTACK_PAGES KSTACK_USAGE_PROF Index: sys/kern/link_elf.c =================================================================== --- sys/kern/link_elf.c +++ sys/kern/link_elf.c @@ -29,7 +29,9 @@ #include "opt_ddb.h" #include "opt_gdb.h" +#include "opt_kldgzelf.h" +#include #include #include #ifdef GPROF @@ -64,8 +66,9 @@ #include #include +#include -#ifdef DDB_CTF +#if defined(DDB_CTF) || defined(KLDGZELF) #include #endif @@ -73,6 +76,7 @@ #define MAXSEGS 4 + typedef struct elf_file { struct linker_file lf; /* Common fields */ int preloaded; /* Was file pre-loaded */ @@ -174,7 +178,7 @@ KOBJMETHOD(linker_ctf_get, link_elf_ctf_get), KOBJMETHOD(linker_symtab_get, link_elf_symtab_get), KOBJMETHOD(linker_strtab_get, link_elf_strtab_get), - { 0, 0 } + KOBJMETHOD_END }; static struct linker_class link_elf_class = { @@ -186,6 +190,65 @@ link_elf_methods, sizeof(struct elf_file) }; +#ifdef KLDGZELF +#define GZ_ID1 31 +#define GZ_ID2 139 +/* Default ocmpression */ +#define GZ_CM 8 +/* Header flags */ +#define GZ_FLG_FTEXT 1 +#define GZ_FLG_FHCRC 2 +#define GZ_FLG_FEXTRA 4 +#define GZ_FLG_FNAME 8 +#define GZ_FLG_FCOMMENT 16 + +#define GZ_EXTRA_F_SIZE_SIZE 2 +#define GZ_INFLATED_SIZE_SIZE 4 + +static int link_gzelf_link_preload(linker_class_t, + const char *, linker_file_t *); +static int link_gzelf_link_preload_finish(linker_file_t); +static int link_gzelf_load_file(linker_class_t, const char *, + linker_file_t *); + +static kobj_method_t link_gzelf_methods[] = { + KOBJMETHOD(linker_load_file, link_elf_load_file), + KOBJMETHOD(linker_link_preload, link_gzelf_link_preload), + KOBJMETHOD(linker_link_preload_finish, link_gzelf_link_preload_finish), + KOBJMETHOD_END +}; + +static kobj_class_t link_gzelf_baseclasses[] = { + (kobj_class_t)&link_elf_class, NULL +}; + +static struct linker_class link_gzelf_class = { +#if ELF_TARG_CLASS == ELFCLASS32 + "gzelf32", +#else + "gzelf64", +#endif + link_gzelf_methods, sizeof(struct elf_file), link_gzelf_baseclasses +}; + +static int +link_gzelf_link_preload(linker_class_t cls, + const char* filename, linker_file_t *result) +{ + + return ENOENT; +} + +static int +link_gzelf_link_preload_finish(linker_file_t lf) +{ + + return ENOENT; +} + +#endif + + static int parse_dynamic(elf_file_t); static int relocate_file(elf_file_t); static int link_elf_preload_parse_symbols(elf_file_t); @@ -376,6 +439,9 @@ char *modname; linker_add_class(&link_elf_class); +#ifdef KLDGZELF + linker_add_class(&link_gzelf_class); +#endif dp = (Elf_Dyn *)&_DYNAMIC; modname = NULL; @@ -575,7 +641,7 @@ static int parse_dpcpu(elf_file_t ef) -{ +{ int count; int error; @@ -606,7 +672,7 @@ #ifdef VIMAGE static int parse_vnet(elf_file_t ef) -{ +{ int count; int error; @@ -710,14 +776,59 @@ return (link_elf_link_common_finish(lf)); } +#ifdef KLDGZELF +typedef struct gzcontext { + u_char *out_p; + u_int inflated_size; + struct vnode *vd; + struct thread *td; + int error; + off_t skip; +} *gzcontext_t; + +typedef struct gz_hdr { + u_char id1; + u_char id2; + u_char cm; + u_char flags; + u_int mtime; + u_char xflags; + u_char os; +}__attribute__((__packed__)) *gz_hdr_t; + +static long gzhdr(u_char *, int); +static int gzelf_inflate(struct gzcontext *); +static void *zalloc(void *, u_int, u_int); +static void zfree(void *, void *); +#endif + +/* + * Safer memcpy; it checks if attempted copy will not cross source + * buffer size. + * + * Returns: 0 in case of success and EFAULT if chunk would cross + * the range of source buffer. + */ +static inline int +smemcpy(void *dst, void *base, off_t off, size_t len, size_t src_size) +{ + + if (off + len > src_size) { + return EFAULT; + } + + memcpy(dst, (caddr_t)base + off, len); + return 0; +} + static int link_elf_load_file(linker_class_t cls, const char* filename, linker_file_t* result) { - struct nameidata nd; + struct nameidata nd = { 0 }; struct thread* td = curthread; /* XXX */ - Elf_Ehdr *hdr; - caddr_t firstpage; + Elf_Ehdr *hdr = NULL; + caddr_t firstpage = NULL; int nbytes, i; Elf_Phdr *phdr; Elf_Phdr *phlimit; @@ -740,6 +851,9 @@ int symstrindex; int symcnt; int strcnt; +#ifdef KLDGZELF + struct gzcontext gzc = { 0 }; +#endif shdr = NULL; lf = NULL; @@ -774,6 +888,23 @@ nbytes = PAGE_SIZE - resid; if (error != 0) goto out; +#ifdef KLDGZELF + if (cls == &link_gzelf_class) { + gzc.td = td; + gzc.vd = nd.ni_vp; + if (gzelf_inflate(&gzc) != Z_OK) { + if (gzc.error != 0) + error = gzc.error; + else + error = EIO; + + goto out; + } + nbytes = gzc.inflated_size; + + hdr = (Elf_Ehdr *)gzc.out_p; + } +#endif if (!IS_ELF(*hdr)) { error = ENOEXEC; @@ -819,7 +950,7 @@ * We rely on there being exactly two load segments, text and data, * in that order. */ - phdr = (Elf_Phdr *) (firstpage + hdr->e_phoff); + phdr = (Elf_Phdr *) ((caddr_t)hdr + hdr->e_phoff); phlimit = phdr + hdr->e_phnum; nsegs = 0; phdyn = NULL; @@ -872,11 +1003,11 @@ */ base_offset = trunc_page(segs[0]->p_offset); base_vaddr = trunc_page(segs[0]->p_vaddr); - base_vlimit = round_page(segs[nsegs - 1]->p_vaddr + + base_vlimit = round_page(segs[nsegs - 1]->p_vaddr + segs[nsegs - 1]->p_memsz); mapsize = base_vlimit - base_vaddr; - lf = linker_make_file(filename, &link_elf_class); + lf = linker_make_file(filename, cls); if (lf == NULL) { error = ENOMEM; goto out; @@ -908,13 +1039,20 @@ */ for (i = 0; i < nsegs; i++) { caddr_t segbase = mapbase + segs[i]->p_vaddr - base_vaddr; - error = vn_rdwr(UIO_READ, nd.ni_vp, - segbase, segs[i]->p_filesz, segs[i]->p_offset, - UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, - &resid, td); +#ifdef KLDGZELF + if (cls == &link_gzelf_class) + error = smemcpy(segbase, gzc.out_p, segs[i]->p_offset, + segs[i]->p_filesz, gzc.inflated_size); + else +#endif + error = vn_rdwr(UIO_READ, nd.ni_vp, + segbase, segs[i]->p_filesz, segs[i]->p_offset, + UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, + &resid, td); + if (error != 0) goto out; - bzero(segbase + segs[i]->p_filesz, + memset(segbase + segs[i]->p_filesz, 0, segs[i]->p_memsz - segs[i]->p_filesz); #ifdef SPARSE_MAPPING @@ -975,10 +1113,17 @@ if (nbytes == 0 || hdr->e_shoff == 0) goto nosyms; shdr = malloc(nbytes, M_LINKER, M_WAITOK | M_ZERO); - error = vn_rdwr(UIO_READ, nd.ni_vp, - (caddr_t)shdr, nbytes, hdr->e_shoff, - UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, - &resid, td); +#ifdef KLDGZELF + if (cls == &link_gzelf_class) + error = smemcpy((caddr_t)shdr, gzc.out_p, hdr->e_shoff, + nbytes, gzc.inflated_size); + else +#endif + error = vn_rdwr(UIO_READ, nd.ni_vp, + (caddr_t)shdr, nbytes, hdr->e_shoff, + UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, + &resid, td); + if (error != 0) goto out; symtabindex = -1; @@ -997,16 +1142,31 @@ strcnt = shdr[symstrindex].sh_size; ef->strbase = malloc(strcnt, M_LINKER, M_WAITOK); - error = vn_rdwr(UIO_READ, nd.ni_vp, - ef->symbase, symcnt, shdr[symtabindex].sh_offset, - UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, - &resid, td); +#ifdef KLDGZELF + if (cls == &link_gzelf_class) + error = smemcpy(ef->symbase, gzc.out_p, shdr[symtabindex].sh_offset, + symcnt, gzc.inflated_size); + else +#endif + error = vn_rdwr(UIO_READ, nd.ni_vp, + ef->symbase, symcnt, shdr[symtabindex].sh_offset, + UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, + &resid, td); + if (error != 0) goto out; - error = vn_rdwr(UIO_READ, nd.ni_vp, - ef->strbase, strcnt, shdr[symstrindex].sh_offset, - UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, - &resid, td); + +#ifdef KLDGZELF + if (cls == &link_gzelf_class) + error = smemcpy(ef->strbase, gzc.out_p, shdr[symstrindex].sh_offset, + strcnt, gzc.inflated_size); + else +#endif + error = vn_rdwr(UIO_READ, nd.ni_vp, + ef->strbase, strcnt, shdr[symstrindex].sh_offset, + UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, + &resid, td); + if (error != 0) goto out; @@ -1025,16 +1185,258 @@ out: VOP_UNLOCK(nd.ni_vp, 0); vn_close(nd.ni_vp, FREAD, td->td_ucred, td); + if (error != 0 && lf != NULL) linker_file_unload(lf, LINKER_UNLOAD_FORCE); if (shdr != NULL) free(shdr, M_LINKER); +#ifdef KLDGZELF + if (cls == &link_gzelf_class) + free(gzc.out_p, M_LINKER); + +#endif if (firstpage != NULL) free(firstpage, M_LINKER); return (error); } +#ifdef KLDGZELF + +/* + * Checks buffer for GZIP header. + * + * Returns: number of bytes skipped while processing header (> 0) + * or Z_DATA_ERROR (< 0) in case of failure + */ +static long +gzhdr(u_char *p, int len) +{ + struct gz_hdr *hdr = (struct gz_hdr *)p; + u_short size = 0; + u_long total = sizeof(*hdr); + + if (len < total) + return (Z_DATA_ERROR); + + if (hdr->id1 != GZ_ID1 || hdr->id2 != GZ_ID2) { + return (Z_DATA_ERROR); + } + + if (hdr->cm != GZ_CM) { + printf("Unsupported compression\n"); + return (Z_DATA_ERROR); + } + + p += sizeof(*hdr); + + /* Process optional fields */ + /* Extra field */ + if (hdr->flags & GZ_FLG_FEXTRA) { + if (total + GZ_EXTRA_F_SIZE_SIZE > len) + return (Z_DATA_ERROR); + + /* + * Extra field length, 2 bytes, does not include size + * itself. + */ + if (len < (sizeof(*hdr) + GZ_EXTRA_F_SIZE_SIZE)) + return (Z_DATA_ERROR); + + size = *(u_short *)p; + + total += GZ_EXTRA_F_SIZE_SIZE; + p += GZ_EXTRA_F_SIZE_SIZE; + + if (total + size > len) + return (Z_DATA_ERROR); + + total += size; + p += size; + } + + /* Skip over the file name */ + if (hdr->flags & GZ_FLG_FNAME) { + do { + if (total >= len) + return (Z_DATA_ERROR); + total++; + } while (*p++ != 0); + } + + /* Skip comment */ + if (hdr->flags & GZ_FLG_FCOMMENT) { + do { + if (total >= len) + return (Z_DATA_ERROR); + total++; + } while (*p++ != 0); + } + + /* Header crc */ + if (hdr->flags & GZ_FLG_FHCRC) { + total += 2; + if (total > len) + return (Z_DATA_ERROR); + } + + /* + * Successfully went through gzip header, return number of + * octets processed + */ + return (total); +} + + +/* zalloc/zfree are malloc/free wrappers for internal use by zlib */ +static void * +zalloc(void *_up __unused, u_int items, u_int size) +{ + + return (malloc(size*items, M_LINKER, M_WAITOK)); +} + +static void +zfree(void *_up __unused, void *p) +{ + + free(p, M_LINKER); +} + +/* + * Inflates file into buffer returned via gzc.out_p pointer. + * In case of failure this pointer will be NULL and non 0 + * (non-Z_OK) error zlib type code will be returned. In case when + * Z_ERRNO is returned, gzc.error contains errno.h defined + * code. + */ +static int +gzelf_inflate(struct gzcontext *gzc) +{ + z_stream strm; + u_char *in_p = NULL; + int ret = Z_OK; + struct stat sb; + off_t off = 0; + ssize_t resid; + ssize_t loaded_bytes; + + gzc->error = 0; + gzc->out_p = NULL; + + memset(&strm, 0, sizeof(strm)); + strm.zalloc = zalloc; + strm.zfree = zfree; + + ret = inflateInit2(&strm, -15); + if (ret != Z_OK) + goto out; + + ret = Z_ERRNO; + + /* Alloc one page for purpose of checking if given file is + gzip file indeed and getting inflated file size */ + in_p = malloc(PAGE_SIZE, M_LINKER, M_WAITOK); + + gzc->error = vn_rdwr(UIO_READ, gzc->vd, in_p, PAGE_SIZE, 0, + UIO_SYSSPACE, IO_NODELOCKED, gzc->td->td_ucred, NOCRED, + &resid, gzc->td); + + if (gzc->error != 0) + goto out; + + ret = gzhdr(in_p, PAGE_SIZE - resid); + + if (ret < 0) { + ret = Z_DATA_ERROR; + goto out; + } + + gzc->skip = ret; + + gzc->error = vn_stat(gzc->vd, &sb, gzc->td->td_ucred, NOCRED, gzc->td); + if (gzc->error != 0) + goto out; + + gzc->error = vn_rdwr(UIO_READ, gzc->vd, in_p, PAGE_SIZE, + MAX(0, sb.st_size - PAGE_SIZE), UIO_SYSSPACE, IO_NODELOCKED, + gzc->td->td_ucred, NOCRED, &resid, gzc->td); + if (gzc->error != 0) + goto out; + + /* Last four bytes of GZIPed file store size of inlated data */ + loaded_bytes = PAGE_SIZE - resid; + gzc->inflated_size = *(u_int *)(in_p + loaded_bytes - GZ_INFLATED_SIZE_SIZE); + + /* Be reasonable, 100MB of module is enough */ + if (gzc->inflated_size > (100 * 1024 * 1024)) { + gzc->error = E2BIG; + goto out; + } + + gzc->out_p = malloc(gzc->inflated_size, M_LINKER, M_WAITOK); + + strm.avail_out = gzc->inflated_size; + strm.next_out = gzc->out_p; + + do { + gzc->error = vn_rdwr(UIO_READ, gzc->vd, in_p, PAGE_SIZE, + off + gzc->skip, UIO_SYSSPACE, IO_NODELOCKED, + gzc->td->td_ucred, NOCRED, &resid, gzc->td); + if (gzc->error) { + ret = Z_ERRNO; + goto out; + } + + strm.avail_in = PAGE_SIZE - resid; + strm.next_in = in_p; + off += strm.avail_in; + + ret = inflate(&strm, Z_NO_FLUSH); + if (ret == Z_NEED_DICT) + ret = Z_DATA_ERROR; + + /* All negative return codes mean error */ + if (ret < 0) + goto out; + + } while (strm.avail_out > 0 && ret != Z_STREAM_END); + + /* + * We run out of buffer space for data and still have not inflated + * all of data. + */ + if (strm.avail_out == 0 && ret == Z_OK) + ret = Z_DATA_ERROR; + + /* + * Expected inflated size differs from obtained, something + * went wrong. + */ + if (strm.avail_out != 0 && ret == Z_STREAM_END) + ret = Z_DATA_ERROR; + + /* Return Z_OK if stream fully decompressed to avoid confusion */ + if (ret == Z_STREAM_END) + ret = Z_OK; +out: + /* Close file and release buffers */ + inflateEnd(&strm); + free(in_p, M_LINKER); + + if (ret != Z_OK) { + free(gzc->out_p, M_LINKER); + gzc->out_p = NULL; + } + + if (gzc->error != 0) + return (Z_ERRNO); + + return (ret); +} + +#endif + Elf_Addr elf_relocaddr(linker_file_t lf, Elf_Addr x) { @@ -1416,7 +1818,7 @@ elf_file_t ef = (elf_file_t)file; const Elf_Sym *symp; int i, error; - + /* Exhaustive search */ for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) { if (symp->st_value != 0 && @@ -1602,7 +2004,7 @@ return (ef->ddbsymcnt); } - + static long link_elf_strtab_get(linker_file_t lf, caddr_t *strtab) {