imgact_gzip.c revision 220373
1139804Simp/*- 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 * ---------------------------------------------------------------------------- 8139804Simp */ 9139804Simp 10139804Simp/* 113332Sphk * This module handles execution of a.out files which have been run through 123784Sphk * "gzip". This saves diskspace, but wastes cpu-cycles and VM. 133332Sphk * 143332Sphk * TODO: 153332Sphk * text-segments should be made R/O after being filled 163332Sphk * is the vm-stuff safe ? 173332Sphk * should handle the entire header of gzip'ed stuff. 183332Sphk * inflate isn't quite reentrant yet... 193332Sphk * error-handling is a mess... 203332Sphk * so is the rest... 213417Scsgr * tidy up unnecesary includes 223332Sphk */ 233332Sphk 24116182Sobrien#include <sys/cdefs.h> 25116182Sobrien__FBSDID("$FreeBSD: head/sys/kern/imgact_gzip.c 220373 2011-04-05 20:23:59Z trasz $"); 26116182Sobrien 273784Sphk#include <sys/param.h> 283332Sphk#include <sys/exec.h> 293332Sphk#include <sys/imgact.h> 303332Sphk#include <sys/imgact_aout.h> 313332Sphk#include <sys/kernel.h> 3276166Smarkm#include <sys/lock.h> 333507Scsgr#include <sys/mman.h> 3476166Smarkm#include <sys/mutex.h> 3515494Sbde#include <sys/proc.h> 36220373Strasz#include <sys/racct.h> 373507Scsgr#include <sys/resourcevar.h> 383332Sphk#include <sys/sysent.h> 393507Scsgr#include <sys/systm.h> 4015494Sbde#include <sys/vnode.h> 413784Sphk#include <sys/inflate.h> 423332Sphk 433332Sphk#include <vm/vm.h> 4412662Sdg#include <vm/vm_param.h> 4512662Sdg#include <vm/pmap.h> 4612662Sdg#include <vm/vm_map.h> 473332Sphk#include <vm/vm_kern.h> 4812662Sdg#include <vm/vm_extern.h> 493332Sphk 503784Sphkstruct imgact_gzip { 513784Sphk struct image_params *ip; 523784Sphk struct exec a_out; 533784Sphk int error; 5448079Shoek int gotheader; 553784Sphk int where; 563784Sphk u_char *inbuf; 573784Sphk u_long offset; 583784Sphk u_long output; 593784Sphk u_long len; 603784Sphk int idx; 613784Sphk u_long virtual_offset, file_offset, file_end, bss_size; 623784Sphk}; 633784Sphk 6492723Salfredstatic int exec_gzip_imgact(struct image_params *imgp); 6592723Salfredstatic int NextByte(void *vp); 6692723Salfredstatic int do_aout_hdr(struct imgact_gzip *); 6792723Salfredstatic int Flush(void *vp, u_char *, u_long siz); 683784Sphk 6912568Sbdestatic int 7012130Sdgexec_gzip_imgact(imgp) 7112130Sdg struct image_params *imgp; 723332Sphk{ 733784Sphk int error, error2 = 0; 7417974Sbde const u_char *p = (const u_char *) imgp->image_header; 753784Sphk struct imgact_gzip igz; 763784Sphk struct inflate infl; 7717386Sphk struct vmspace *vmspace; 783332Sphk 793348Sphk /* If these four are not OK, it isn't a gzip file */ 803784Sphk if (p[0] != 0x1f) 813784Sphk return -1; /* 0 Simply magic */ 823784Sphk if (p[1] != 0x8b) 833784Sphk return -1; /* 1 Simply magic */ 843784Sphk if (p[2] != 0x08) 853784Sphk return -1; /* 2 Compression method */ 863784Sphk if (p[9] != 0x03) 873784Sphk return -1; /* 9 OS compressed on */ 883332Sphk 893784Sphk /* 903784Sphk * If this one contains anything but a comment or a filename marker, 913784Sphk * we don't want to chew on it 923348Sphk */ 933784Sphk if (p[3] & ~(0x18)) 943784Sphk return ENOEXEC; /* 3 Flags */ 953332Sphk 963348Sphk /* These are of no use to us */ 973784Sphk /* 4-7 Timestamp */ 983784Sphk /* 8 Extra flags */ 993348Sphk 1003784Sphk bzero(&igz, sizeof igz); 1013784Sphk bzero(&infl, sizeof infl); 1023784Sphk infl.gz_private = (void *) &igz; 1033784Sphk infl.gz_input = NextByte; 1043784Sphk infl.gz_output = Flush; 1053332Sphk 10612130Sdg igz.ip = imgp; 1073784Sphk igz.idx = 10; 1083353Sphk 1093784Sphk if (p[3] & 0x08) { /* skip a filename */ 1103784Sphk while (p[igz.idx++]) 1113784Sphk if (igz.idx >= PAGE_SIZE) 1123784Sphk return ENOEXEC; 1133348Sphk } 1143784Sphk if (p[3] & 0x10) { /* skip a comment */ 1153784Sphk while (p[igz.idx++]) 1163784Sphk if (igz.idx >= PAGE_SIZE) 1173784Sphk return ENOEXEC; 1183348Sphk } 11917386Sphk igz.len = imgp->attr->va_size; 1203332Sphk 1213784Sphk error = inflate(&infl); 1223348Sphk 12348079Shoek /* 12448079Shoek * The unzipped file may not even have been long enough to contain 12548079Shoek * a header giving Flush() a chance to return error. Check for this. 12648079Shoek */ 12748079Shoek if ( !igz.gotheader ) 12848079Shoek return ENOEXEC; 12948079Shoek 13017386Sphk if ( !error ) { 13117386Sphk vmspace = imgp->proc->p_vmspace; 13217386Sphk error = vm_map_protect(&vmspace->vm_map, 13317386Sphk (vm_offset_t) vmspace->vm_taddr, 13417386Sphk (vm_offset_t) (vmspace->vm_taddr + 13517386Sphk (vmspace->vm_tsize << PAGE_SHIFT)) , 13617386Sphk VM_PROT_READ|VM_PROT_EXECUTE,0); 13717386Sphk } 13817386Sphk 1393784Sphk if (igz.inbuf) { 1403784Sphk error2 = 1416579Sdg vm_map_remove(kernel_map, (vm_offset_t) igz.inbuf, 1426579Sdg (vm_offset_t) igz.inbuf + PAGE_SIZE); 1433332Sphk } 1443784Sphk if (igz.error || error || error2) { 1453784Sphk printf("Output=%lu ", igz.output); 1463784Sphk printf("Inflate_error=%d igz.error=%d error2=%d where=%d\n", 1473784Sphk error, igz.error, error2, igz.where); 1483348Sphk } 1493784Sphk if (igz.error) 1503784Sphk return igz.error; 1513784Sphk if (error) 1523784Sphk return ENOEXEC; 1538876Srgrimes if (error2) 1543784Sphk return error2; 1553784Sphk return 0; 1563332Sphk} 1573332Sphk 1583784Sphkstatic int 1593784Sphkdo_aout_hdr(struct imgact_gzip * gz) 1603332Sphk{ 1613784Sphk int error; 16224848Sdyson struct vmspace *vmspace; 16314703Sbde vm_offset_t vmaddr; 1643332Sphk 1653784Sphk /* 1663784Sphk * Set file/virtual offset based on a.out variant. We do two cases: 1673784Sphk * host byte order and network byte order (for NetBSD compatibility) 1683784Sphk */ 1693784Sphk switch ((int) (gz->a_out.a_magic & 0xffff)) { 1703332Sphk case ZMAGIC: 1713784Sphk gz->virtual_offset = 0; 1723784Sphk if (gz->a_out.a_text) { 17315538Sphk gz->file_offset = PAGE_SIZE; 1743784Sphk } else { 1753784Sphk /* Bill's "screwball mode" */ 1763784Sphk gz->file_offset = 0; 1773784Sphk } 1783784Sphk break; 1793332Sphk case QMAGIC: 18015538Sphk gz->virtual_offset = PAGE_SIZE; 1813784Sphk gz->file_offset = 0; 1823784Sphk break; 1833332Sphk default: 1843784Sphk /* NetBSD compatibility */ 1853784Sphk switch ((int) (ntohl(gz->a_out.a_magic) & 0xffff)) { 1863784Sphk case ZMAGIC: 1873784Sphk case QMAGIC: 18815538Sphk gz->virtual_offset = PAGE_SIZE; 1893784Sphk gz->file_offset = 0; 1903784Sphk break; 1913784Sphk default: 1923784Sphk gz->where = __LINE__; 1933784Sphk return (-1); 1943784Sphk } 1953332Sphk } 1963332Sphk 19715538Sphk gz->bss_size = roundup(gz->a_out.a_bss, PAGE_SIZE); 1983332Sphk 1993784Sphk /* 2003784Sphk * Check various fields in header for validity/bounds. 2013784Sphk */ 2023784Sphk if ( /* entry point must lay with text region */ 2033784Sphk gz->a_out.a_entry < gz->virtual_offset || 2043784Sphk gz->a_out.a_entry >= gz->virtual_offset + gz->a_out.a_text || 2053332Sphk 2063332Sphk /* text and data size must each be page rounded */ 20715538Sphk gz->a_out.a_text & PAGE_MASK || gz->a_out.a_data & PAGE_MASK) { 2083784Sphk gz->where = __LINE__; 2093784Sphk return (-1); 2103784Sphk } 2113784Sphk /* 2123784Sphk * text/data/bss must not exceed limits 2133784Sphk */ 214125454Sjhb PROC_LOCK(gz->ip->proc); 2153784Sphk if ( /* text can't exceed maximum text size */ 21684783Sps gz->a_out.a_text > maxtsiz || 2173332Sphk 2183332Sphk /* data + bss can't exceed rlimit */ 2193784Sphk gz->a_out.a_data + gz->bss_size > 220220373Strasz lim_cur(gz->ip->proc, RLIMIT_DATA) || 221220373Strasz racct_set(gz->ip->proc, RACCT_DATA, 222220373Strasz gz->a_out.a_data + gz->bss_size) != 0) { 223125454Sjhb PROC_UNLOCK(gz->ip->proc); 2243784Sphk gz->where = __LINE__; 2253784Sphk return (ENOMEM); 2263784Sphk } 227125454Sjhb PROC_UNLOCK(gz->ip->proc); 2283784Sphk /* Find out how far we should go */ 2293784Sphk gz->file_end = gz->file_offset + gz->a_out.a_text + gz->a_out.a_data; 2303332Sphk 2313784Sphk /* 232153698Salc * Avoid a possible deadlock if the current address space is destroyed 233153698Salc * and that address space maps the locked vnode. In the common case, 234153698Salc * the locked vnode's v_usecount is decremented but remains greater 235153698Salc * than zero. Consequently, the vnode lock is not needed by vrele(). 236153698Salc * However, in cases where the vnode lock is external, such as nullfs, 237153698Salc * v_usecount may become zero. 238153698Salc */ 239175294Sattilio VOP_UNLOCK(gz->ip->vp, 0); 240153698Salc 241153698Salc /* 2423784Sphk * Destroy old process VM and create a new one (with a new stack) 2433784Sphk */ 244173361Skib error = exec_new_vmspace(gz->ip, &aout_sysvec); 2453348Sphk 246175202Sattilio vn_lock(gz->ip->vp, LK_EXCLUSIVE | LK_RETRY); 247173361Skib if (error) { 248173361Skib gz->where = __LINE__; 249173361Skib return (error); 250173361Skib } 251153698Salc 25224848Sdyson vmspace = gz->ip->proc->p_vmspace; 25324848Sdyson 2543784Sphk vmaddr = gz->virtual_offset; 2553332Sphk 2563784Sphk error = vm_mmap(&vmspace->vm_map, 2573784Sphk &vmaddr, 25817386Sphk gz->a_out.a_text + gz->a_out.a_data, 25917386Sphk VM_PROT_ALL, VM_PROT_ALL, MAP_ANON | MAP_FIXED, 260144501Sjhb OBJT_DEFAULT, 261144501Sjhb NULL, 2623784Sphk 0); 2633332Sphk 2643784Sphk if (error) { 2653784Sphk gz->where = __LINE__; 2663784Sphk return (error); 2673784Sphk } 26817386Sphk 2696579Sdg if (gz->bss_size != 0) { 2706579Sdg /* 27114087Sphk * Allocate demand-zeroed area for uninitialized data. 27214087Sphk * "bss" = 'block started by symbol' - named after the 27314087Sphk * IBM 7090 instruction of the same name. 2746579Sdg */ 27514087Sphk vmaddr = gz->virtual_offset + gz->a_out.a_text + 27614087Sphk gz->a_out.a_data; 27717386Sphk error = vm_map_find(&vmspace->vm_map, 27817386Sphk NULL, 27917386Sphk 0, 28017386Sphk &vmaddr, 28117386Sphk gz->bss_size, 28217386Sphk FALSE, VM_PROT_ALL, VM_PROT_ALL, 0); 2836579Sdg if (error) { 2846579Sdg gz->where = __LINE__; 2856579Sdg return (error); 2866579Sdg } 2873784Sphk } 2883784Sphk /* Fill in process VM information */ 2893784Sphk vmspace->vm_tsize = gz->a_out.a_text >> PAGE_SHIFT; 2903784Sphk vmspace->vm_dsize = (gz->a_out.a_data + gz->bss_size) >> PAGE_SHIFT; 29137656Sbde vmspace->vm_taddr = (caddr_t) (uintptr_t) gz->virtual_offset; 29237656Sbde vmspace->vm_daddr = (caddr_t) (uintptr_t) 29337656Sbde (gz->virtual_offset + gz->a_out.a_text); 2943332Sphk 2953784Sphk /* Fill in image_params */ 2963784Sphk gz->ip->interpreted = 0; 2973784Sphk gz->ip->entry_addr = gz->a_out.a_entry; 2983332Sphk 2993784Sphk gz->ip->proc->p_sysent = &aout_sysvec; 3003332Sphk 3013784Sphk return 0; 3023784Sphk} 3033332Sphk 3043784Sphkstatic int 3053784SphkNextByte(void *vp) 3063784Sphk{ 3073784Sphk int error; 3083784Sphk struct imgact_gzip *igz = (struct imgact_gzip *) vp; 3093332Sphk 3103784Sphk if (igz->idx >= igz->len) { 3113784Sphk igz->where = __LINE__; 3123784Sphk return GZ_EOF; 3133784Sphk } 3143784Sphk if (igz->inbuf && igz->idx < (igz->offset + PAGE_SIZE)) { 3153784Sphk return igz->inbuf[(igz->idx++) - igz->offset]; 3163784Sphk } 3173784Sphk if (igz->inbuf) { 3186579Sdg error = vm_map_remove(kernel_map, (vm_offset_t) igz->inbuf, 3196579Sdg (vm_offset_t) igz->inbuf + PAGE_SIZE); 3203784Sphk if (error) { 3213784Sphk igz->where = __LINE__; 3223784Sphk igz->error = error; 3233784Sphk return GZ_EOF; 3243784Sphk } 3253784Sphk } 3266342Sphk igz->offset = igz->idx & ~PAGE_MASK; 3273332Sphk 3283784Sphk error = vm_mmap(kernel_map, /* map */ 3293784Sphk (vm_offset_t *) & igz->inbuf, /* address */ 3303784Sphk PAGE_SIZE, /* size */ 3313784Sphk VM_PROT_READ, /* protection */ 3323784Sphk VM_PROT_READ, /* max protection */ 3333784Sphk 0, /* flags */ 334144501Sjhb OBJT_VNODE, /* handle type */ 335144501Sjhb igz->ip->vp, /* vnode */ 3363784Sphk igz->offset); /* offset */ 3373784Sphk if (error) { 3383784Sphk igz->where = __LINE__; 3393784Sphk igz->error = error; 3403784Sphk return GZ_EOF; 3413784Sphk } 3423784Sphk return igz->inbuf[(igz->idx++) - igz->offset]; 3433784Sphk} 3443332Sphk 3453784Sphkstatic int 3463784SphkFlush(void *vp, u_char * ptr, u_long siz) 3473784Sphk{ 3483784Sphk struct imgact_gzip *gz = (struct imgact_gzip *) vp; 3493784Sphk u_char *p = ptr, *q; 3503784Sphk int i; 3513348Sphk 352108533Sschweikh /* First, find an a.out-header. */ 3533784Sphk if (gz->output < sizeof gz->a_out) { 3543784Sphk q = (u_char *) & gz->a_out; 3553784Sphk i = min(siz, sizeof gz->a_out - gz->output); 3563784Sphk bcopy(p, q + gz->output, i); 3573784Sphk gz->output += i; 3583784Sphk p += i; 3593784Sphk siz -= i; 3603784Sphk if (gz->output == sizeof gz->a_out) { 36148079Shoek gz->gotheader = 1; 3623784Sphk i = do_aout_hdr(gz); 3633784Sphk if (i == -1) { 3643785Sphk if (!gz->where) 3653785Sphk gz->where = __LINE__; 3663784Sphk gz->error = ENOEXEC; 3673784Sphk return ENOEXEC; 3683784Sphk } else if (i) { 3693784Sphk gz->where = __LINE__; 3703784Sphk gz->error = i; 3713784Sphk return ENOEXEC; 3723784Sphk } 37331718Sjdp if (gz->file_offset == 0) { 37437656Sbde q = (u_char *) (uintptr_t) gz->virtual_offset; 37537015Sbde copyout(&gz->a_out, q, sizeof gz->a_out); 3763784Sphk } 3773784Sphk } 3783784Sphk } 3793784Sphk /* Skip over zero-padded first PAGE if needed */ 38037015Sbde if (gz->output < gz->file_offset && 38137015Sbde gz->output + siz > gz->file_offset) { 3823784Sphk i = min(siz, gz->file_offset - gz->output); 3833784Sphk gz->output += i; 3843784Sphk p += i; 3853784Sphk siz -= i; 3863784Sphk } 3873784Sphk if (gz->output >= gz->file_offset && gz->output < gz->file_end) { 3883784Sphk i = min(siz, gz->file_end - gz->output); 38937656Sbde q = (u_char *) (uintptr_t) 39037656Sbde (gz->virtual_offset + gz->output - gz->file_offset); 39137015Sbde copyout(p, q, i); 3923784Sphk gz->output += i; 3933784Sphk p += i; 3943784Sphk siz -= i; 3953784Sphk } 3963784Sphk gz->output += siz; 3973784Sphk return 0; 3983332Sphk} 3993332Sphk 4003784Sphk 4013332Sphk/* 4023332Sphk * Tell kern_execve.c about it, with a little help from the linker. 4033332Sphk */ 40443402Sdillonstatic struct execsw gzip_execsw = {exec_gzip_imgact, "gzip"}; 40540435SpeterEXEC_SET(execgzip, gzip_execsw); 406