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