imgact_gzip.c revision 125454
13332Sphk/* 23332Sphk * ---------------------------------------------------------------------------- 33332Sphk * "THE BEER-WARE LICENSE" (Revision 42): 493149Sphk * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you 53332Sphk * can do whatever you want with this stuff. If we meet some day, and you think 63332Sphk * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 73332Sphk * ---------------------------------------------------------------------------- 83332Sphk * 93332Sphk * 103332Sphk * This module handles execution of a.out files which have been run through 113784Sphk * "gzip". This saves diskspace, but wastes cpu-cycles and VM. 123332Sphk * 133332Sphk * TODO: 143332Sphk * text-segments should be made R/O after being filled 153332Sphk * is the vm-stuff safe ? 163332Sphk * should handle the entire header of gzip'ed stuff. 173332Sphk * inflate isn't quite reentrant yet... 183332Sphk * error-handling is a mess... 193332Sphk * so is the rest... 203417Scsgr * tidy up unnecesary includes 213332Sphk */ 223332Sphk 23116182Sobrien#include <sys/cdefs.h> 24116182Sobrien__FBSDID("$FreeBSD: head/sys/kern/imgact_gzip.c 125454 2004-02-04 21:52:57Z jhb $"); 25116182Sobrien 263784Sphk#include <sys/param.h> 273332Sphk#include <sys/exec.h> 283332Sphk#include <sys/imgact.h> 293332Sphk#include <sys/imgact_aout.h> 303332Sphk#include <sys/kernel.h> 3176166Smarkm#include <sys/lock.h> 323507Scsgr#include <sys/mman.h> 3376166Smarkm#include <sys/mutex.h> 3415494Sbde#include <sys/proc.h> 353507Scsgr#include <sys/resourcevar.h> 363332Sphk#include <sys/sysent.h> 373507Scsgr#include <sys/systm.h> 3815494Sbde#include <sys/vnode.h> 393784Sphk#include <sys/inflate.h> 403332Sphk 413332Sphk#include <vm/vm.h> 4212662Sdg#include <vm/vm_param.h> 4312662Sdg#include <vm/pmap.h> 4412662Sdg#include <vm/vm_map.h> 453332Sphk#include <vm/vm_kern.h> 4612662Sdg#include <vm/vm_extern.h> 473332Sphk 483784Sphkstruct imgact_gzip { 493784Sphk struct image_params *ip; 503784Sphk struct exec a_out; 513784Sphk int error; 5248079Shoek int gotheader; 533784Sphk int where; 543784Sphk u_char *inbuf; 553784Sphk u_long offset; 563784Sphk u_long output; 573784Sphk u_long len; 583784Sphk int idx; 593784Sphk u_long virtual_offset, file_offset, file_end, bss_size; 603784Sphk}; 613784Sphk 6292723Salfredstatic int exec_gzip_imgact(struct image_params *imgp); 6392723Salfredstatic int NextByte(void *vp); 6492723Salfredstatic int do_aout_hdr(struct imgact_gzip *); 6592723Salfredstatic int Flush(void *vp, u_char *, u_long siz); 663784Sphk 6712568Sbdestatic int 6812130Sdgexec_gzip_imgact(imgp) 6912130Sdg struct image_params *imgp; 703332Sphk{ 713784Sphk int error, error2 = 0; 7217974Sbde const u_char *p = (const u_char *) imgp->image_header; 733784Sphk struct imgact_gzip igz; 743784Sphk struct inflate infl; 7517386Sphk struct vmspace *vmspace; 763332Sphk 773348Sphk /* If these four are not OK, it isn't a gzip file */ 783784Sphk if (p[0] != 0x1f) 793784Sphk return -1; /* 0 Simply magic */ 803784Sphk if (p[1] != 0x8b) 813784Sphk return -1; /* 1 Simply magic */ 823784Sphk if (p[2] != 0x08) 833784Sphk return -1; /* 2 Compression method */ 843784Sphk if (p[9] != 0x03) 853784Sphk return -1; /* 9 OS compressed on */ 863332Sphk 873784Sphk /* 883784Sphk * If this one contains anything but a comment or a filename marker, 893784Sphk * we don't want to chew on it 903348Sphk */ 913784Sphk if (p[3] & ~(0x18)) 923784Sphk return ENOEXEC; /* 3 Flags */ 933332Sphk 943348Sphk /* These are of no use to us */ 953784Sphk /* 4-7 Timestamp */ 963784Sphk /* 8 Extra flags */ 973348Sphk 983784Sphk bzero(&igz, sizeof igz); 993784Sphk bzero(&infl, sizeof infl); 1003784Sphk infl.gz_private = (void *) &igz; 1013784Sphk infl.gz_input = NextByte; 1023784Sphk infl.gz_output = Flush; 1033332Sphk 10412130Sdg igz.ip = imgp; 1053784Sphk igz.idx = 10; 1063353Sphk 1073784Sphk if (p[3] & 0x08) { /* skip a filename */ 1083784Sphk while (p[igz.idx++]) 1093784Sphk if (igz.idx >= PAGE_SIZE) 1103784Sphk return ENOEXEC; 1113348Sphk } 1123784Sphk if (p[3] & 0x10) { /* skip a comment */ 1133784Sphk while (p[igz.idx++]) 1143784Sphk if (igz.idx >= PAGE_SIZE) 1153784Sphk return ENOEXEC; 1163348Sphk } 11717386Sphk igz.len = imgp->attr->va_size; 1183332Sphk 1193784Sphk error = inflate(&infl); 1203348Sphk 12148079Shoek /* 12248079Shoek * The unzipped file may not even have been long enough to contain 12348079Shoek * a header giving Flush() a chance to return error. Check for this. 12448079Shoek */ 12548079Shoek if ( !igz.gotheader ) 12648079Shoek return ENOEXEC; 12748079Shoek 12817386Sphk if ( !error ) { 12917386Sphk vmspace = imgp->proc->p_vmspace; 13017386Sphk error = vm_map_protect(&vmspace->vm_map, 13117386Sphk (vm_offset_t) vmspace->vm_taddr, 13217386Sphk (vm_offset_t) (vmspace->vm_taddr + 13317386Sphk (vmspace->vm_tsize << PAGE_SHIFT)) , 13417386Sphk VM_PROT_READ|VM_PROT_EXECUTE,0); 13517386Sphk } 13617386Sphk 1373784Sphk if (igz.inbuf) { 1383784Sphk error2 = 1396579Sdg vm_map_remove(kernel_map, (vm_offset_t) igz.inbuf, 1406579Sdg (vm_offset_t) igz.inbuf + PAGE_SIZE); 1413332Sphk } 1423784Sphk if (igz.error || error || error2) { 1433784Sphk printf("Output=%lu ", igz.output); 1443784Sphk printf("Inflate_error=%d igz.error=%d error2=%d where=%d\n", 1453784Sphk error, igz.error, error2, igz.where); 1463348Sphk } 1473784Sphk if (igz.error) 1483784Sphk return igz.error; 1493784Sphk if (error) 1503784Sphk return ENOEXEC; 1518876Srgrimes if (error2) 1523784Sphk return error2; 1533784Sphk return 0; 1543332Sphk} 1553332Sphk 1563784Sphkstatic int 1573784Sphkdo_aout_hdr(struct imgact_gzip * gz) 1583332Sphk{ 1593784Sphk int error; 16024848Sdyson struct vmspace *vmspace; 16114703Sbde vm_offset_t vmaddr; 1623332Sphk 1633784Sphk /* 1643784Sphk * Set file/virtual offset based on a.out variant. We do two cases: 1653784Sphk * host byte order and network byte order (for NetBSD compatibility) 1663784Sphk */ 1673784Sphk switch ((int) (gz->a_out.a_magic & 0xffff)) { 1683332Sphk case ZMAGIC: 1693784Sphk gz->virtual_offset = 0; 1703784Sphk if (gz->a_out.a_text) { 17115538Sphk gz->file_offset = PAGE_SIZE; 1723784Sphk } else { 1733784Sphk /* Bill's "screwball mode" */ 1743784Sphk gz->file_offset = 0; 1753784Sphk } 1763784Sphk break; 1773332Sphk case QMAGIC: 17815538Sphk gz->virtual_offset = PAGE_SIZE; 1793784Sphk gz->file_offset = 0; 1803784Sphk break; 1813332Sphk default: 1823784Sphk /* NetBSD compatibility */ 1833784Sphk switch ((int) (ntohl(gz->a_out.a_magic) & 0xffff)) { 1843784Sphk case ZMAGIC: 1853784Sphk case QMAGIC: 18615538Sphk gz->virtual_offset = PAGE_SIZE; 1873784Sphk gz->file_offset = 0; 1883784Sphk break; 1893784Sphk default: 1903784Sphk gz->where = __LINE__; 1913784Sphk return (-1); 1923784Sphk } 1933332Sphk } 1943332Sphk 19515538Sphk gz->bss_size = roundup(gz->a_out.a_bss, PAGE_SIZE); 1963332Sphk 1973784Sphk /* 1983784Sphk * Check various fields in header for validity/bounds. 1993784Sphk */ 2003784Sphk if ( /* entry point must lay with text region */ 2013784Sphk gz->a_out.a_entry < gz->virtual_offset || 2023784Sphk gz->a_out.a_entry >= gz->virtual_offset + gz->a_out.a_text || 2033332Sphk 2043332Sphk /* text and data size must each be page rounded */ 20515538Sphk gz->a_out.a_text & PAGE_MASK || gz->a_out.a_data & PAGE_MASK) { 2063784Sphk gz->where = __LINE__; 2073784Sphk return (-1); 2083784Sphk } 2093784Sphk /* 2103784Sphk * text/data/bss must not exceed limits 2113784Sphk */ 212125454Sjhb PROC_LOCK(gz->ip->proc); 2133784Sphk if ( /* text can't exceed maximum text size */ 21484783Sps gz->a_out.a_text > maxtsiz || 2153332Sphk 2163332Sphk /* data + bss can't exceed rlimit */ 2173784Sphk gz->a_out.a_data + gz->bss_size > 218125454Sjhb lim_cur(gz->ip->proc, RLIMIT_DATA)) { 219125454Sjhb PROC_UNLOCK(gz->ip->proc); 2203784Sphk gz->where = __LINE__; 2213784Sphk return (ENOMEM); 2223784Sphk } 223125454Sjhb PROC_UNLOCK(gz->ip->proc); 2243784Sphk /* Find out how far we should go */ 2253784Sphk gz->file_end = gz->file_offset + gz->a_out.a_text + gz->a_out.a_data; 2263332Sphk 2273784Sphk /* copy in arguments and/or environment from old process */ 2283784Sphk error = exec_extract_strings(gz->ip); 2293784Sphk if (error) { 2303784Sphk gz->where = __LINE__; 2313784Sphk return (error); 2323784Sphk } 2333784Sphk /* 2343784Sphk * Destroy old process VM and create a new one (with a new stack) 2353784Sphk */ 236103767Sjake exec_new_vmspace(gz->ip, &aout_sysvec); 2373348Sphk 23824848Sdyson vmspace = gz->ip->proc->p_vmspace; 23924848Sdyson 2403784Sphk vmaddr = gz->virtual_offset; 2413332Sphk 2423784Sphk error = vm_mmap(&vmspace->vm_map, 2433784Sphk &vmaddr, 24417386Sphk gz->a_out.a_text + gz->a_out.a_data, 24517386Sphk VM_PROT_ALL, VM_PROT_ALL, MAP_ANON | MAP_FIXED, 2463784Sphk 0, 2473784Sphk 0); 2483332Sphk 2493784Sphk if (error) { 2503784Sphk gz->where = __LINE__; 2513784Sphk return (error); 2523784Sphk } 25317386Sphk 2546579Sdg if (gz->bss_size != 0) { 2556579Sdg /* 25614087Sphk * Allocate demand-zeroed area for uninitialized data. 25714087Sphk * "bss" = 'block started by symbol' - named after the 25814087Sphk * IBM 7090 instruction of the same name. 2596579Sdg */ 26014087Sphk vmaddr = gz->virtual_offset + gz->a_out.a_text + 26114087Sphk gz->a_out.a_data; 26217386Sphk error = vm_map_find(&vmspace->vm_map, 26317386Sphk NULL, 26417386Sphk 0, 26517386Sphk &vmaddr, 26617386Sphk gz->bss_size, 26717386Sphk FALSE, VM_PROT_ALL, VM_PROT_ALL, 0); 2686579Sdg if (error) { 2696579Sdg gz->where = __LINE__; 2706579Sdg return (error); 2716579Sdg } 2723784Sphk } 2733784Sphk /* Fill in process VM information */ 2743784Sphk vmspace->vm_tsize = gz->a_out.a_text >> PAGE_SHIFT; 2753784Sphk vmspace->vm_dsize = (gz->a_out.a_data + gz->bss_size) >> PAGE_SHIFT; 27637656Sbde vmspace->vm_taddr = (caddr_t) (uintptr_t) gz->virtual_offset; 27737656Sbde vmspace->vm_daddr = (caddr_t) (uintptr_t) 27837656Sbde (gz->virtual_offset + gz->a_out.a_text); 2793332Sphk 2803784Sphk /* Fill in image_params */ 2813784Sphk gz->ip->interpreted = 0; 2823784Sphk gz->ip->entry_addr = gz->a_out.a_entry; 2833332Sphk 2843784Sphk gz->ip->proc->p_sysent = &aout_sysvec; 2853332Sphk 2863784Sphk return 0; 2873784Sphk} 2883332Sphk 2893784Sphkstatic int 2903784SphkNextByte(void *vp) 2913784Sphk{ 2923784Sphk int error; 2933784Sphk struct imgact_gzip *igz = (struct imgact_gzip *) vp; 2943332Sphk 2953784Sphk if (igz->idx >= igz->len) { 2963784Sphk igz->where = __LINE__; 2973784Sphk return GZ_EOF; 2983784Sphk } 2993784Sphk if (igz->inbuf && igz->idx < (igz->offset + PAGE_SIZE)) { 3003784Sphk return igz->inbuf[(igz->idx++) - igz->offset]; 3013784Sphk } 3023784Sphk if (igz->inbuf) { 3036579Sdg error = vm_map_remove(kernel_map, (vm_offset_t) igz->inbuf, 3046579Sdg (vm_offset_t) igz->inbuf + PAGE_SIZE); 3053784Sphk if (error) { 3063784Sphk igz->where = __LINE__; 3073784Sphk igz->error = error; 3083784Sphk return GZ_EOF; 3093784Sphk } 3103784Sphk } 3116342Sphk igz->offset = igz->idx & ~PAGE_MASK; 3123332Sphk 3133784Sphk error = vm_mmap(kernel_map, /* map */ 3143784Sphk (vm_offset_t *) & igz->inbuf, /* address */ 3153784Sphk PAGE_SIZE, /* size */ 3163784Sphk VM_PROT_READ, /* protection */ 3173784Sphk VM_PROT_READ, /* max protection */ 3183784Sphk 0, /* flags */ 31912130Sdg (caddr_t) igz->ip->vp, /* vnode */ 3203784Sphk igz->offset); /* offset */ 3213784Sphk if (error) { 3223784Sphk igz->where = __LINE__; 3233784Sphk igz->error = error; 3243784Sphk return GZ_EOF; 3253784Sphk } 3263784Sphk return igz->inbuf[(igz->idx++) - igz->offset]; 3273784Sphk} 3283332Sphk 3293784Sphkstatic int 3303784SphkFlush(void *vp, u_char * ptr, u_long siz) 3313784Sphk{ 3323784Sphk struct imgact_gzip *gz = (struct imgact_gzip *) vp; 3333784Sphk u_char *p = ptr, *q; 3343784Sphk int i; 3353348Sphk 336108533Sschweikh /* First, find an a.out-header. */ 3373784Sphk if (gz->output < sizeof gz->a_out) { 3383784Sphk q = (u_char *) & gz->a_out; 3393784Sphk i = min(siz, sizeof gz->a_out - gz->output); 3403784Sphk bcopy(p, q + gz->output, i); 3413784Sphk gz->output += i; 3423784Sphk p += i; 3433784Sphk siz -= i; 3443784Sphk if (gz->output == sizeof gz->a_out) { 34548079Shoek gz->gotheader = 1; 3463784Sphk i = do_aout_hdr(gz); 3473784Sphk if (i == -1) { 3483785Sphk if (!gz->where) 3493785Sphk gz->where = __LINE__; 3503784Sphk gz->error = ENOEXEC; 3513784Sphk return ENOEXEC; 3523784Sphk } else if (i) { 3533784Sphk gz->where = __LINE__; 3543784Sphk gz->error = i; 3553784Sphk return ENOEXEC; 3563784Sphk } 35731718Sjdp if (gz->file_offset == 0) { 35837656Sbde q = (u_char *) (uintptr_t) gz->virtual_offset; 35937015Sbde copyout(&gz->a_out, q, sizeof gz->a_out); 3603784Sphk } 3613784Sphk } 3623784Sphk } 3633784Sphk /* Skip over zero-padded first PAGE if needed */ 36437015Sbde if (gz->output < gz->file_offset && 36537015Sbde gz->output + siz > gz->file_offset) { 3663784Sphk i = min(siz, gz->file_offset - gz->output); 3673784Sphk gz->output += i; 3683784Sphk p += i; 3693784Sphk siz -= i; 3703784Sphk } 3713784Sphk if (gz->output >= gz->file_offset && gz->output < gz->file_end) { 3723784Sphk i = min(siz, gz->file_end - gz->output); 37337656Sbde q = (u_char *) (uintptr_t) 37437656Sbde (gz->virtual_offset + gz->output - gz->file_offset); 37537015Sbde copyout(p, q, i); 3763784Sphk gz->output += i; 3773784Sphk p += i; 3783784Sphk siz -= i; 3793784Sphk } 3803784Sphk gz->output += siz; 3813784Sphk return 0; 3823332Sphk} 3833332Sphk 3843784Sphk 3853332Sphk/* 3863332Sphk * Tell kern_execve.c about it, with a little help from the linker. 3873332Sphk */ 38843402Sdillonstatic struct execsw gzip_execsw = {exec_gzip_imgact, "gzip"}; 38940435SpeterEXEC_SET(execgzip, gzip_execsw); 390