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