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