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