imgact_gzip.c revision 139804
154558Sbp/*- 266479Sbp * ---------------------------------------------------------------------------- 354558Sbp * "THE BEER-WARE LICENSE" (Revision 42): 454558Sbp * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you 554558Sbp * can do whatever you want with this stuff. If we meet some day, and you think 654558Sbp * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 754558Sbp * ---------------------------------------------------------------------------- 854558Sbp */ 954558Sbp 1054558Sbp/* 1154558Sbp * This module handles execution of a.out files which have been run through 1254558Sbp * "gzip". This saves diskspace, but wastes cpu-cycles and VM. 1354558Sbp * 1454558Sbp * TODO: 1554558Sbp * text-segments should be made R/O after being filled 1654558Sbp * is the vm-stuff safe ? 1754558Sbp * should handle the entire header of gzip'ed stuff. 1854558Sbp * inflate isn't quite reentrant yet... 1954558Sbp * error-handling is a mess... 2054558Sbp * so is the rest... 2154558Sbp * tidy up unnecesary includes 2254558Sbp */ 2354558Sbp 2454558Sbp#include <sys/cdefs.h> 2554558Sbp__FBSDID("$FreeBSD: head/sys/kern/imgact_gzip.c 139804 2005-01-06 23:35:40Z imp $"); 2654558Sbp 2754558Sbp#include <sys/param.h> 2854558Sbp#include <sys/exec.h> 2954558Sbp#include <sys/imgact.h> 3054558Sbp#include <sys/imgact_aout.h> 3156424Sbp#include <sys/kernel.h> 3254558Sbp#include <sys/lock.h> 3354558Sbp#include <sys/mman.h> 3454558Sbp#include <sys/mutex.h> 3554558Sbp#include <sys/proc.h> 3654558Sbp#include <sys/resourcevar.h> 3754558Sbp#include <sys/sysent.h> 3854558Sbp#include <sys/systm.h> 3954558Sbp#include <sys/vnode.h> 4054558Sbp#include <sys/inflate.h> 4154558Sbp 4254558Sbp#include <vm/vm.h> 4354558Sbp#include <vm/vm_param.h> 4454558Sbp#include <vm/pmap.h> 4554558Sbp#include <vm/vm_map.h> 4654558Sbp#include <vm/vm_kern.h> 4754558Sbp#include <vm/vm_extern.h> 4854558Sbp 4954558Sbpstruct imgact_gzip { 5054558Sbp struct image_params *ip; 5154558Sbp struct exec a_out; 5254558Sbp int error; 5354558Sbp int gotheader; 5454558Sbp int where; 5554558Sbp u_char *inbuf; 5654558Sbp u_long offset; 5754558Sbp u_long output; 5854558Sbp u_long len; 5954558Sbp int idx; 6054558Sbp u_long virtual_offset, file_offset, file_end, bss_size; 6154558Sbp}; 6254558Sbp 6354558Sbpstatic int exec_gzip_imgact(struct image_params *imgp); 6454558Sbpstatic int NextByte(void *vp); 6554558Sbpstatic int do_aout_hdr(struct imgact_gzip *); 6654558Sbpstatic int Flush(void *vp, u_char *, u_long siz); 6754558Sbp 6854558Sbpstatic int 6954558Sbpexec_gzip_imgact(imgp) 7054558Sbp struct image_params *imgp; 7154558Sbp{ 7287599Sobrien int error, error2 = 0; 7354558Sbp const u_char *p = (const u_char *) imgp->image_header; 7454558Sbp struct imgact_gzip igz; 7554558Sbp struct inflate infl; 7654558Sbp struct vmspace *vmspace; 7787599Sobrien 7854558Sbp /* If these four are not OK, it isn't a gzip file */ 7954558Sbp if (p[0] != 0x1f) 8054558Sbp return -1; /* 0 Simply magic */ 8154558Sbp if (p[1] != 0x8b) 8254558Sbp return -1; /* 1 Simply magic */ 8354558Sbp if (p[2] != 0x08) 8454558Sbp return -1; /* 2 Compression method */ 8560938Sjake if (p[9] != 0x03) 8654558Sbp return -1; /* 9 OS compressed on */ 8754558Sbp 8854558Sbp /* 8954558Sbp * If this one contains anything but a comment or a filename marker, 9060938Sjake * we don't want to chew on it 9154558Sbp */ 9254558Sbp if (p[3] & ~(0x18)) 9354558Sbp return ENOEXEC; /* 3 Flags */ 9459681Sbp 9566479Sbp /* These are of no use to us */ 9654558Sbp /* 4-7 Timestamp */ 9754558Sbp /* 8 Extra flags */ 9854558Sbp 9954558Sbp bzero(&igz, sizeof igz); 10054558Sbp bzero(&infl, sizeof infl); 10154558Sbp infl.gz_private = (void *) &igz; 10254558Sbp infl.gz_input = NextByte; 10354558Sbp infl.gz_output = Flush; 10454558Sbp 10554558Sbp igz.ip = imgp; 10659681Sbp igz.idx = 10; 10766479Sbp 10854558Sbp if (p[3] & 0x08) { /* skip a filename */ 10954558Sbp while (p[igz.idx++]) 11054558Sbp if (igz.idx >= PAGE_SIZE) 11154558Sbp return ENOEXEC; 11254558Sbp } 11354558Sbp if (p[3] & 0x10) { /* skip a comment */ 11454558Sbp while (p[igz.idx++]) 11554558Sbp if (igz.idx >= PAGE_SIZE) 11654558Sbp return ENOEXEC; 11754558Sbp } 11854558Sbp igz.len = imgp->attr->va_size; 119106939Ssam 120106939Ssam error = inflate(&infl); 12154558Sbp 12254558Sbp /* 12354558Sbp * The unzipped file may not even have been long enough to contain 12454558Sbp * a header giving Flush() a chance to return error. Check for this. 12554558Sbp */ 12654558Sbp if ( !igz.gotheader ) 12754558Sbp return ENOEXEC; 12854558Sbp 12954558Sbp if ( !error ) { 13054558Sbp vmspace = imgp->proc->p_vmspace; 131106939Ssam error = vm_map_protect(&vmspace->vm_map, 132106939Ssam (vm_offset_t) vmspace->vm_taddr, 133106939Ssam (vm_offset_t) (vmspace->vm_taddr + 13454558Sbp (vmspace->vm_tsize << PAGE_SHIFT)) , 13554558Sbp VM_PROT_READ|VM_PROT_EXECUTE,0); 13654558Sbp } 13754558Sbp 13854558Sbp if (igz.inbuf) { 13954558Sbp error2 = 14054558Sbp vm_map_remove(kernel_map, (vm_offset_t) igz.inbuf, 14154558Sbp (vm_offset_t) igz.inbuf + PAGE_SIZE); 14254558Sbp } 14354558Sbp if (igz.error || error || error2) { 14454558Sbp printf("Output=%lu ", igz.output); 14554558Sbp printf("Inflate_error=%d igz.error=%d error2=%d where=%d\n", 14654558Sbp error, igz.error, error2, igz.where); 14754558Sbp } 14854558Sbp if (igz.error) 14954558Sbp return igz.error; 15054558Sbp if (error) 15154558Sbp return ENOEXEC; 15254558Sbp if (error2) 15354558Sbp return error2; 15454558Sbp return 0; 15554558Sbp} 15654558Sbp 15754558Sbpstatic int 15854558Sbpdo_aout_hdr(struct imgact_gzip * gz) 15954558Sbp{ 16054558Sbp int error; 16172012Sphk struct vmspace *vmspace; 16254558Sbp vm_offset_t vmaddr; 16354558Sbp 16454558Sbp /* 16554558Sbp * Set file/virtual offset based on a.out variant. We do two cases: 166108172Shsu * host byte order and network byte order (for NetBSD compatibility) 16754558Sbp */ 168108172Shsu switch ((int) (gz->a_out.a_magic & 0xffff)) { 16954558Sbp case ZMAGIC: 17054558Sbp gz->virtual_offset = 0; 17154558Sbp if (gz->a_out.a_text) { 17254558Sbp gz->file_offset = PAGE_SIZE; 17354558Sbp } else { 17454558Sbp /* Bill's "screwball mode" */ 17554558Sbp gz->file_offset = 0; 17654558Sbp } 17754558Sbp break; 17854558Sbp case QMAGIC: 17954558Sbp gz->virtual_offset = PAGE_SIZE; 18054558Sbp gz->file_offset = 0; 18154558Sbp break; 18254558Sbp default: 18354558Sbp /* NetBSD compatibility */ 18454558Sbp switch ((int) (ntohl(gz->a_out.a_magic) & 0xffff)) { 18554558Sbp case ZMAGIC: 18654558Sbp case QMAGIC: 18754558Sbp gz->virtual_offset = PAGE_SIZE; 18854558Sbp gz->file_offset = 0; 189106939Ssam break; 190106939Ssam default: 191106939Ssam gz->where = __LINE__; 19254558Sbp return (-1); 19354558Sbp } 19454558Sbp } 19554558Sbp 19654558Sbp gz->bss_size = roundup(gz->a_out.a_bss, PAGE_SIZE); 19754558Sbp 19854558Sbp /* 19954558Sbp * Check various fields in header for validity/bounds. 200106939Ssam */ 20154558Sbp if ( /* entry point must lay with text region */ 20254558Sbp gz->a_out.a_entry < gz->virtual_offset || 20354558Sbp gz->a_out.a_entry >= gz->virtual_offset + gz->a_out.a_text || 20454558Sbp 20554558Sbp /* text and data size must each be page rounded */ 20654558Sbp gz->a_out.a_text & PAGE_MASK || gz->a_out.a_data & PAGE_MASK) { 20754558Sbp gz->where = __LINE__; 20854558Sbp return (-1); 20954558Sbp } 21054558Sbp /* 21154558Sbp * text/data/bss must not exceed limits 21254558Sbp */ 21354558Sbp PROC_LOCK(gz->ip->proc); 21454558Sbp if ( /* text can't exceed maximum text size */ 21554558Sbp gz->a_out.a_text > maxtsiz || 21654558Sbp 21754558Sbp /* data + bss can't exceed rlimit */ 21854558Sbp gz->a_out.a_data + gz->bss_size > 21954558Sbp lim_cur(gz->ip->proc, RLIMIT_DATA)) { 22054558Sbp PROC_UNLOCK(gz->ip->proc); 22154558Sbp gz->where = __LINE__; 22254558Sbp return (ENOMEM); 22354558Sbp } 22454558Sbp PROC_UNLOCK(gz->ip->proc); 22554558Sbp /* Find out how far we should go */ 22654558Sbp gz->file_end = gz->file_offset + gz->a_out.a_text + gz->a_out.a_data; 227106939Ssam 22871891Sbp /* copy in arguments and/or environment from old process */ 22959681Sbp error = exec_extract_strings(gz->ip); 23059681Sbp if (error) { 23154558Sbp gz->where = __LINE__; 23269152Sjlemon return (error); 23354558Sbp } 23454558Sbp /* 23554558Sbp * Destroy old process VM and create a new one (with a new stack) 23654558Sbp */ 23754558Sbp exec_new_vmspace(gz->ip, &aout_sysvec); 23854558Sbp 23954558Sbp vmspace = gz->ip->proc->p_vmspace; 24054558Sbp 24154558Sbp vmaddr = gz->virtual_offset; 24254558Sbp 24397220Speter error = vm_mmap(&vmspace->vm_map, 24454558Sbp &vmaddr, 24554558Sbp gz->a_out.a_text + gz->a_out.a_data, 24654558Sbp VM_PROT_ALL, VM_PROT_ALL, MAP_ANON | MAP_FIXED, 24754558Sbp 0, 24854558Sbp 0); 24954558Sbp 25054558Sbp if (error) { 25154558Sbp gz->where = __LINE__; 25254558Sbp return (error); 25354558Sbp } 25454558Sbp 25554558Sbp if (gz->bss_size != 0) { 25654558Sbp /* 25754558Sbp * Allocate demand-zeroed area for uninitialized data. 25854558Sbp * "bss" = 'block started by symbol' - named after the 25954558Sbp * IBM 7090 instruction of the same name. 26054558Sbp */ 26154558Sbp vmaddr = gz->virtual_offset + gz->a_out.a_text + 26254558Sbp gz->a_out.a_data; 26354558Sbp error = vm_map_find(&vmspace->vm_map, 26454558Sbp NULL, 26554558Sbp 0, 26659681Sbp &vmaddr, 26759681Sbp gz->bss_size, 26854558Sbp FALSE, VM_PROT_ALL, VM_PROT_ALL, 0); 26954558Sbp if (error) { 27054558Sbp gz->where = __LINE__; 27154558Sbp return (error); 27254558Sbp } 27354558Sbp } 27454558Sbp /* Fill in process VM information */ 27554558Sbp vmspace->vm_tsize = gz->a_out.a_text >> PAGE_SHIFT; 27654558Sbp vmspace->vm_dsize = (gz->a_out.a_data + gz->bss_size) >> PAGE_SHIFT; 27754558Sbp vmspace->vm_taddr = (caddr_t) (uintptr_t) gz->virtual_offset; 27854558Sbp vmspace->vm_daddr = (caddr_t) (uintptr_t) 27954558Sbp (gz->virtual_offset + gz->a_out.a_text); 28054558Sbp 28154558Sbp /* Fill in image_params */ 28254558Sbp gz->ip->interpreted = 0; 28354558Sbp gz->ip->entry_addr = gz->a_out.a_entry; 28459681Sbp 28559681Sbp gz->ip->proc->p_sysent = &aout_sysvec; 28654558Sbp 28754558Sbp return 0; 28854558Sbp} 28954558Sbp 29054558Sbpstatic int 29154558SbpNextByte(void *vp) 29254558Sbp{ 29354558Sbp int error; 29454558Sbp struct imgact_gzip *igz = (struct imgact_gzip *) vp; 29554558Sbp 29654558Sbp if (igz->idx >= igz->len) { 29754558Sbp igz->where = __LINE__; 29854558Sbp return GZ_EOF; 29954558Sbp } 30054558Sbp if (igz->inbuf && igz->idx < (igz->offset + PAGE_SIZE)) { 30154558Sbp return igz->inbuf[(igz->idx++) - igz->offset]; 30259681Sbp } 30359681Sbp if (igz->inbuf) { 30454558Sbp error = vm_map_remove(kernel_map, (vm_offset_t) igz->inbuf, 30554558Sbp (vm_offset_t) igz->inbuf + PAGE_SIZE); 30654558Sbp if (error) { 30754558Sbp igz->where = __LINE__; 30854558Sbp igz->error = error; 30954558Sbp return GZ_EOF; 31054558Sbp } 31154558Sbp } 31254558Sbp igz->offset = igz->idx & ~PAGE_MASK; 31354558Sbp 31471891Sbp error = vm_mmap(kernel_map, /* map */ 31554558Sbp (vm_offset_t *) & igz->inbuf, /* address */ 31654558Sbp PAGE_SIZE, /* size */ 31754558Sbp VM_PROT_READ, /* protection */ 31854558Sbp VM_PROT_READ, /* max protection */ 31954558Sbp 0, /* flags */ 32054558Sbp (caddr_t) igz->ip->vp, /* vnode */ 32154558Sbp igz->offset); /* offset */ 32254558Sbp if (error) { 32354558Sbp igz->where = __LINE__; 32454558Sbp igz->error = error; 32554558Sbp return GZ_EOF; 32654558Sbp } 32754558Sbp return igz->inbuf[(igz->idx++) - igz->offset]; 32854558Sbp} 32954558Sbp 33054558Sbpstatic int 33154558SbpFlush(void *vp, u_char * ptr, u_long siz) 33254558Sbp{ 33354558Sbp struct imgact_gzip *gz = (struct imgact_gzip *) vp; 33454558Sbp u_char *p = ptr, *q; 33554558Sbp int i; 33654558Sbp 33754558Sbp /* First, find an a.out-header. */ 33854558Sbp if (gz->output < sizeof gz->a_out) { 33954558Sbp q = (u_char *) & gz->a_out; 34054558Sbp i = min(siz, sizeof gz->a_out - gz->output); 34154558Sbp bcopy(p, q + gz->output, i); 34254558Sbp gz->output += i; 34354558Sbp p += i; 34454558Sbp siz -= i; 34554558Sbp if (gz->output == sizeof gz->a_out) { 34654558Sbp gz->gotheader = 1; 34759681Sbp i = do_aout_hdr(gz); 34854558Sbp if (i == -1) { 34954558Sbp if (!gz->where) 35054558Sbp gz->where = __LINE__; 35154558Sbp gz->error = ENOEXEC; 35254558Sbp return ENOEXEC; 35354558Sbp } else if (i) { 35454558Sbp gz->where = __LINE__; 35554558Sbp gz->error = i; 35654558Sbp return ENOEXEC; 35754558Sbp } 35854558Sbp if (gz->file_offset == 0) { 35954558Sbp q = (u_char *) (uintptr_t) gz->virtual_offset; 36054558Sbp copyout(&gz->a_out, q, sizeof gz->a_out); 36154558Sbp } 36259681Sbp } 36354558Sbp } 36454558Sbp /* Skip over zero-padded first PAGE if needed */ 36554558Sbp if (gz->output < gz->file_offset && 36659681Sbp gz->output + siz > gz->file_offset) { 36754558Sbp i = min(siz, gz->file_offset - gz->output); 36854558Sbp gz->output += i; 36954558Sbp p += i; 37054558Sbp siz -= i; 37154558Sbp } 37254558Sbp if (gz->output >= gz->file_offset && gz->output < gz->file_end) { 37354558Sbp i = min(siz, gz->file_end - gz->output); 37454558Sbp q = (u_char *) (uintptr_t) 375106939Ssam (gz->virtual_offset + gz->output - gz->file_offset); 37654558Sbp copyout(p, q, i); 37754558Sbp gz->output += i; 37854558Sbp p += i; 37954558Sbp siz -= i; 38054558Sbp } 38154558Sbp gz->output += siz; 38254558Sbp return 0; 38397220Speter} 38459681Sbp 38554558Sbp 38654558Sbp/* 38754558Sbp * Tell kern_execve.c about it, with a little help from the linker. 38854558Sbp */ 38954558Sbpstatic struct execsw gzip_execsw = {exec_gzip_imgact, "gzip"}; 39054558SbpEXEC_SET(execgzip, gzip_execsw); 39154558Sbp