imgact_gzip.c revision 125454
1189903Sjkim/* 2189903Sjkim * ---------------------------------------------------------------------------- 3189903Sjkim * "THE BEER-WARE LICENSE" (Revision 42): 4189903Sjkim * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you 5210777Sjkim * can do whatever you want with this stuff. If we meet some day, and you think 6189903Sjkim * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 7189903Sjkim * ---------------------------------------------------------------------------- 8189903Sjkim * 9189903Sjkim * 10189903Sjkim * This module handles execution of a.out files which have been run through 11189903Sjkim * "gzip". This saves diskspace, but wastes cpu-cycles and VM. 12189903Sjkim * 13189903Sjkim * TODO: 14189903Sjkim * text-segments should be made R/O after being filled 15189903Sjkim * is the vm-stuff safe ? 16189903Sjkim * should handle the entire header of gzip'ed stuff. 17189903Sjkim * inflate isn't quite reentrant yet... 18189903Sjkim * error-handling is a mess... 19189903Sjkim * so is the rest... 20189903Sjkim * tidy up unnecesary includes 21189903Sjkim */ 22189903Sjkim 23189903Sjkim#include <sys/cdefs.h> 24189903Sjkim__FBSDID("$FreeBSD: head/sys/kern/imgact_gzip.c 125454 2004-02-04 21:52:57Z jhb $"); 25189903Sjkim 26189903Sjkim#include <sys/param.h> 27189903Sjkim#include <sys/exec.h> 28189903Sjkim#include <sys/imgact.h> 29189903Sjkim#include <sys/imgact_aout.h> 30189903Sjkim#include <sys/kernel.h> 31189903Sjkim#include <sys/lock.h> 32189903Sjkim#include <sys/mman.h> 33189903Sjkim#include <sys/mutex.h> 34189903Sjkim#include <sys/proc.h> 35189903Sjkim#include <sys/resourcevar.h> 36189903Sjkim#include <sys/sysent.h> 37189903Sjkim#include <sys/systm.h> 38189903Sjkim#include <sys/vnode.h> 39189903Sjkim#include <sys/inflate.h> 40189903Sjkim 41189903Sjkim#include <vm/vm.h> 42189903Sjkim#include <vm/vm_param.h> 43189903Sjkim#include <vm/pmap.h> 44189903Sjkim#include <vm/vm_map.h> 45189903Sjkim#include <vm/vm_kern.h> 46189903Sjkim#include <vm/vm_extern.h> 47189903Sjkim 48189903Sjkimstruct imgact_gzip { 49189903Sjkim struct image_params *ip; 50189903Sjkim struct exec a_out; 51189903Sjkim int error; 52189903Sjkim int gotheader; 53189903Sjkim int where; 54189903Sjkim u_char *inbuf; 55197863Sjkim u_long offset; 56189903Sjkim u_long output; 57189903Sjkim u_long len; 58189903Sjkim int idx; 59189903Sjkim u_long virtual_offset, file_offset, file_end, bss_size; 60189903Sjkim}; 61189903Sjkim 62189903Sjkimstatic int exec_gzip_imgact(struct image_params *imgp); 63189903Sjkimstatic int NextByte(void *vp); 64189903Sjkimstatic int do_aout_hdr(struct imgact_gzip *); 65197863Sjkimstatic int Flush(void *vp, u_char *, u_long siz); 66190341Sjkim 67190341Sjkimstatic int 68189903Sjkimexec_gzip_imgact(imgp) 69189903Sjkim struct image_params *imgp; 70189903Sjkim{ 71190341Sjkim int error, error2 = 0; 72190341Sjkim const u_char *p = (const u_char *) imgp->image_header; 73190341Sjkim struct imgact_gzip igz; 74189903Sjkim struct inflate infl; 75189903Sjkim struct vmspace *vmspace; 76189903Sjkim 77189903Sjkim /* If these four are not OK, it isn't a gzip file */ 78189903Sjkim if (p[0] != 0x1f) 79189903Sjkim return -1; /* 0 Simply magic */ 80189903Sjkim if (p[1] != 0x8b) 81189903Sjkim return -1; /* 1 Simply magic */ 82189903Sjkim if (p[2] != 0x08) 83189903Sjkim return -1; /* 2 Compression method */ 84190341Sjkim if (p[9] != 0x03) 85190341Sjkim return -1; /* 9 OS compressed on */ 86190341Sjkim 87189903Sjkim /* 88189903Sjkim * If this one contains anything but a comment or a filename marker, 89198422Sjkim * we don't want to chew on it 90198422Sjkim */ 91198422Sjkim if (p[3] & ~(0x18)) 92198422Sjkim return ENOEXEC; /* 3 Flags */ 93198422Sjkim 94190341Sjkim /* These are of no use to us */ 95190341Sjkim /* 4-7 Timestamp */ 96189903Sjkim /* 8 Extra flags */ 97189903Sjkim 98189903Sjkim bzero(&igz, sizeof igz); 99189903Sjkim bzero(&infl, sizeof infl); 100189903Sjkim infl.gz_private = (void *) &igz; 101189903Sjkim infl.gz_input = NextByte; 102189903Sjkim infl.gz_output = Flush; 103189903Sjkim 104189903Sjkim igz.ip = imgp; 105189903Sjkim igz.idx = 10; 106189903Sjkim 107189903Sjkim if (p[3] & 0x08) { /* skip a filename */ 108189903Sjkim while (p[igz.idx++]) 109189903Sjkim if (igz.idx >= PAGE_SIZE) 110189903Sjkim return ENOEXEC; 111189903Sjkim } 112189903Sjkim if (p[3] & 0x10) { /* skip a comment */ 113189903Sjkim while (p[igz.idx++]) 114189903Sjkim if (igz.idx >= PAGE_SIZE) 115189903Sjkim return ENOEXEC; 116189903Sjkim } 117189903Sjkim igz.len = imgp->attr->va_size; 118189903Sjkim 119189903Sjkim error = inflate(&infl); 120189903Sjkim 121189903Sjkim /* 122189903Sjkim * The unzipped file may not even have been long enough to contain 123189903Sjkim * a header giving Flush() a chance to return error. Check for this. 124189903Sjkim */ 125189903Sjkim if ( !igz.gotheader ) 126189903Sjkim return ENOEXEC; 127189903Sjkim 128189903Sjkim if ( !error ) { 129189903Sjkim vmspace = imgp->proc->p_vmspace; 130189903Sjkim error = vm_map_protect(&vmspace->vm_map, 131189903Sjkim (vm_offset_t) vmspace->vm_taddr, 132189903Sjkim (vm_offset_t) (vmspace->vm_taddr + 133189903Sjkim (vmspace->vm_tsize << PAGE_SHIFT)) , 134197863Sjkim VM_PROT_READ|VM_PROT_EXECUTE,0); 135189903Sjkim } 136189903Sjkim 137189903Sjkim if (igz.inbuf) { 138189903Sjkim error2 = 139189903Sjkim vm_map_remove(kernel_map, (vm_offset_t) igz.inbuf, 140189903Sjkim (vm_offset_t) igz.inbuf + PAGE_SIZE); 141189903Sjkim } 142189903Sjkim if (igz.error || error || error2) { 143189903Sjkim printf("Output=%lu ", igz.output); 144189903Sjkim printf("Inflate_error=%d igz.error=%d error2=%d where=%d\n", 145189903Sjkim error, igz.error, error2, igz.where); 146189903Sjkim } 147189903Sjkim if (igz.error) 148189903Sjkim return igz.error; 149189903Sjkim if (error) 150189903Sjkim return ENOEXEC; 151189903Sjkim if (error2) 152189903Sjkim return error2; 153189903Sjkim return 0; 154189903Sjkim} 155189903Sjkim 156189903Sjkimstatic int 157189903Sjkimdo_aout_hdr(struct imgact_gzip * gz) 158189903Sjkim{ 159189903Sjkim int error; 160189903Sjkim struct vmspace *vmspace; 161189903Sjkim vm_offset_t vmaddr; 162189903Sjkim 163189903Sjkim /* 164189903Sjkim * Set file/virtual offset based on a.out variant. We do two cases: 165189903Sjkim * host byte order and network byte order (for NetBSD compatibility) 166189903Sjkim */ 167189903Sjkim switch ((int) (gz->a_out.a_magic & 0xffff)) { 168189903Sjkim case ZMAGIC: 169189903Sjkim gz->virtual_offset = 0; 170189903Sjkim if (gz->a_out.a_text) { 171189903Sjkim gz->file_offset = PAGE_SIZE; 172189903Sjkim } else { 173189903Sjkim /* Bill's "screwball mode" */ 174189903Sjkim gz->file_offset = 0; 175189903Sjkim } 176189903Sjkim break; 177189903Sjkim case QMAGIC: 178189903Sjkim gz->virtual_offset = PAGE_SIZE; 179189903Sjkim gz->file_offset = 0; 180189903Sjkim break; 181189903Sjkim default: 182189903Sjkim /* NetBSD compatibility */ 183189903Sjkim switch ((int) (ntohl(gz->a_out.a_magic) & 0xffff)) { 184189903Sjkim case ZMAGIC: 185189903Sjkim case QMAGIC: 186189903Sjkim gz->virtual_offset = PAGE_SIZE; 187189903Sjkim gz->file_offset = 0; 188189903Sjkim break; 189189903Sjkim default: 190189903Sjkim gz->where = __LINE__; 191189903Sjkim return (-1); 192189903Sjkim } 193189903Sjkim } 194189903Sjkim 195189903Sjkim gz->bss_size = roundup(gz->a_out.a_bss, PAGE_SIZE); 196189903Sjkim 197189903Sjkim /* 198189903Sjkim * Check various fields in header for validity/bounds. 199189903Sjkim */ 200189903Sjkim if ( /* entry point must lay with text region */ 201189903Sjkim gz->a_out.a_entry < gz->virtual_offset || 202189903Sjkim gz->a_out.a_entry >= gz->virtual_offset + gz->a_out.a_text || 203189903Sjkim 204189903Sjkim /* text and data size must each be page rounded */ 205189903Sjkim gz->a_out.a_text & PAGE_MASK || gz->a_out.a_data & PAGE_MASK) { 206190341Sjkim gz->where = __LINE__; 207189903Sjkim return (-1); 208189903Sjkim } 209189903Sjkim /* 210189903Sjkim * text/data/bss must not exceed limits 211189903Sjkim */ 212189903Sjkim PROC_LOCK(gz->ip->proc); 213189903Sjkim if ( /* text can't exceed maximum text size */ 214189903Sjkim gz->a_out.a_text > maxtsiz || 215189903Sjkim 216189903Sjkim /* data + bss can't exceed rlimit */ 217189903Sjkim gz->a_out.a_data + gz->bss_size > 218190341Sjkim lim_cur(gz->ip->proc, RLIMIT_DATA)) { 219190341Sjkim PROC_UNLOCK(gz->ip->proc); 220190341Sjkim gz->where = __LINE__; 221190341Sjkim return (ENOMEM); 222190341Sjkim } 223190341Sjkim PROC_UNLOCK(gz->ip->proc); 224190341Sjkim /* Find out how far we should go */ 225189903Sjkim gz->file_end = gz->file_offset + gz->a_out.a_text + gz->a_out.a_data; 226189903Sjkim 227189903Sjkim /* copy in arguments and/or environment from old process */ 228189903Sjkim error = exec_extract_strings(gz->ip); 229190635Sjkim if (error) { 230190635Sjkim gz->where = __LINE__; 231190635Sjkim return (error); 232190635Sjkim } 233190635Sjkim /* 234190635Sjkim * Destroy old process VM and create a new one (with a new stack) 235189903Sjkim */ 236189903Sjkim exec_new_vmspace(gz->ip, &aout_sysvec); 237189903Sjkim 238189903Sjkim vmspace = gz->ip->proc->p_vmspace; 239189903Sjkim 240189903Sjkim vmaddr = gz->virtual_offset; 241189903Sjkim 242189903Sjkim error = vm_mmap(&vmspace->vm_map, 243189903Sjkim &vmaddr, 244189903Sjkim gz->a_out.a_text + gz->a_out.a_data, 245189903Sjkim VM_PROT_ALL, VM_PROT_ALL, MAP_ANON | MAP_FIXED, 246189903Sjkim 0, 247189903Sjkim 0); 248189903Sjkim 249189903Sjkim if (error) { 250189903Sjkim gz->where = __LINE__; 251189903Sjkim return (error); 252189903Sjkim } 253189903Sjkim 254189903Sjkim if (gz->bss_size != 0) { 255189903Sjkim /* 256189903Sjkim * Allocate demand-zeroed area for uninitialized data. 257189903Sjkim * "bss" = 'block started by symbol' - named after the 258189903Sjkim * IBM 7090 instruction of the same name. 259189903Sjkim */ 260189903Sjkim vmaddr = gz->virtual_offset + gz->a_out.a_text + 261189903Sjkim gz->a_out.a_data; 262189903Sjkim error = vm_map_find(&vmspace->vm_map, 263189903Sjkim NULL, 264189903Sjkim 0, 265189903Sjkim &vmaddr, 266189903Sjkim gz->bss_size, 267189903Sjkim FALSE, VM_PROT_ALL, VM_PROT_ALL, 0); 268210777Sjkim if (error) { 269189903Sjkim gz->where = __LINE__; 270189903Sjkim return (error); 271189903Sjkim } 272189903Sjkim } 273190635Sjkim /* Fill in process VM information */ 274190635Sjkim vmspace->vm_tsize = gz->a_out.a_text >> PAGE_SHIFT; 275189903Sjkim vmspace->vm_dsize = (gz->a_out.a_data + gz->bss_size) >> PAGE_SHIFT; 276189903Sjkim vmspace->vm_taddr = (caddr_t) (uintptr_t) gz->virtual_offset; 277189903Sjkim vmspace->vm_daddr = (caddr_t) (uintptr_t) 278189903Sjkim (gz->virtual_offset + gz->a_out.a_text); 279189903Sjkim 280189903Sjkim /* Fill in image_params */ 281189903Sjkim gz->ip->interpreted = 0; 282189903Sjkim gz->ip->entry_addr = gz->a_out.a_entry; 283189903Sjkim 284189903Sjkim gz->ip->proc->p_sysent = &aout_sysvec; 285189903Sjkim 286189903Sjkim return 0; 287189903Sjkim} 288189903Sjkim 289189903Sjkimstatic int 290NextByte(void *vp) 291{ 292 int error; 293 struct imgact_gzip *igz = (struct imgact_gzip *) vp; 294 295 if (igz->idx >= igz->len) { 296 igz->where = __LINE__; 297 return GZ_EOF; 298 } 299 if (igz->inbuf && igz->idx < (igz->offset + PAGE_SIZE)) { 300 return igz->inbuf[(igz->idx++) - igz->offset]; 301 } 302 if (igz->inbuf) { 303 error = vm_map_remove(kernel_map, (vm_offset_t) igz->inbuf, 304 (vm_offset_t) igz->inbuf + PAGE_SIZE); 305 if (error) { 306 igz->where = __LINE__; 307 igz->error = error; 308 return GZ_EOF; 309 } 310 } 311 igz->offset = igz->idx & ~PAGE_MASK; 312 313 error = vm_mmap(kernel_map, /* map */ 314 (vm_offset_t *) & igz->inbuf, /* address */ 315 PAGE_SIZE, /* size */ 316 VM_PROT_READ, /* protection */ 317 VM_PROT_READ, /* max protection */ 318 0, /* flags */ 319 (caddr_t) igz->ip->vp, /* vnode */ 320 igz->offset); /* offset */ 321 if (error) { 322 igz->where = __LINE__; 323 igz->error = error; 324 return GZ_EOF; 325 } 326 return igz->inbuf[(igz->idx++) - igz->offset]; 327} 328 329static int 330Flush(void *vp, u_char * ptr, u_long siz) 331{ 332 struct imgact_gzip *gz = (struct imgact_gzip *) vp; 333 u_char *p = ptr, *q; 334 int i; 335 336 /* First, find an a.out-header. */ 337 if (gz->output < sizeof gz->a_out) { 338 q = (u_char *) & gz->a_out; 339 i = min(siz, sizeof gz->a_out - gz->output); 340 bcopy(p, q + gz->output, i); 341 gz->output += i; 342 p += i; 343 siz -= i; 344 if (gz->output == sizeof gz->a_out) { 345 gz->gotheader = 1; 346 i = do_aout_hdr(gz); 347 if (i == -1) { 348 if (!gz->where) 349 gz->where = __LINE__; 350 gz->error = ENOEXEC; 351 return ENOEXEC; 352 } else if (i) { 353 gz->where = __LINE__; 354 gz->error = i; 355 return ENOEXEC; 356 } 357 if (gz->file_offset == 0) { 358 q = (u_char *) (uintptr_t) gz->virtual_offset; 359 copyout(&gz->a_out, q, sizeof gz->a_out); 360 } 361 } 362 } 363 /* Skip over zero-padded first PAGE if needed */ 364 if (gz->output < gz->file_offset && 365 gz->output + siz > gz->file_offset) { 366 i = min(siz, gz->file_offset - gz->output); 367 gz->output += i; 368 p += i; 369 siz -= i; 370 } 371 if (gz->output >= gz->file_offset && gz->output < gz->file_end) { 372 i = min(siz, gz->file_end - gz->output); 373 q = (u_char *) (uintptr_t) 374 (gz->virtual_offset + gz->output - gz->file_offset); 375 copyout(p, q, i); 376 gz->output += i; 377 p += i; 378 siz -= i; 379 } 380 gz->output += siz; 381 return 0; 382} 383 384 385/* 386 * Tell kern_execve.c about it, with a little help from the linker. 387 */ 388static struct execsw gzip_execsw = {exec_gzip_imgact, "gzip"}; 389EXEC_SET(execgzip, gzip_execsw); 390