imgact_gzip.c revision 93149
1129199Scognet/* 2129199Scognet * ---------------------------------------------------------------------------- 3129199Scognet * "THE BEER-WARE LICENSE" (Revision 42): 4129199Scognet * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you 5129199Scognet * can do whatever you want with this stuff. If we meet some day, and you think 6129199Scognet * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 7129199Scognet * ---------------------------------------------------------------------------- 8129199Scognet * 9129199Scognet * $FreeBSD: head/sys/kern/imgact_gzip.c 93149 2002-03-25 13:52:45Z phk $ 10129199Scognet * 11135670Scognet * This module handles execution of a.out files which have been run through 12129199Scognet * "gzip". This saves diskspace, but wastes cpu-cycles and VM. 13129199Scognet * 14129199Scognet * TODO: 15135670Scognet * text-segments should be made R/O after being filled 16129199Scognet * is the vm-stuff safe ? 17129199Scognet * should handle the entire header of gzip'ed stuff. 18129199Scognet * inflate isn't quite reentrant yet... 19129199Scognet * error-handling is a mess... 20169612Swkoszek * so is the rest... 21129199Scognet * tidy up unnecesary includes 22129199Scognet */ 23136910Sru 24129199Scognet#include <sys/param.h> 25129199Scognet#include <sys/exec.h> 26129199Scognet#include <sys/imgact.h> 27129199Scognet#include <sys/imgact_aout.h> 28129199Scognet#include <sys/kernel.h> 29129199Scognet#include <sys/lock.h> 30129199Scognet#include <sys/mman.h> 31129199Scognet#include <sys/mutex.h> 32129199Scognet#include <sys/proc.h> 33129199Scognet#include <sys/resourcevar.h> 34129199Scognet#include <sys/sysent.h> 35129199Scognet#include <sys/systm.h> 36135670Scognet#include <sys/vnode.h> 37135670Scognet#include <sys/inflate.h> 38140479Scognet 39140479Scognet#include <vm/vm.h> 40140479Scognet#include <vm/vm_param.h> 41140479Scognet#include <vm/pmap.h> 42160479Scognet#include <vm/vm_map.h> 43140479Scognet#include <vm/vm_kern.h> 44140479Scognet#include <vm/vm_extern.h> 45151423Scognet 46157165Scognetstruct imgact_gzip { 47153112Scognet struct image_params *ip; 48147630Scognet struct exec a_out; 49150862Scognet int error; 50150862Scognet int gotheader; 51150862Scognet int where; 52160629Scognet u_char *inbuf; 53160629Scognet u_long offset; 54160629Scognet u_long output; 55160629Scognet u_long len; 56152782Scognet int idx; 57152742Scognet u_long virtual_offset, file_offset, file_end, bss_size; 58152742Scognet}; 59153549Scognet 60152742Scognetstatic int exec_gzip_imgact(struct image_params *imgp); 61152742Scognetstatic int NextByte(void *vp); 62152742Scognetstatic int do_aout_hdr(struct imgact_gzip *); 63152742Scognetstatic int Flush(void *vp, u_char *, u_long siz); 64152742Scognet 65153566Scognetstatic int 66175984Srajexec_gzip_imgact(imgp) 67175984Sraj struct image_params *imgp; 68175984Sraj{ 69175984Sraj int error, error2 = 0; 70159084Scognet const u_char *p = (const u_char *) imgp->image_header; 71159084Scognet struct imgact_gzip igz; 72159084Scognet struct inflate infl; 73171619Scognet struct vmspace *vmspace; 74171619Scognet 75153566Scognet /* If these four are not OK, it isn't a gzip file */ 76159758Scognet if (p[0] != 0x1f) 77153566Scognet return -1; /* 0 Simply magic */ 78153566Scognet if (p[1] != 0x8b) 79153549Scognet return -1; /* 1 Simply magic */ 80153566Scognet if (p[2] != 0x08) 81171619Scognet return -1; /* 2 Compression method */ 82171619Scognet if (p[9] != 0x03) 83171619Scognet return -1; /* 9 OS compressed on */ 84153549Scognet 85153566Scognet /* 86159557Scognet * If this one contains anything but a comment or a filename marker, 87159557Scognet * we don't want to chew on it 88153549Scognet */ 89171619Scognet if (p[3] & ~(0x18)) 90171619Scognet return ENOEXEC; /* 3 Flags */ 91153549Scognet 92171619Scognet /* These are of no use to us */ 93171619Scognet /* 4-7 Timestamp */ 94152742Scognet /* 8 Extra flags */ 95153566Scognet 96153566Scognet bzero(&igz, sizeof igz); 97153566Scognet bzero(&infl, sizeof infl); 98153566Scognet infl.gz_private = (void *) &igz; 99154561Scognet infl.gz_input = NextByte; 100154561Scognet infl.gz_output = Flush; 101153566Scognet 102153112Scognet igz.ip = imgp; 103154561Scognet igz.idx = 10; 104153566Scognet 105153549Scognet if (p[3] & 0x08) { /* skip a filename */ 106171619Scognet while (p[igz.idx++]) 107171619Scognet if (igz.idx >= PAGE_SIZE) 108153549Scognet return ENOEXEC; 109171619Scognet } 110171619Scognet if (p[3] & 0x10) { /* skip a comment */ 111153112Scognet while (p[igz.idx++]) 112153566Scognet if (igz.idx >= PAGE_SIZE) 113153549Scognet return ENOEXEC; 114171619Scognet } 115150862Scognet igz.len = imgp->attr->va_size; 116129199Scognet 117129199Scognet error = inflate(&infl); 118129199Scognet 119129199Scognet /* 120129199Scognet * The unzipped file may not even have been long enough to contain 121129199Scognet * a header giving Flush() a chance to return error. Check for this. 122129199Scognet */ 123129199Scognet if ( !igz.gotheader ) 124129199Scognet return ENOEXEC; 125129199Scognet 126129199Scognet if ( !error ) { 127129199Scognet vmspace = imgp->proc->p_vmspace; 128153549Scognet error = vm_map_protect(&vmspace->vm_map, 129153549Scognet (vm_offset_t) vmspace->vm_taddr, 130150918Scognet (vm_offset_t) (vmspace->vm_taddr + 131153549Scognet (vmspace->vm_tsize << PAGE_SHIFT)) , 132153549Scognet VM_PROT_READ|VM_PROT_EXECUTE,0); 133153549Scognet } 134153549Scognet 135150918Scognet if (igz.inbuf) { 136135670Scognet error2 = 137135670Scognet vm_map_remove(kernel_map, (vm_offset_t) igz.inbuf, 138135670Scognet (vm_offset_t) igz.inbuf + PAGE_SIZE); 139129199Scognet } 140129199Scognet if (igz.error || error || error2) { 141129199Scognet printf("Output=%lu ", igz.output); 142 printf("Inflate_error=%d igz.error=%d error2=%d where=%d\n", 143 error, igz.error, error2, igz.where); 144 } 145 if (igz.error) 146 return igz.error; 147 if (error) 148 return ENOEXEC; 149 if (error2) 150 return error2; 151 return 0; 152} 153 154static int 155do_aout_hdr(struct imgact_gzip * gz) 156{ 157 int error; 158 struct vmspace *vmspace; 159 vm_offset_t vmaddr; 160 161 /* 162 * Set file/virtual offset based on a.out variant. We do two cases: 163 * host byte order and network byte order (for NetBSD compatibility) 164 */ 165 switch ((int) (gz->a_out.a_magic & 0xffff)) { 166 case ZMAGIC: 167 gz->virtual_offset = 0; 168 if (gz->a_out.a_text) { 169 gz->file_offset = PAGE_SIZE; 170 } else { 171 /* Bill's "screwball mode" */ 172 gz->file_offset = 0; 173 } 174 break; 175 case QMAGIC: 176 gz->virtual_offset = PAGE_SIZE; 177 gz->file_offset = 0; 178 break; 179 default: 180 /* NetBSD compatibility */ 181 switch ((int) (ntohl(gz->a_out.a_magic) & 0xffff)) { 182 case ZMAGIC: 183 case QMAGIC: 184 gz->virtual_offset = PAGE_SIZE; 185 gz->file_offset = 0; 186 break; 187 default: 188 gz->where = __LINE__; 189 return (-1); 190 } 191 } 192 193 gz->bss_size = roundup(gz->a_out.a_bss, PAGE_SIZE); 194 195 /* 196 * Check various fields in header for validity/bounds. 197 */ 198 if ( /* entry point must lay with text region */ 199 gz->a_out.a_entry < gz->virtual_offset || 200 gz->a_out.a_entry >= gz->virtual_offset + gz->a_out.a_text || 201 202 /* text and data size must each be page rounded */ 203 gz->a_out.a_text & PAGE_MASK || gz->a_out.a_data & PAGE_MASK) { 204 gz->where = __LINE__; 205 return (-1); 206 } 207 /* 208 * text/data/bss must not exceed limits 209 */ 210 mtx_assert(&Giant, MA_OWNED); 211 if ( /* text can't exceed maximum text size */ 212 gz->a_out.a_text > maxtsiz || 213 214 /* data + bss can't exceed rlimit */ 215 gz->a_out.a_data + gz->bss_size > 216 gz->ip->proc->p_rlimit[RLIMIT_DATA].rlim_cur) { 217 gz->where = __LINE__; 218 return (ENOMEM); 219 } 220 /* Find out how far we should go */ 221 gz->file_end = gz->file_offset + gz->a_out.a_text + gz->a_out.a_data; 222 223 /* copy in arguments and/or environment from old process */ 224 error = exec_extract_strings(gz->ip); 225 if (error) { 226 gz->where = __LINE__; 227 return (error); 228 } 229 /* 230 * Destroy old process VM and create a new one (with a new stack) 231 */ 232 exec_new_vmspace(gz->ip); 233 234 vmspace = gz->ip->proc->p_vmspace; 235 236 vmaddr = gz->virtual_offset; 237 238 error = vm_mmap(&vmspace->vm_map, 239 &vmaddr, 240 gz->a_out.a_text + gz->a_out.a_data, 241 VM_PROT_ALL, VM_PROT_ALL, MAP_ANON | MAP_FIXED, 242 0, 243 0); 244 245 if (error) { 246 gz->where = __LINE__; 247 return (error); 248 } 249 250 if (gz->bss_size != 0) { 251 /* 252 * Allocate demand-zeroed area for uninitialized data. 253 * "bss" = 'block started by symbol' - named after the 254 * IBM 7090 instruction of the same name. 255 */ 256 vmaddr = gz->virtual_offset + gz->a_out.a_text + 257 gz->a_out.a_data; 258 error = vm_map_find(&vmspace->vm_map, 259 NULL, 260 0, 261 &vmaddr, 262 gz->bss_size, 263 FALSE, VM_PROT_ALL, VM_PROT_ALL, 0); 264 if (error) { 265 gz->where = __LINE__; 266 return (error); 267 } 268 } 269 /* Fill in process VM information */ 270 vmspace->vm_tsize = gz->a_out.a_text >> PAGE_SHIFT; 271 vmspace->vm_dsize = (gz->a_out.a_data + gz->bss_size) >> PAGE_SHIFT; 272 vmspace->vm_taddr = (caddr_t) (uintptr_t) gz->virtual_offset; 273 vmspace->vm_daddr = (caddr_t) (uintptr_t) 274 (gz->virtual_offset + gz->a_out.a_text); 275 276 /* Fill in image_params */ 277 gz->ip->interpreted = 0; 278 gz->ip->entry_addr = gz->a_out.a_entry; 279 280 gz->ip->proc->p_sysent = &aout_sysvec; 281 282 return 0; 283} 284 285static int 286NextByte(void *vp) 287{ 288 int error; 289 struct imgact_gzip *igz = (struct imgact_gzip *) vp; 290 291 if (igz->idx >= igz->len) { 292 igz->where = __LINE__; 293 return GZ_EOF; 294 } 295 if (igz->inbuf && igz->idx < (igz->offset + PAGE_SIZE)) { 296 return igz->inbuf[(igz->idx++) - igz->offset]; 297 } 298 if (igz->inbuf) { 299 error = vm_map_remove(kernel_map, (vm_offset_t) igz->inbuf, 300 (vm_offset_t) igz->inbuf + PAGE_SIZE); 301 if (error) { 302 igz->where = __LINE__; 303 igz->error = error; 304 return GZ_EOF; 305 } 306 } 307 igz->offset = igz->idx & ~PAGE_MASK; 308 309 error = vm_mmap(kernel_map, /* map */ 310 (vm_offset_t *) & igz->inbuf, /* address */ 311 PAGE_SIZE, /* size */ 312 VM_PROT_READ, /* protection */ 313 VM_PROT_READ, /* max protection */ 314 0, /* flags */ 315 (caddr_t) igz->ip->vp, /* vnode */ 316 igz->offset); /* offset */ 317 if (error) { 318 igz->where = __LINE__; 319 igz->error = error; 320 return GZ_EOF; 321 } 322 return igz->inbuf[(igz->idx++) - igz->offset]; 323} 324 325static int 326Flush(void *vp, u_char * ptr, u_long siz) 327{ 328 struct imgact_gzip *gz = (struct imgact_gzip *) vp; 329 u_char *p = ptr, *q; 330 int i; 331 332 /* First, find a a.out-header */ 333 if (gz->output < sizeof gz->a_out) { 334 q = (u_char *) & gz->a_out; 335 i = min(siz, sizeof gz->a_out - gz->output); 336 bcopy(p, q + gz->output, i); 337 gz->output += i; 338 p += i; 339 siz -= i; 340 if (gz->output == sizeof gz->a_out) { 341 gz->gotheader = 1; 342 i = do_aout_hdr(gz); 343 if (i == -1) { 344 if (!gz->where) 345 gz->where = __LINE__; 346 gz->error = ENOEXEC; 347 return ENOEXEC; 348 } else if (i) { 349 gz->where = __LINE__; 350 gz->error = i; 351 return ENOEXEC; 352 } 353 if (gz->file_offset == 0) { 354 q = (u_char *) (uintptr_t) gz->virtual_offset; 355 copyout(&gz->a_out, q, sizeof gz->a_out); 356 } 357 } 358 } 359 /* Skip over zero-padded first PAGE if needed */ 360 if (gz->output < gz->file_offset && 361 gz->output + siz > gz->file_offset) { 362 i = min(siz, gz->file_offset - gz->output); 363 gz->output += i; 364 p += i; 365 siz -= i; 366 } 367 if (gz->output >= gz->file_offset && gz->output < gz->file_end) { 368 i = min(siz, gz->file_end - gz->output); 369 q = (u_char *) (uintptr_t) 370 (gz->virtual_offset + gz->output - gz->file_offset); 371 copyout(p, q, i); 372 gz->output += i; 373 p += i; 374 siz -= i; 375 } 376 gz->output += siz; 377 return 0; 378} 379 380 381/* 382 * Tell kern_execve.c about it, with a little help from the linker. 383 */ 384static struct execsw gzip_execsw = {exec_gzip_imgact, "gzip"}; 385EXEC_SET(execgzip, gzip_execsw); 386