/* $NetBSD$ */ /*- * Copyright (c) 2015 Taylor R. Campbell * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __KERNEL_RCSID(0, "$NetBSD$"); #include #include #include #include #include #include #include const size_t pb_copyin_limit = 0x10000; static const char proplib_prefix[] = "\n" "\n" "\n"; int pb_copyin_init(struct plistref *pref, struct pb_msg msg) { int error; pb_init(msg); error = pb_copyin(pref, msg); if (error) pb_destroy(msg); return error; } int pb_copyin_init_prop(struct plistref *pref, struct pb_msg msg, const struct pb_prop_msgdesc *prop) { int error; pb_init(msg); error = pb_copyin_prop(pref, msg, prop); if (error) pb_destroy(msg); return error; } int pb_copyin(struct plistref *pref, struct pb_msg msg) { unsigned char *buf; int error; if (pref->pref_len > pb_copyin_limit) return E2BIG; /* XXX What error code? */ if (pref->pref_len < PB_IOCTL_HDRSIZE) return EINVAL; /* XXX What error code? */ buf = kmem_alloc(pref->pref_len, KM_SLEEP); error = copyin(pref->pref_plist, buf, pref->pref_len); if (error) goto out; if (memcmp(buf, PB_IOCTL_HDR_REQ, PB_IOCTL_HDRSIZE) != 0) { /* XXX Fall back to parsing a plist. */ error = EINVAL; /* XXX What error code? */ goto out; } error = pb_decode_from_memory(msg, buf + PB_IOCTL_HDRSIZE, pref->pref_len - PB_IOCTL_HDRSIZE); if (error) goto out; /* Success! */ error = 0; out: kmem_free(buf, pref->pref_len); return error; } int pb_copyin_prop(struct plistref *pref, struct pb_msg msg, const struct pb_prop_msgdesc *prop) { void *buf; const struct pb_prop_msgdesc *prop0; prop_object_t obj; int error; if (pref->pref_len > pb_copyin_limit) return E2BIG; /* XXX */ if (pref->pref_len < strlen(proplib_prefix)) return EINVAL; /* XXX */ buf = kmem_alloc(pref->pref_len + 1, KM_SLEEP); error = copyin(pref->pref_plist, buf, pref->pref_len); if (error) goto out; if (memcmp(buf, proplib_prefix, strlen(proplib_prefix)) != 0) { error = EINVAL; goto out; } buf[pref->pref_len] = '\0'; prop0 = prop; retry: switch (prop0->pbpm_t) { case PB_PROP_MSG_ARRAY: obj = prop_array_internalize(buf); break; case PB_PROP_MSG_RECORD: obj = prop_dictionary_internalize(buf); break; case PB_PROP_MSG_SPLICE: prop0 = prop0->pbpm_u.splice.pbps_msgdesc; goto retry; default: panic("invalid protobuf proplib descriptor: %d", (int)prop0->pbpm_t); } if (obj == NULL) { error = EIO; goto out; } out: kmem_free(buf, pref->pref_len + 1); if (error) return error; return pb_decode_prop(msg, prop, obj); } int pb_copyout_destroy(struct plistref *pref, struct pb_msg msg) { int error; error = pb_copyout(pref, msg); pb_destroy(msg); return error; } int pb_copyout_destroy_prop(struct plistref *pref, struct pb_msg msg, const struct pb_prop_msgdesc *prop) { int error; error = pb_copyout_prop(pref, msg, prop); pb_destroy(msg); return error; } int pb_copyout(struct plistref *pref, struct pb_msg msg) { unsigned char hdr[PB_IOCTL_HDRSIZE]; size_t size, buflen; void *buf; int error; error = pb_encoding_size(msg, &size); if (error) return error; if (size > SIZE_MAX - PB_IOCTL_HDRSIZE) return ENOMEM; /* XXX Generate a plist if the input was a plist. */ /* * If the user's buffer is too small, report truncation and the * necessary size. */ if (pref->pref_len < size) { unsigned char hdr[PB_IOCTL_HDRSIZE]; uint64_t s64 = size; CTASSERT(PB_IOCTL_HDR_TRUNCSIZE + sizeof(s64) == PB_IOCTL_HDRSIZE); (void)memcpy(hdr, PB_IOCTL_HDR_TRUNC, PB_IOCTL_HDR_TRUNCSIZE); (void)memcpy(hdr + PB_IOCTL_HDR_TRUNCSIZE, &s64, sizeof(s64)); if (pref->pref_len < PB_IOCTL_HDRSIZE) return EINVAL; return copyout(hdr, pref->pref_buf, PB_IOCTL_HDRSIZE); } /* * Otherwise, encode the message to a buffer in memory, * prefixed by a response header. */ buflen = PB_IOCTL_HDRSIZE + size; buf = kmem_alloc(buflen, KM_SLEEP); (void)memcpy(buf, PB_IOCTL_HDR_RES, PB_IOCTL_HDRSIZE); error = pb_encode_to_memory(msg, buf + PB_IOCTL_HDRSIZE, size); if (error) goto out; error = copyout(buf, pref->pref_buf, buflen); if (error) goto out; out: kmem_free(buf, buflen); return error; } int pb_copyout_prop(struct plistref *pref, struct pb_msg msg, const struct pb_prop_msgdesc *prop) { prop_object_t obj = NULL; void *buf = NULL; size_t size, rsize; void *uaddr = NULL; int error; /* Encode the message as a proplib object. */ error = pb_encode_prop(msg, prop, &obj); if (error) goto out; /* Externalize the proplib object. */ switch (prop_object_type(obj)) { case PROP_TYPE_ARRAY: buf = prop_array_externalize(obj); break; case PROP_TYPE_DICTIONARY: buf = prop_dictionary_externalize(obj); break; default: panic("pb_encode_prop returned non-array, non-dict: %d", (int)prop_object_type(obj)); } if (buf == NULL) { error = ENOMEM; goto out; } /* Allocate memory in the userland process. */ size = strlen(buf) + 1; rsize = round_page(size); error = uvm_mmap_anon(curproc, &uaddr, rsize); if (error) goto out; /* Copy the XML to the userland process. */ error = copyout(buf, uaddr, size); if (error) goto out; /* Success! */ uaddr = NULL; error = 0; out: if (uaddr) { /* XXX Copypasta from sys_munmap. */ const vaddr_t vaddr = (vaddr_t)uaddr; struct vm_map_entry *dead_entries = NULL; vm_map_lock(&curproc->p_vmspace->vm_map); uvm_unmap_remove(&curproc->p_vmspace->vm_map, vaddr, vaddr + rsize, &dead_entries, 0); vm_map_unlock(&curproc->p_vmspace->vm_map); if (dead_entries != NULL) uvm_unmap_detach(dead_entries, 0); } if (buf) free(buf, M_TEMP); if (obj) prop_object_release(obj); return error; }