imgact_gzip.c revision 15494
1/* 2 * ---------------------------------------------------------------------------- 3 * "THE BEER-WARE LICENSE" (Revision 42): 4 * <phk@login.dkuug.dk> wrote this file. As long as you retain this notice you 5 * can do whatever you want with this stuff. If we meet some day, and you think 6 * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 7 * ---------------------------------------------------------------------------- 8 * 9 * $Id: imgact_gzip.c,v 1.20 1996/03/19 15:02:47 bde Exp $ 10 * 11 * This module handles execution of a.out files which have been run through 12 * "gzip". This saves diskspace, but wastes cpu-cycles and VM. 13 * 14 * TODO: 15 * text-segments should be made R/O after being filled 16 * is the vm-stuff safe ? 17 * should handle the entire header of gzip'ed stuff. 18 * inflate isn't quite reentrant yet... 19 * error-handling is a mess... 20 * so is the rest... 21 * tidy up unnecesary includes 22 */ 23 24#include <sys/param.h> 25#include <sys/exec.h> 26#include <sys/imgact.h> 27#include <sys/imgact_aout.h> 28#include <sys/kernel.h> 29#include <sys/mman.h> 30#include <sys/proc.h> 31#include <sys/resourcevar.h> 32#include <sys/sysent.h> 33#include <sys/systm.h> 34#include <sys/vnode.h> 35#include <sys/inflate.h> 36 37#include <vm/vm.h> 38#include <vm/vm_param.h> 39#include <vm/vm_prot.h> 40#include <vm/lock.h> 41#include <vm/pmap.h> 42#include <vm/vm_map.h> 43#include <vm/vm_kern.h> 44#include <vm/vm_extern.h> 45 46struct imgact_gzip { 47 struct image_params *ip; 48 struct exec a_out; 49 int error; 50 int where; 51 u_char *inbuf; 52 u_long offset; 53 u_long output; 54 u_long len; 55 int idx; 56 u_long virtual_offset, file_offset, file_end, bss_size; 57}; 58 59static int exec_gzip_imgact __P((struct image_params *imgp)); 60static int NextByte __P((void *vp)); 61static int do_aout_hdr __P((struct imgact_gzip *)); 62static int Flush __P((void *vp, u_char *, u_long siz)); 63 64static int 65exec_gzip_imgact(imgp) 66 struct image_params *imgp; 67{ 68 int error, error2 = 0; 69 u_char *p = (u_char *) imgp->image_header; 70 struct imgact_gzip igz; 71 struct inflate infl; 72 73 /* If these four are not OK, it isn't a gzip file */ 74 if (p[0] != 0x1f) 75 return -1; /* 0 Simply magic */ 76 if (p[1] != 0x8b) 77 return -1; /* 1 Simply magic */ 78 if (p[2] != 0x08) 79 return -1; /* 2 Compression method */ 80 if (p[9] != 0x03) 81 return -1; /* 9 OS compressed on */ 82 83 /* 84 * If this one contains anything but a comment or a filename marker, 85 * we don't want to chew on it 86 */ 87 if (p[3] & ~(0x18)) 88 return ENOEXEC; /* 3 Flags */ 89 90 /* These are of no use to us */ 91 /* 4-7 Timestamp */ 92 /* 8 Extra flags */ 93 94 bzero(&igz, sizeof igz); 95 bzero(&infl, sizeof infl); 96 infl.gz_private = (void *) &igz; 97 infl.gz_input = NextByte; 98 infl.gz_output = Flush; 99 100 igz.ip = imgp; 101 igz.idx = 10; 102 103 if (p[3] & 0x08) { /* skip a filename */ 104 while (p[igz.idx++]) 105 if (igz.idx >= PAGE_SIZE) 106 return ENOEXEC; 107 } 108 if (p[3] & 0x10) { /* skip a comment */ 109 while (p[igz.idx++]) 110 if (igz.idx >= PAGE_SIZE) 111 return ENOEXEC; 112 } 113 igz.len = igz.ip->attr->va_size; 114 115 error = inflate(&infl); 116 117 if (igz.inbuf) { 118 error2 = 119 vm_map_remove(kernel_map, (vm_offset_t) igz.inbuf, 120 (vm_offset_t) igz.inbuf + PAGE_SIZE); 121 } 122 if (igz.error || error || error2) { 123 printf("Output=%lu ", igz.output); 124 printf("Inflate_error=%d igz.error=%d error2=%d where=%d\n", 125 error, igz.error, error2, igz.where); 126 } 127 if (igz.error) 128 return igz.error; 129 if (error) 130 return ENOEXEC; 131 if (error2) 132 return error2; 133 return 0; 134} 135 136static int 137do_aout_hdr(struct imgact_gzip * gz) 138{ 139 int error; 140 struct vmspace *vmspace = gz->ip->proc->p_vmspace; 141 vm_offset_t vmaddr; 142 143 /* 144 * Set file/virtual offset based on a.out variant. We do two cases: 145 * host byte order and network byte order (for NetBSD compatibility) 146 */ 147 switch ((int) (gz->a_out.a_magic & 0xffff)) { 148 case ZMAGIC: 149 gz->virtual_offset = 0; 150 if (gz->a_out.a_text) { 151 gz->file_offset = NBPG; 152 } else { 153 /* Bill's "screwball mode" */ 154 gz->file_offset = 0; 155 } 156 break; 157 case QMAGIC: 158 gz->virtual_offset = NBPG; 159 gz->file_offset = 0; 160 break; 161 default: 162 /* NetBSD compatibility */ 163 switch ((int) (ntohl(gz->a_out.a_magic) & 0xffff)) { 164 case ZMAGIC: 165 case QMAGIC: 166 gz->virtual_offset = NBPG; 167 gz->file_offset = 0; 168 break; 169 default: 170 gz->where = __LINE__; 171 return (-1); 172 } 173 } 174 175 gz->bss_size = roundup(gz->a_out.a_bss, NBPG); 176 177 /* 178 * Check various fields in header for validity/bounds. 179 */ 180 if ( /* entry point must lay with text region */ 181 gz->a_out.a_entry < gz->virtual_offset || 182 gz->a_out.a_entry >= gz->virtual_offset + gz->a_out.a_text || 183 184 /* text and data size must each be page rounded */ 185 gz->a_out.a_text % NBPG || 186 gz->a_out.a_data % NBPG) { 187 gz->where = __LINE__; 188 return (-1); 189 } 190 /* 191 * text/data/bss must not exceed limits 192 */ 193 if ( /* text can't exceed maximum text size */ 194 gz->a_out.a_text > MAXTSIZ || 195 196 /* data + bss can't exceed maximum data size */ 197 gz->a_out.a_data + gz->bss_size > MAXDSIZ || 198 199 /* data + bss can't exceed rlimit */ 200 gz->a_out.a_data + gz->bss_size > 201 gz->ip->proc->p_rlimit[RLIMIT_DATA].rlim_cur) { 202 gz->where = __LINE__; 203 return (ENOMEM); 204 } 205 /* Find out how far we should go */ 206 gz->file_end = gz->file_offset + gz->a_out.a_text + gz->a_out.a_data; 207 208 /* copy in arguments and/or environment from old process */ 209 error = exec_extract_strings(gz->ip); 210 if (error) { 211 gz->where = __LINE__; 212 return (error); 213 } 214 /* 215 * Destroy old process VM and create a new one (with a new stack) 216 */ 217 exec_new_vmspace(gz->ip); 218 219 vmaddr = gz->virtual_offset; 220 221 error = vm_mmap(&vmspace->vm_map, /* map */ 222 &vmaddr,/* address */ 223 gz->a_out.a_text, /* size */ 224 VM_PROT_READ | VM_PROT_EXECUTE | VM_PROT_WRITE, /* protection */ 225 VM_PROT_READ | VM_PROT_EXECUTE | VM_PROT_WRITE, 226 MAP_ANON | MAP_FIXED, /* flags */ 227 0, /* vnode */ 228 0); /* offset */ 229 230 if (error) { 231 gz->where = __LINE__; 232 return (error); 233 } 234 vmaddr = gz->virtual_offset + gz->a_out.a_text; 235 236 /* 237 * Map data read/write (if text is 0, assume text is in data area 238 * [Bill's screwball mode]) 239 */ 240 241 error = vm_mmap(&vmspace->vm_map, 242 &vmaddr, 243 gz->a_out.a_data, 244 VM_PROT_READ | VM_PROT_WRITE | (gz->a_out.a_text ? 0 : VM_PROT_EXECUTE), 245 VM_PROT_ALL, MAP_ANON | MAP_FIXED, 246 0, 247 0); 248 249 if (error) { 250 gz->where = __LINE__; 251 return (error); 252 } 253 if (gz->bss_size != 0) { 254 /* 255 * Allocate demand-zeroed area for uninitialized data. 256 * "bss" = 'block started by symbol' - named after the 257 * IBM 7090 instruction of the same name. 258 */ 259 vmaddr = gz->virtual_offset + gz->a_out.a_text + 260 gz->a_out.a_data; 261 error = vm_map_find(&vmspace->vm_map, NULL, 0, &vmaddr, 262 gz->bss_size, FALSE, VM_PROT_ALL, VM_PROT_ALL, 0); 263 if (error) { 264 gz->where = __LINE__; 265 return (error); 266 } 267 } 268 /* Fill in process VM information */ 269 vmspace->vm_tsize = gz->a_out.a_text >> PAGE_SHIFT; 270 vmspace->vm_dsize = (gz->a_out.a_data + gz->bss_size) >> PAGE_SHIFT; 271 vmspace->vm_taddr = (caddr_t) gz->virtual_offset; 272 vmspace->vm_daddr = (caddr_t) gz->virtual_offset + gz->a_out.a_text; 273 274 /* Fill in image_params */ 275 gz->ip->interpreted = 0; 276 gz->ip->entry_addr = gz->a_out.a_entry; 277 278 gz->ip->proc->p_sysent = &aout_sysvec; 279 280 return 0; 281} 282 283static int 284NextByte(void *vp) 285{ 286 int error; 287 struct imgact_gzip *igz = (struct imgact_gzip *) vp; 288 289 if (igz->idx >= igz->len) { 290 igz->where = __LINE__; 291 return GZ_EOF; 292 } 293 if (igz->inbuf && igz->idx < (igz->offset + PAGE_SIZE)) { 294 return igz->inbuf[(igz->idx++) - igz->offset]; 295 } 296 if (igz->inbuf) { 297 error = vm_map_remove(kernel_map, (vm_offset_t) igz->inbuf, 298 (vm_offset_t) igz->inbuf + PAGE_SIZE); 299 if (error) { 300 igz->where = __LINE__; 301 igz->error = error; 302 return GZ_EOF; 303 } 304 } 305 igz->offset = igz->idx & ~PAGE_MASK; 306 307 error = vm_mmap(kernel_map, /* map */ 308 (vm_offset_t *) & igz->inbuf, /* address */ 309 PAGE_SIZE, /* size */ 310 VM_PROT_READ, /* protection */ 311 VM_PROT_READ, /* max protection */ 312 0, /* flags */ 313 (caddr_t) igz->ip->vp, /* vnode */ 314 igz->offset); /* offset */ 315 if (error) { 316 igz->where = __LINE__; 317 igz->error = error; 318 return GZ_EOF; 319 } 320 return igz->inbuf[(igz->idx++) - igz->offset]; 321} 322 323static int 324Flush(void *vp, u_char * ptr, u_long siz) 325{ 326 struct imgact_gzip *gz = (struct imgact_gzip *) vp; 327 u_char *p = ptr, *q; 328 int i; 329 330 /* First, find a a.out-header */ 331 if (gz->output < sizeof gz->a_out) { 332 q = (u_char *) & gz->a_out; 333 i = min(siz, sizeof gz->a_out - gz->output); 334 bcopy(p, q + gz->output, i); 335 gz->output += i; 336 p += i; 337 siz -= i; 338 if (gz->output == sizeof gz->a_out) { 339 i = do_aout_hdr(gz); 340 if (i == -1) { 341 if (!gz->where) 342 gz->where = __LINE__; 343 gz->error = ENOEXEC; 344 return ENOEXEC; 345 } else if (i) { 346 gz->where = __LINE__; 347 gz->error = i; 348 return ENOEXEC; 349 } 350 if (gz->file_offset < sizeof gz->a_out) { 351 q = (u_char *) gz->virtual_offset + gz->output - gz->file_offset; 352 bcopy(&gz->a_out, q, sizeof gz->a_out - gz->file_offset); 353 } 354 } 355 } 356 /* Skip over zero-padded first PAGE if needed */ 357 if (gz->output < gz->file_offset && (gz->output + siz) > gz->file_offset) { 358 i = min(siz, gz->file_offset - gz->output); 359 gz->output += i; 360 p += i; 361 siz -= i; 362 } 363 if (gz->output >= gz->file_offset && gz->output < gz->file_end) { 364 i = min(siz, gz->file_end - gz->output); 365 q = (u_char *) gz->virtual_offset + gz->output - gz->file_offset; 366 bcopy(p, q, i); 367 gz->output += i; 368 p += i; 369 siz -= i; 370 } 371 gz->output += siz; 372 return 0; 373} 374 375 376/* 377 * Tell kern_execve.c about it, with a little help from the linker. 378 * Since `const' objects end up in the text segment, TEXT_SET is the 379 * correct directive to use. 380 */ 381 382static const struct execsw gzip_execsw = {exec_gzip_imgact, "gzip"}; 383TEXT_SET(execsw_set, gzip_execsw); 384