imgact_gzip.c revision 125454
129138Sdg/* 229138Sdg * ---------------------------------------------------------------------------- 329138Sdg * "THE BEER-WARE LICENSE" (Revision 42): 429138Sdg * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you 529138Sdg * can do whatever you want with this stuff. If we meet some day, and you think 629138Sdg * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 729138Sdg * ---------------------------------------------------------------------------- 829138Sdg * 929138Sdg * 1029138Sdg * This module handles execution of a.out files which have been run through 1129138Sdg * "gzip". This saves diskspace, but wastes cpu-cycles and VM. 1229138Sdg * 1329138Sdg * TODO: 1429138Sdg * text-segments should be made R/O after being filled 1529138Sdg * is the vm-stuff safe ? 1629138Sdg * should handle the entire header of gzip'ed stuff. 1729138Sdg * inflate isn't quite reentrant yet... 1829138Sdg * error-handling is a mess... 1929138Sdg * so is the rest... 2029138Sdg * tidy up unnecesary includes 2129138Sdg */ 2229138Sdg 2329138Sdg#include <sys/cdefs.h> 2429138Sdg__FBSDID("$FreeBSD: head/sys/kern/imgact_gzip.c 125454 2004-02-04 21:52:57Z jhb $"); 2529138Sdg 2629138Sdg#include <sys/param.h> 2750477Speter#include <sys/exec.h> 2829138Sdg#include <sys/imgact.h> 2929138Sdg#include <sys/imgact_aout.h> 3029138Sdg#include <sys/kernel.h> 3129138Sdg#include <sys/lock.h> 3229138Sdg#include <sys/mman.h> 3329138Sdg#include <sys/mutex.h> 3474178Sjlemon#include <sys/proc.h> 3538006Sdg#include <sys/resourcevar.h> 3674178Sjlemon#include <sys/sysent.h> 3774178Sjlemon#include <sys/systm.h> 3874178Sjlemon#include <sys/vnode.h> 3974178Sjlemon#include <sys/inflate.h> 4074178Sjlemon 4174178Sjlemon#include <vm/vm.h> 4274178Sjlemon#include <vm/vm_param.h> 43112982Smux#include <vm/pmap.h> 44112982Smux#include <vm/vm_map.h> 45112982Smux#include <vm/vm_kern.h> 46112982Smux#include <vm/vm_extern.h> 47112982Smux 48112982Smuxstruct imgact_gzip { 49112982Smux struct image_params *ip; 50112982Smux struct exec a_out; 51112982Smux int error; 52112982Smux int gotheader; 53112982Smux int where; 54112982Smux u_char *inbuf; 55112982Smux u_long offset; 5674178Sjlemon u_long output; 5774178Sjlemon u_long len; 5874178Sjlemon int idx; 5974178Sjlemon u_long virtual_offset, file_offset, file_end, bss_size; 6074178Sjlemon}; 6174178Sjlemon 6274178Sjlemonstatic int exec_gzip_imgact(struct image_params *imgp); 6374178Sjlemonstatic int NextByte(void *vp); 6474178Sjlemonstatic int do_aout_hdr(struct imgact_gzip *); 6574178Sjlemonstatic int Flush(void *vp, u_char *, u_long siz); 6674178Sjlemon 6774178Sjlemonstatic int 6874178Sjlemonexec_gzip_imgact(imgp) 6974178Sjlemon struct image_params *imgp; 7074178Sjlemon{ 7174178Sjlemon int error, error2 = 0; 7274178Sjlemon const u_char *p = (const u_char *) imgp->image_header; 7387902Sluigi struct imgact_gzip igz; 7487902Sluigi struct inflate infl; 7587902Sluigi struct vmspace *vmspace; 7674178Sjlemon 7787902Sluigi /* If these four are not OK, it isn't a gzip file */ 7874178Sjlemon if (p[0] != 0x1f) 7974178Sjlemon return -1; /* 0 Simply magic */ 8074178Sjlemon if (p[1] != 0x8b) 8174178Sjlemon return -1; /* 1 Simply magic */ 8274178Sjlemon if (p[2] != 0x08) 8374178Sjlemon return -1; /* 2 Compression method */ 8474178Sjlemon if (p[9] != 0x03) 8574178Sjlemon return -1; /* 9 OS compressed on */ 8674178Sjlemon 8785461Sjlemon /* 8885461Sjlemon * If this one contains anything but a comment or a filename marker, 8985461Sjlemon * we don't want to chew on it 9085461Sjlemon */ 9185461Sjlemon if (p[3] & ~(0x18)) 9285461Sjlemon return ENOEXEC; /* 3 Flags */ 9385461Sjlemon 9485461Sjlemon /* These are of no use to us */ 9585461Sjlemon /* 4-7 Timestamp */ 9685461Sjlemon /* 8 Extra flags */ 9785461Sjlemon 9885461Sjlemon bzero(&igz, sizeof igz); 9985461Sjlemon bzero(&infl, sizeof infl); 10085461Sjlemon infl.gz_private = (void *) &igz; 10185461Sjlemon infl.gz_input = NextByte; 10285461Sjlemon infl.gz_output = Flush; 10385461Sjlemon 10474178Sjlemon igz.ip = imgp; 10574178Sjlemon igz.idx = 10; 10674178Sjlemon 107114006Snjl if (p[3] & 0x08) { /* skip a filename */ 108114006Snjl while (p[igz.idx++]) 109114006Snjl if (igz.idx >= PAGE_SIZE) 11093818Sjhb return ENOEXEC; 11174178Sjlemon } 11274178Sjlemon if (p[3] & 0x10) { /* skip a comment */ 11374178Sjlemon while (p[igz.idx++]) 11474178Sjlemon if (igz.idx >= PAGE_SIZE) 11574178Sjlemon return ENOEXEC; 11674178Sjlemon } 11774178Sjlemon igz.len = imgp->attr->va_size; 118112982Smux 119112982Smux error = inflate(&infl); 120112982Smux 121112982Smux /* 122112982Smux * The unzipped file may not even have been long enough to contain 123112982Smux * a header giving Flush() a chance to return error. Check for this. 124112982Smux */ 125112982Smux if ( !igz.gotheader ) 126112982Smux return ENOEXEC; 12774178Sjlemon 128112982Smux if ( !error ) { 129112982Smux vmspace = imgp->proc->p_vmspace; 130112982Smux error = vm_map_protect(&vmspace->vm_map, 131112982Smux (vm_offset_t) vmspace->vm_taddr, 132112982Smux (vm_offset_t) (vmspace->vm_taddr + 133112982Smux (vmspace->vm_tsize << PAGE_SHIFT)) , 134112982Smux VM_PROT_READ|VM_PROT_EXECUTE,0); 135112982Smux } 136112982Smux 137112982Smux if (igz.inbuf) { 138112982Smux error2 = 139112982Smux vm_map_remove(kernel_map, (vm_offset_t) igz.inbuf, 140112982Smux (vm_offset_t) igz.inbuf + PAGE_SIZE); 141112982Smux } 142112982Smux if (igz.error || error || error2) { 143112982Smux printf("Output=%lu ", igz.output); 144112982Smux printf("Inflate_error=%d igz.error=%d error2=%d where=%d\n", 145112982Smux error, igz.error, error2, igz.where); 146112982Smux } 147112982Smux if (igz.error) 148112982Smux return igz.error; 14974178Sjlemon if (error) 15038006Sdg return ENOEXEC; 15138006Sdg if (error2) 15238006Sdg return error2; 15329138Sdg return 0; 15429138Sdg} 15545720Speter 15671485Smjacobstatic int 15771485Smjacobdo_aout_hdr(struct imgact_gzip * gz) 15845720Speter{ 15945720Speter int error; 16065983Scp struct vmspace *vmspace; 16151821Sgallatin vm_offset_t vmaddr; 16251821Sgallatin 163112982Smux /* 164112982Smux * Set file/virtual offset based on a.out variant. We do two cases: 165112982Smux * host byte order and network byte order (for NetBSD compatibility) 166112982Smux */ 167112982Smux switch ((int) (gz->a_out.a_magic & 0xffff)) { 168112982Smux case ZMAGIC: 169112982Smux gz->virtual_offset = 0; 170112982Smux if (gz->a_out.a_text) { 171112982Smux gz->file_offset = PAGE_SIZE; 17231447Sdg } else { 17331447Sdg /* Bill's "screwball mode" */ 17429138Sdg gz->file_offset = 0; 175112982Smux } 17631447Sdg break; 17729681Sgibbs case QMAGIC: 17831447Sdg gz->virtual_offset = PAGE_SIZE; 179112982Smux gz->file_offset = 0; 18038006Sdg break; 18174178Sjlemon default: 18274178Sjlemon /* NetBSD compatibility */ 18385461Sjlemon switch ((int) (ntohl(gz->a_out.a_magic) & 0xffff)) { 18485461Sjlemon case ZMAGIC: 18585461Sjlemon case QMAGIC: 18685461Sjlemon gz->virtual_offset = PAGE_SIZE; 18758715Sdg gz->file_offset = 0; 18866007Sdg break; 18976777Sjlemon default: 19085461Sjlemon gz->where = __LINE__; 19174178Sjlemon return (-1); 19266007Sdg } 19366007Sdg } 19466007Sdg 19566007Sdg gz->bss_size = roundup(gz->a_out.a_bss, PAGE_SIZE); 19666007Sdg 197111578Swpaul /* 198111578Swpaul * Check various fields in header for validity/bounds. 19929138Sdg */ 20029138Sdg if ( /* entry point must lay with text region */ 20174178Sjlemon gz->a_out.a_entry < gz->virtual_offset || 20274178Sjlemon gz->a_out.a_entry >= gz->virtual_offset + gz->a_out.a_text || 20374178Sjlemon 20474178Sjlemon /* text and data size must each be page rounded */ 20574178Sjlemon gz->a_out.a_text & PAGE_MASK || gz->a_out.a_data & PAGE_MASK) { 20674178Sjlemon gz->where = __LINE__; 20774178Sjlemon return (-1); 20876777Sjlemon } 20985461Sjlemon /* 210106554Siedowse * text/data/bss must not exceed limits 211111578Swpaul */ 21274178Sjlemon PROC_LOCK(gz->ip->proc); 21329138Sdg if ( /* text can't exceed maximum text size */ 21429138Sdg gz->a_out.a_text > maxtsiz || 21529138Sdg 21629138Sdg /* data + bss can't exceed rlimit */ 21729138Sdg gz->a_out.a_data + gz->bss_size > 21829138Sdg lim_cur(gz->ip->proc, RLIMIT_DATA)) { 21929138Sdg PROC_UNLOCK(gz->ip->proc); 22029138Sdg gz->where = __LINE__; 22129138Sdg return (ENOMEM); 22229138Sdg } 22329138Sdg PROC_UNLOCK(gz->ip->proc); 22429138Sdg /* Find out how far we should go */ 22529138Sdg gz->file_end = gz->file_offset + gz->a_out.a_text + gz->a_out.a_data; 22629138Sdg 22729138Sdg /* copy in arguments and/or environment from old process */ 22874178Sjlemon error = exec_extract_strings(gz->ip); 22966045Sdg if (error) { 230 gz->where = __LINE__; 231 return (error); 232 } 233 /* 234 * Destroy old process VM and create a new one (with a new stack) 235 */ 236 exec_new_vmspace(gz->ip, &aout_sysvec); 237 238 vmspace = gz->ip->proc->p_vmspace; 239 240 vmaddr = gz->virtual_offset; 241 242 error = vm_mmap(&vmspace->vm_map, 243 &vmaddr, 244 gz->a_out.a_text + gz->a_out.a_data, 245 VM_PROT_ALL, VM_PROT_ALL, MAP_ANON | MAP_FIXED, 246 0, 247 0); 248 249 if (error) { 250 gz->where = __LINE__; 251 return (error); 252 } 253 254 if (gz->bss_size != 0) { 255 /* 256 * Allocate demand-zeroed area for uninitialized data. 257 * "bss" = 'block started by symbol' - named after the 258 * IBM 7090 instruction of the same name. 259 */ 260 vmaddr = gz->virtual_offset + gz->a_out.a_text + 261 gz->a_out.a_data; 262 error = vm_map_find(&vmspace->vm_map, 263 NULL, 264 0, 265 &vmaddr, 266 gz->bss_size, 267 FALSE, VM_PROT_ALL, VM_PROT_ALL, 0); 268 if (error) { 269 gz->where = __LINE__; 270 return (error); 271 } 272 } 273 /* Fill in process VM information */ 274 vmspace->vm_tsize = gz->a_out.a_text >> PAGE_SHIFT; 275 vmspace->vm_dsize = (gz->a_out.a_data + gz->bss_size) >> PAGE_SHIFT; 276 vmspace->vm_taddr = (caddr_t) (uintptr_t) gz->virtual_offset; 277 vmspace->vm_daddr = (caddr_t) (uintptr_t) 278 (gz->virtual_offset + gz->a_out.a_text); 279 280 /* Fill in image_params */ 281 gz->ip->interpreted = 0; 282 gz->ip->entry_addr = gz->a_out.a_entry; 283 284 gz->ip->proc->p_sysent = &aout_sysvec; 285 286 return 0; 287} 288 289static 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