X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/a3d08fcd5120d2aa8303b6349ca8b14e3f284af3..91447636331957f3d9b5ca5b508f07c526b0074d:/bsd/kern/kern_panicinfo.c diff --git a/bsd/kern/kern_panicinfo.c b/bsd/kern/kern_panicinfo.c index 83f753872..9ad8549f1 100644 --- a/bsd/kern/kern_panicinfo.c +++ b/bsd/kern/kern_panicinfo.c @@ -23,210 +23,159 @@ #include #include #include -#include #include -#include #include #include #include +#include +#include #include +#include +#include -/* prototypes not exported by osfmk. */ -extern void kmem_free(vm_map_t, vm_offset_t, vm_size_t); -extern kern_return_t kmem_alloc_wired(vm_map_t, vm_offset_t *, vm_size_t); +/* prototypes not exported by osfmk/console. */ +extern void panic_dialog_test( void ); +extern int panic_dialog_set_image( const unsigned char * ptr, unsigned int size ); +extern void panic_dialog_get_image( unsigned char ** ptr, unsigned int * size ); -/* Globals */ -static off_t imagesizelimit = (4 * 4096); +/* make the compiler happy */ +extern int sysctl_dopanicinfo(int *, u_int, user_addr_t, size_t *, user_addr_t, size_t, struct proc *); -/* Information about the current panic image */ -static int image_bits = 32; /* Bitdepth */ -static char *image_pathname = NULL; /* path to it */ -static size_t image_pathlen = 0; /* and the length of the pathname */ +#define PANIC_IMAGE_SIZE_LIMIT (32 * 4096) /* 128K - Maximum amount of memory consumed for the panic UI */ +#define KERN_PANICINFO_TEST (KERN_PANICINFO_IMAGE+2) /* Allow the panic UI to be tested by root without causing a panic */ -static vm_offset_t image_ptr = NULL; /* the image itself */ -static off_t image_size = 0; /* and the imagesize */ - - -__private_extern__ void -get_panicimage(vm_offset_t *imageptr, vm_size_t *imagesize, int *imagebits) -{ - *imageptr = image_ptr; - *imagesize = image_size; - *imagebits = image_bits; -} - -static int -panicimage_from_file( - char *imname, - off_t sizelimit, - vm_offset_t *image, - off_t *filesize, - struct proc *p) -{ - int error = 0; - int error1 = 0; - int aresid; - struct nameidata nd; - struct vattr vattr; - struct vnode * vp; - kern_return_t kret; - struct pcred *pcred = p->p_cred; - struct ucred *cred = pcred->pc_ucred; - vm_offset_t iobuf; - - /* Open the file */ - NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, imname, p); - error = vn_open(&nd, FREAD, S_IRUSR); - if (error) - return (error); - vp = nd.ni_vp; - - if (vp->v_type != VREG) { - error = EFAULT; - goto out; - } - - /* get the file size */ - error = VOP_GETATTR(vp, &vattr, cred, p); - if (error) - goto out; - - /* validate the file size */ - if (vattr.va_size > sizelimit) { - error = EFBIG; - goto out; - } - - /* allocate kernel wired memory */ - kret = kmem_alloc_wired(kernel_map, &iobuf, - (vm_size_t)vattr.va_size); - if (kret != KERN_SUCCESS) { - switch (kret) { - default: - error = EINVAL; - break; - case KERN_NO_SPACE: - case KERN_RESOURCE_SHORTAGE: - error = ENOMEM; - break; - case KERN_PROTECTION_FAILURE: - error = EPERM; - break; - } - goto out; - } - - /* read the file in the kernel buffer */ - error = vn_rdwr(UIO_READ, vp, (caddr_t)iobuf, (int)vattr.va_size, - (off_t)0, UIO_SYSSPACE, IO_NODELOCKED|IO_UNIT, - cred, &aresid, p); - if (error) { - (void)kmem_free(kernel_map, iobuf, (vm_size_t)vattr.va_size); - goto out; - } - - /* - * return the image to the caller - * freeing this memory is callers responsibility - */ - *image = iobuf; - *filesize = (off_t)vattr.va_size; - -out: - VOP_UNLOCK(vp, 0, p); - error1 = vn_close(vp, FREAD, cred, p); - if (error == 0) - error = error1; - return (error); -} +/* Local data */ +static int image_size_limit = PANIC_IMAGE_SIZE_LIMIT; __private_extern__ int sysctl_dopanicinfo(name, namelen, oldp, oldlenp, newp, newlen, p) int *name; u_int namelen; - void *oldp; + user_addr_t oldp; size_t *oldlenp; - void *newp; + user_addr_t newp; size_t newlen; struct proc *p; { int error = 0; - int bitdepth = 32; /* default is 32 bits */ - char *imname; + vm_offset_t newimage = (vm_offset_t )NULL; + kern_return_t kret; + unsigned char * prev_image_ptr; + unsigned int prev_image_size; + /* all sysctl names at this level are terminal */ if (namelen != 1) return (ENOTDIR); /* overloaded */ + if ( (error = proc_suser(p)) ) /* must be super user to muck with image */ + return (error); + switch (name[0]) { default: - return (EOPNOTSUPP); + return (ENOTSUP); + + case KERN_PANICINFO_TEST: + + panic_dialog_test(); + return (0); + case KERN_PANICINFO_MAXSIZE: - if (newp != NULL && (error = suser(p->p_ucred, &p->p_acflag))) - return (error); - error = sysctl_quad(oldp, oldlenp, newp, newlen, &imagesizelimit); + + /* return the image size limits */ + + newlen = 0; + newp = USER_ADDR_NULL; + + error = sysctl_int(oldp, oldlenp, newp, newlen, &image_size_limit); + return (error); - case KERN_PANICINFO_IMAGE16: - bitdepth = 16; - /* and fall through */ - case KERN_PANICINFO_IMAGE32: - /* allocate a buffer for the image pathname */ - MALLOC_ZONE(imname, char *, MAXPATHLEN, M_NAMEI, M_WAITOK); - - if (!newp) { - bcopy(image_pathname, imname, image_pathlen); - imname[image_pathlen] = '\0'; - } else - imname[0] = '\0'; - error = sysctl_string(oldp, oldlenp, newp, newlen, - imname, MAXPATHLEN); - if (newp && !error) { - char *tmpstr, *oldstr; - off_t filesize = 0; - size_t len; - vm_offset_t image; - vm_offset_t oimage = NULL; - vm_size_t osize = 0; /* covariable: quiet compiler */ - - len = strlen(imname); - oldstr = image_pathname; - - error = panicimage_from_file(imname, imagesizelimit, - &image, &filesize, p); - if (error) + case KERN_PANICINFO_IMAGE: + + /* If we have a new image, allocate wired kernel memory and copy it in from user space */ + if ( newp != USER_ADDR_NULL ) { + + /* check the length of the incoming image before allocating space for it. */ + if ( newlen > (size_t)image_size_limit ) + return (ENOMEM); + + /* allocate some kernel wired memory for the new image */ + kret = kmem_alloc(kernel_map, &newimage, (vm_size_t)round_page_32(newlen)); + + if (kret != KERN_SUCCESS) { + switch (kret) { + default: + error = EINVAL; + break; + case KERN_NO_SPACE: + case KERN_RESOURCE_SHORTAGE: + error = ENOMEM; + break; + case KERN_PROTECTION_FAILURE: + error = EPERM; + break; + } + + return (error); + } + + /* copy the image in from user space */ + if ( (error = copyin(newp, (char *) newimage, newlen)) ) goto errout; - /* release the old image */ - if (image_ptr) { - oimage = image_ptr; - osize = image_size; + } else { /* setup to make the default image active */ + + newimage = (vm_offset_t )NULL; + newlen = 0; + } + + /* get the current image location and size */ + panic_dialog_get_image( &prev_image_ptr, &prev_image_size ); + + /* did the caller request a copy of the previous image ? */ + if ( oldp != USER_ADDR_NULL ) { + if ( *oldlenp < prev_image_size ) { + error = ERANGE; + goto errout; } - /* remember the new one */ - image_ptr = image; - image_bits = bitdepth; /* new bith depth */ - image_size = filesize; /* new imagesize */ + /* copy the image to user space or zero the size if the default image is active */ + if ( prev_image_ptr != NULL ) { + if ( (error = copyout( prev_image_ptr, oldp, prev_image_size )) ) + goto errout; - if (oimage) - kmem_free(kernel_map, oimage, osize); + *oldlenp = prev_image_size; + } + else /* tell the user that the default image is active */ + *oldlenp = 0; + } - /* save the new name */ - MALLOC(tmpstr, char *, len+1, M_TEMP, M_WAITOK); - bcopy(imname, tmpstr, len); - tmpstr[len] = '\0'; + /* Make the new image active, or reactivate the default image. + But, handle the special case of asking for the current image + without changing the current image. + */ - image_pathname = tmpstr; /* new pathname */ - image_pathlen = len; /* new pathname length */ + if ( !(oldp && newp == USER_ADDR_NULL) ) { + if ( (error = panic_dialog_set_image( (unsigned char *) newimage, newlen )) ) + goto errout; - /* free the old name */ - FREE(oldstr, M_TEMP); + /* free the wired memory used by the previous image */ + if ( prev_image_ptr != NULL ) { + (void)kmem_free(kernel_map, (vm_offset_t) prev_image_ptr, (vm_size_t)round_page_32(prev_image_size)); + printf("Panic UI memory freed (%d)\n", round_page_32(prev_image_size)); + } } + + return (0); + errout: - FREE_ZONE(imname, MAXPATHLEN, M_NAMEI); + if ( newimage != (vm_offset_t )NULL ) + (void)kmem_free(kernel_map, newimage, (vm_size_t)round_page_32(newlen)); + return (error); } }