imgact_gzip.c revision 17974
1215976Sjmallett/* 2232812Sjmallett * ---------------------------------------------------------------------------- 3215976Sjmallett * "THE BEER-WARE LICENSE" (Revision 42): 4215976Sjmallett * <phk@login.dkuug.dk> wrote this file. As long as you retain this notice you 5215976Sjmallett * can do whatever you want with this stuff. If we meet some day, and you think 6215976Sjmallett * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 7215976Sjmallett * ---------------------------------------------------------------------------- 8215976Sjmallett * 9215976Sjmallett * $Id: imgact_gzip.c,v 1.23 1996/08/01 22:00:14 phk Exp $ 10215976Sjmallett * 11215976Sjmallett * This module handles execution of a.out files which have been run through 12215976Sjmallett * "gzip". This saves diskspace, but wastes cpu-cycles and VM. 13215976Sjmallett * 14215976Sjmallett * TODO: 15215976Sjmallett * text-segments should be made R/O after being filled 16215976Sjmallett * is the vm-stuff safe ? 17215976Sjmallett * should handle the entire header of gzip'ed stuff. 18232812Sjmallett * inflate isn't quite reentrant yet... 19215976Sjmallett * error-handling is a mess... 20215976Sjmallett * so is the rest... 21215976Sjmallett * tidy up unnecesary includes 22215976Sjmallett */ 23215976Sjmallett 24215976Sjmallett#include <sys/param.h> 25215976Sjmallett#include <sys/exec.h> 26215976Sjmallett#include <sys/imgact.h> 27215976Sjmallett#include <sys/imgact_aout.h> 28215976Sjmallett#include <sys/kernel.h> 29232812Sjmallett#include <sys/mman.h> 30215976Sjmallett#include <sys/proc.h> 31215976Sjmallett#include <sys/resourcevar.h> 32215976Sjmallett#include <sys/sysent.h> 33215976Sjmallett#include <sys/systm.h> 34215976Sjmallett#include <sys/vnode.h> 35215976Sjmallett#include <sys/inflate.h> 36215976Sjmallett 37215976Sjmallett#include <vm/vm.h> 38215976Sjmallett#include <vm/vm_param.h> 39215976Sjmallett#include <vm/vm_prot.h> 40215976Sjmallett#include <vm/lock.h> 41215976Sjmallett#include <vm/pmap.h> 42215976Sjmallett#include <vm/vm_map.h> 43215976Sjmallett#include <vm/vm_kern.h> 44215976Sjmallett#include <vm/vm_extern.h> 45215976Sjmallett 46215976Sjmallettstruct imgact_gzip { 47215976Sjmallett struct image_params *ip; 48215976Sjmallett struct exec a_out; 49215976Sjmallett int error; 50215976Sjmallett int where; 51215976Sjmallett u_char *inbuf; 52232812Sjmallett u_long offset; 53232812Sjmallett u_long output; 54215976Sjmallett u_long len; 55215976Sjmallett int idx; 56215976Sjmallett u_long virtual_offset, file_offset, file_end, bss_size; 57215976Sjmallett}; 58215976Sjmallett 59215976Sjmallettstatic int exec_gzip_imgact __P((struct image_params *imgp)); 60215976Sjmallettstatic int NextByte __P((void *vp)); 61232812Sjmallettstatic int do_aout_hdr __P((struct imgact_gzip *)); 62232812Sjmallettstatic int Flush __P((void *vp, u_char *, u_long siz)); 63232812Sjmallett 64232812Sjmallettstatic int 65232812Sjmallettexec_gzip_imgact(imgp) 66215976Sjmallett struct image_params *imgp; 67215976Sjmallett{ 68215976Sjmallett int error, error2 = 0; 69215976Sjmallett const u_char *p = (const u_char *) imgp->image_header; 70215976Sjmallett struct imgact_gzip igz; 71215976Sjmallett struct inflate infl; 72215976Sjmallett struct vmspace *vmspace; 73215976Sjmallett 74215976Sjmallett /* If these four are not OK, it isn't a gzip file */ 75215976Sjmallett if (p[0] != 0x1f) 76215976Sjmallett return -1; /* 0 Simply magic */ 77215976Sjmallett if (p[1] != 0x8b) 78232812Sjmallett return -1; /* 1 Simply magic */ 79232812Sjmallett if (p[2] != 0x08) 80232812Sjmallett return -1; /* 2 Compression method */ 81232812Sjmallett if (p[9] != 0x03) 82232812Sjmallett return -1; /* 9 OS compressed on */ 83215976Sjmallett 84215976Sjmallett /* 85215976Sjmallett * If this one contains anything but a comment or a filename marker, 86215976Sjmallett * we don't want to chew on it 87215976Sjmallett */ 88215976Sjmallett if (p[3] & ~(0x18)) 89215976Sjmallett return ENOEXEC; /* 3 Flags */ 90215976Sjmallett 91215976Sjmallett /* These are of no use to us */ 92215976Sjmallett /* 4-7 Timestamp */ 93215976Sjmallett /* 8 Extra flags */ 94215976Sjmallett 95232812Sjmallett bzero(&igz, sizeof igz); 96232812Sjmallett bzero(&infl, sizeof infl); 97232812Sjmallett infl.gz_private = (void *) &igz; 98232812Sjmallett infl.gz_input = NextByte; 99232812Sjmallett infl.gz_output = Flush; 100215976Sjmallett 101215976Sjmallett igz.ip = imgp; 102215976Sjmallett igz.idx = 10; 103215976Sjmallett 104215976Sjmallett if (p[3] & 0x08) { /* skip a filename */ 105215976Sjmallett while (p[igz.idx++]) 106215976Sjmallett if (igz.idx >= PAGE_SIZE) 107215976Sjmallett return ENOEXEC; 108215976Sjmallett } 109215976Sjmallett if (p[3] & 0x10) { /* skip a comment */ 110215976Sjmallett while (p[igz.idx++]) 111215976Sjmallett if (igz.idx >= PAGE_SIZE) 112232812Sjmallett return ENOEXEC; 113232812Sjmallett } 114232812Sjmallett igz.len = imgp->attr->va_size; 115232812Sjmallett 116232812Sjmallett error = inflate(&infl); 117215976Sjmallett 118215976Sjmallett if ( !error ) { 119215976Sjmallett vmspace = imgp->proc->p_vmspace; 120215976Sjmallett error = vm_map_protect(&vmspace->vm_map, 121215976Sjmallett (vm_offset_t) vmspace->vm_taddr, 122215976Sjmallett (vm_offset_t) (vmspace->vm_taddr + 123215976Sjmallett (vmspace->vm_tsize << PAGE_SHIFT)) , 124215976Sjmallett VM_PROT_READ|VM_PROT_EXECUTE,0); 125215976Sjmallett } 126215976Sjmallett 127215976Sjmallett if (igz.inbuf) { 128215976Sjmallett error2 = 129232812Sjmallett vm_map_remove(kernel_map, (vm_offset_t) igz.inbuf, 130232812Sjmallett (vm_offset_t) igz.inbuf + PAGE_SIZE); 131232812Sjmallett } 132232812Sjmallett if (igz.error || error || error2) { 133232812Sjmallett printf("Output=%lu ", igz.output); 134215976Sjmallett printf("Inflate_error=%d igz.error=%d error2=%d where=%d\n", 135215976Sjmallett error, igz.error, error2, igz.where); 136215976Sjmallett } 137215976Sjmallett if (igz.error) 138215976Sjmallett return igz.error; 139215976Sjmallett if (error) 140215976Sjmallett return ENOEXEC; 141215976Sjmallett if (error2) 142215976Sjmallett return error2; 143215976Sjmallett return 0; 144215976Sjmallett} 145215976Sjmallett 146232812Sjmallettstatic int 147232812Sjmallettdo_aout_hdr(struct imgact_gzip * gz) 148232812Sjmallett{ 149232812Sjmallett int error; 150232812Sjmallett struct vmspace *vmspace = gz->ip->proc->p_vmspace; 151215976Sjmallett vm_offset_t vmaddr; 152215976Sjmallett 153215976Sjmallett /* 154215976Sjmallett * Set file/virtual offset based on a.out variant. We do two cases: 155215976Sjmallett * host byte order and network byte order (for NetBSD compatibility) 156215976Sjmallett */ 157215976Sjmallett switch ((int) (gz->a_out.a_magic & 0xffff)) { 158215976Sjmallett case ZMAGIC: 159215976Sjmallett gz->virtual_offset = 0; 160215976Sjmallett if (gz->a_out.a_text) { 161215976Sjmallett gz->file_offset = PAGE_SIZE; 162215976Sjmallett } else { 163232812Sjmallett /* Bill's "screwball mode" */ 164232812Sjmallett gz->file_offset = 0; 165232812Sjmallett } 166232812Sjmallett break; 167232812Sjmallett case QMAGIC: 168215976Sjmallett gz->virtual_offset = PAGE_SIZE; 169215976Sjmallett gz->file_offset = 0; 170215976Sjmallett break; 171215976Sjmallett default: 172215976Sjmallett /* NetBSD compatibility */ 173215976Sjmallett switch ((int) (ntohl(gz->a_out.a_magic) & 0xffff)) { 174215976Sjmallett case ZMAGIC: 175215976Sjmallett case QMAGIC: 176215976Sjmallett gz->virtual_offset = PAGE_SIZE; 177215976Sjmallett gz->file_offset = 0; 178215976Sjmallett break; 179215976Sjmallett default: 180232812Sjmallett gz->where = __LINE__; 181232812Sjmallett return (-1); 182232812Sjmallett } 183232812Sjmallett } 184232812Sjmallett 185215976Sjmallett gz->bss_size = roundup(gz->a_out.a_bss, PAGE_SIZE); 186215976Sjmallett 187215976Sjmallett /* 188215976Sjmallett * Check various fields in header for validity/bounds. 189215976Sjmallett */ 190215976Sjmallett if ( /* entry point must lay with text region */ 191215976Sjmallett gz->a_out.a_entry < gz->virtual_offset || 192215976Sjmallett gz->a_out.a_entry >= gz->virtual_offset + gz->a_out.a_text || 193215976Sjmallett 194215976Sjmallett /* text and data size must each be page rounded */ 195215976Sjmallett gz->a_out.a_text & PAGE_MASK || gz->a_out.a_data & PAGE_MASK) { 196215976Sjmallett gz->where = __LINE__; 197232812Sjmallett return (-1); 198232812Sjmallett } 199232812Sjmallett /* 200232812Sjmallett * text/data/bss must not exceed limits 201232812Sjmallett */ 202215976Sjmallett if ( /* text can't exceed maximum text size */ 203215976Sjmallett gz->a_out.a_text > MAXTSIZ || 204215976Sjmallett 205215976Sjmallett /* data + bss can't exceed maximum data size */ 206215976Sjmallett gz->a_out.a_data + gz->bss_size > MAXDSIZ || 207215976Sjmallett 208215976Sjmallett /* data + bss can't exceed rlimit */ 209215976Sjmallett gz->a_out.a_data + gz->bss_size > 210215976Sjmallett gz->ip->proc->p_rlimit[RLIMIT_DATA].rlim_cur) { 211215976Sjmallett gz->where = __LINE__; 212215976Sjmallett return (ENOMEM); 213215976Sjmallett } 214232812Sjmallett /* Find out how far we should go */ 215232812Sjmallett gz->file_end = gz->file_offset + gz->a_out.a_text + gz->a_out.a_data; 216232812Sjmallett 217232812Sjmallett /* copy in arguments and/or environment from old process */ 218232812Sjmallett error = exec_extract_strings(gz->ip); 219215976Sjmallett if (error) { 220215976Sjmallett gz->where = __LINE__; 221215976Sjmallett return (error); 222215976Sjmallett } 223215976Sjmallett /* 224215976Sjmallett * Destroy old process VM and create a new one (with a new stack) 225215976Sjmallett */ 226215976Sjmallett exec_new_vmspace(gz->ip); 227215976Sjmallett 228215976Sjmallett vmaddr = gz->virtual_offset; 229215976Sjmallett 230215976Sjmallett error = vm_mmap(&vmspace->vm_map, 231232812Sjmallett &vmaddr, 232232812Sjmallett gz->a_out.a_text + gz->a_out.a_data, 233232812Sjmallett VM_PROT_ALL, VM_PROT_ALL, MAP_ANON | MAP_FIXED, 234232812Sjmallett 0, 235232812Sjmallett 0); 236215976Sjmallett 237215976Sjmallett if (error) { 238215976Sjmallett gz->where = __LINE__; 239215976Sjmallett return (error); 240215976Sjmallett } 241215976Sjmallett 242215976Sjmallett if (gz->bss_size != 0) { 243215976Sjmallett /* 244215976Sjmallett * Allocate demand-zeroed area for uninitialized data. 245215976Sjmallett * "bss" = 'block started by symbol' - named after the 246215976Sjmallett * IBM 7090 instruction of the same name. 247215976Sjmallett */ 248232812Sjmallett vmaddr = gz->virtual_offset + gz->a_out.a_text + 249232812Sjmallett gz->a_out.a_data; 250232812Sjmallett error = vm_map_find(&vmspace->vm_map, 251232812Sjmallett NULL, 252232812Sjmallett 0, 253215976Sjmallett &vmaddr, 254215976Sjmallett gz->bss_size, 255215976Sjmallett FALSE, VM_PROT_ALL, VM_PROT_ALL, 0); 256215976Sjmallett if (error) { 257215976Sjmallett gz->where = __LINE__; 258215976Sjmallett return (error); 259215976Sjmallett } 260215976Sjmallett } 261215976Sjmallett /* Fill in process VM information */ 262215976Sjmallett vmspace->vm_tsize = gz->a_out.a_text >> PAGE_SHIFT; 263215976Sjmallett vmspace->vm_dsize = (gz->a_out.a_data + gz->bss_size) >> PAGE_SHIFT; 264215976Sjmallett vmspace->vm_taddr = (caddr_t) gz->virtual_offset; 265232812Sjmallett vmspace->vm_daddr = (caddr_t) gz->virtual_offset + gz->a_out.a_text; 266232812Sjmallett 267232812Sjmallett /* Fill in image_params */ 268232812Sjmallett gz->ip->interpreted = 0; 269232812Sjmallett gz->ip->entry_addr = gz->a_out.a_entry; 270215976Sjmallett 271215976Sjmallett gz->ip->proc->p_sysent = &aout_sysvec; 272215976Sjmallett 273215976Sjmallett return 0; 274215976Sjmallett} 275215976Sjmallett 276215976Sjmallettstatic int 277215976SjmallettNextByte(void *vp) 278215976Sjmallett{ 279215976Sjmallett int error; 280215976Sjmallett struct imgact_gzip *igz = (struct imgact_gzip *) vp; 281215976Sjmallett 282232812Sjmallett if (igz->idx >= igz->len) { 283232812Sjmallett igz->where = __LINE__; 284232812Sjmallett return GZ_EOF; 285232812Sjmallett } 286232812Sjmallett if (igz->inbuf && igz->idx < (igz->offset + PAGE_SIZE)) { 287215976Sjmallett return igz->inbuf[(igz->idx++) - igz->offset]; 288215976Sjmallett } 289215976Sjmallett if (igz->inbuf) { 290215976Sjmallett error = vm_map_remove(kernel_map, (vm_offset_t) igz->inbuf, 291215976Sjmallett (vm_offset_t) igz->inbuf + PAGE_SIZE); 292215976Sjmallett if (error) { 293215976Sjmallett igz->where = __LINE__; 294215976Sjmallett igz->error = error; 295215976Sjmallett return GZ_EOF; 296215976Sjmallett } 297215976Sjmallett } 298215976Sjmallett igz->offset = igz->idx & ~PAGE_MASK; 299232812Sjmallett 300232812Sjmallett error = vm_mmap(kernel_map, /* map */ 301232812Sjmallett (vm_offset_t *) & igz->inbuf, /* address */ 302232812Sjmallett PAGE_SIZE, /* size */ 303232812Sjmallett VM_PROT_READ, /* protection */ 304215976Sjmallett VM_PROT_READ, /* max protection */ 305215976Sjmallett 0, /* flags */ 306215976Sjmallett (caddr_t) igz->ip->vp, /* vnode */ 307215976Sjmallett igz->offset); /* offset */ 308215976Sjmallett if (error) { 309215976Sjmallett igz->where = __LINE__; 310215976Sjmallett igz->error = error; 311215976Sjmallett return GZ_EOF; 312215976Sjmallett } 313215976Sjmallett return igz->inbuf[(igz->idx++) - igz->offset]; 314215976Sjmallett} 315215976Sjmallett 316232812Sjmallettstatic int 317232812SjmallettFlush(void *vp, u_char * ptr, u_long siz) 318232812Sjmallett{ 319232812Sjmallett struct imgact_gzip *gz = (struct imgact_gzip *) vp; 320232812Sjmallett u_char *p = ptr, *q; 321215976Sjmallett int i; 322215976Sjmallett 323215976Sjmallett /* First, find a a.out-header */ 324215976Sjmallett if (gz->output < sizeof gz->a_out) { 325215976Sjmallett q = (u_char *) & gz->a_out; 326215976Sjmallett i = min(siz, sizeof gz->a_out - gz->output); 327215976Sjmallett bcopy(p, q + gz->output, i); 328215976Sjmallett gz->output += i; 329215976Sjmallett p += i; 330215976Sjmallett siz -= i; 331215976Sjmallett if (gz->output == sizeof gz->a_out) { 332215976Sjmallett i = do_aout_hdr(gz); 333232812Sjmallett if (i == -1) { 334232812Sjmallett if (!gz->where) 335232812Sjmallett gz->where = __LINE__; 336232812Sjmallett gz->error = ENOEXEC; 337232812Sjmallett return ENOEXEC; 338215976Sjmallett } else if (i) { 339215976Sjmallett gz->where = __LINE__; 340215976Sjmallett gz->error = i; 341215976Sjmallett return ENOEXEC; 342215976Sjmallett } 343215976Sjmallett if (gz->file_offset < sizeof gz->a_out) { 344215976Sjmallett q = (u_char *) gz->virtual_offset + gz->output - gz->file_offset; 345215976Sjmallett bcopy(&gz->a_out, q, sizeof gz->a_out - gz->file_offset); 346215976Sjmallett } 347215976Sjmallett } 348215976Sjmallett } 349215976Sjmallett /* Skip over zero-padded first PAGE if needed */ 350232812Sjmallett if (gz->output < gz->file_offset && (gz->output + siz) > gz->file_offset) { 351232812Sjmallett i = min(siz, gz->file_offset - gz->output); 352232812Sjmallett gz->output += i; 353232812Sjmallett p += i; 354232812Sjmallett siz -= i; 355215976Sjmallett } 356215976Sjmallett if (gz->output >= gz->file_offset && gz->output < gz->file_end) { 357215976Sjmallett i = min(siz, gz->file_end - gz->output); 358215976Sjmallett q = (u_char *) gz->virtual_offset + gz->output - gz->file_offset; 359215976Sjmallett bcopy(p, q, i); 360215976Sjmallett gz->output += i; 361215976Sjmallett p += i; 362215976Sjmallett siz -= i; 363215976Sjmallett } 364215976Sjmallett gz->output += siz; 365215976Sjmallett return 0; 366215976Sjmallett} 367232812Sjmallett 368232812Sjmallett 369232812Sjmallett/* 370232812Sjmallett * Tell kern_execve.c about it, with a little help from the linker. 371232812Sjmallett * Since `const' objects end up in the text segment, TEXT_SET is the 372215976Sjmallett * correct directive to use. 373215976Sjmallett */ 374215976Sjmallett 375215976Sjmallettstatic const struct execsw gzip_execsw = {exec_gzip_imgact, "gzip"}; 376215976SjmallettTEXT_SET(execsw_set, gzip_execsw); 377215976Sjmallett