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