/* $NetBSD: prop_kern.c,v 1.30 2025/05/10 18:58:53 thorpej Exp $ */ /*- * Copyright (c) 2006, 2009, 2025 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jason R. Thorpe. * * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ /* This file is only for native NetBSD. No build tools should use it. */ #if defined(__NetBSD__) && !defined(HAVE_NBTOOL_CONFIG_H) #include #include #include #if !defined(_KERNEL) && !defined(_STANDALONE) #include #include #include #include #include #ifdef RUMP_ACTION #include #define ioctl(a,b,c) rump_sys_ioctl(a,b,c) #endif #include "prop_object_impl.h" /* for _PROP_EXPORT */ /* * prop_object_externalize_to_pref -- * Externalize an object into a plistref for sending to the kernel. */ static int _prop_object_externalize_to_pref(prop_object_t obj, struct plistref *pref, char **bufp) { char *buf = prop_object_externalize(obj); if (buf == NULL) { /* Assume we ran out of memory. */ return (ENOMEM); } pref->pref_plist = buf; pref->pref_len = strlen(buf) + 1; if (bufp != NULL) { *bufp = buf; } return (0); } static bool prop_object_externalize_to_pref(prop_object_t obj, struct plistref *prefp) { int rv = _prop_object_externalize_to_pref(obj, prefp, NULL); if (rv != 0) errno = rv; /* pass up error value in errno */ return (rv == 0); } _PROP_EXPORT bool prop_array_externalize_to_pref(prop_array_t array, struct plistref *prefp) { return prop_object_externalize_to_pref(array, prefp); } __strong_alias(prop_dictionary_externalize_to_pref, prop_array_externalize_to_pref) _PROP_EXPORT int prop_object_send_syscall(prop_object_t obj, struct plistref *prefp) { if (prop_object_externalize_to_pref(obj, prefp)) return 0; else return errno; } __strong_alias(prop_array_send_syscall, prop_object_send_syscall) __strong_alias(prop_dictionary_send_syscall, prop_object_send_syscall) /* * prop_object_send_ioctl -- * Send an array to the kernel using the specified ioctl. */ static int _prop_object_send_ioctl(prop_object_t obj, int fd, unsigned long cmd) { struct plistref pref; char *buf; int error; error = _prop_object_externalize_to_pref(obj, &pref, &buf); if (error) return (error); if (ioctl(fd, cmd, &pref) == -1) error = errno; else error = 0; free(buf); return (error); } _PROP_EXPORT int prop_object_send_ioctl(prop_object_t obj, int fd, unsigned long cmd) { int rv; rv = _prop_object_send_ioctl(obj, fd, cmd); if (rv != 0) { errno = rv; /* pass up error value in errno */ return rv; } else return 0; } __strong_alias(prop_array_send_ioctl, prop_object_send_ioctl) __strong_alias(prop_dictionary_send_ioctl, prop_object_send_ioctl) /* * prop_object_internalize_from_pref -- * Internalize a pref into an object. */ static int _prop_object_internalize_from_pref_with_type(const struct plistref *pref, prop_object_t *objp, prop_type_t type) { prop_object_t obj = NULL; char *buf; int error = 0; if (pref->pref_len == 0) { /* * This should never happen; we should always get the XML * for an empty dictionary if it's really empty. */ error = EIO; goto out; } else { buf = pref->pref_plist; buf[pref->pref_len - 1] = '\0'; /* extra insurance */ obj = prop_object_internalize(buf); (void) munmap(buf, pref->pref_len); if (obj != NULL && type != PROP_TYPE_UNKNOWN && prop_object_type(obj) != type) { prop_object_release(obj); obj = NULL; } if (obj == NULL) error = EIO; } out: if (error == 0) *objp = obj; return (error); } static bool _prop_object_internalize_from_pref(const struct plistref *prefp, prop_object_t *objp, prop_type_t type) { int rv; rv = _prop_object_internalize_from_pref_with_type(prefp, objp, type); if (rv != 0) errno = rv; /* pass up error value in errno */ return (rv == 0); } _PROP_EXPORT bool prop_array_internalize_from_pref(const struct plistref *prefp, prop_array_t *arrayp) { return _prop_object_internalize_from_pref(prefp, (prop_object_t *)arrayp, PROP_TYPE_ARRAY); } _PROP_EXPORT bool prop_dictionary_internalize_from_pref(const struct plistref *prefp, prop_dictionary_t *dictp) { return _prop_object_internalize_from_pref(prefp, (prop_object_t *)dictp, PROP_TYPE_DICTIONARY); } static int _prop_object_recv_syscall(const struct plistref *prefp, prop_object_t *objp, prop_type_t type) { if (_prop_object_internalize_from_pref_with_type(prefp, objp, type)) { return 0; } else { return errno; } } _PROP_EXPORT int prop_object_recv_syscall(const struct plistref *prefp, prop_object_t *objp) { return _prop_object_recv_syscall(prefp, objp, PROP_TYPE_UNKNOWN); } _PROP_EXPORT int prop_array_recv_syscall(const struct plistref *prefp, prop_array_t *arrayp) { return _prop_object_recv_syscall(prefp, (prop_object_t *)arrayp, PROP_TYPE_ARRAY); } _PROP_EXPORT int prop_dictionary_recv_syscall(const struct plistref *prefp, prop_dictionary_t *dictp) { return _prop_object_recv_syscall(prefp, (prop_object_t *)dictp, PROP_TYPE_DICTIONARY); } /* * prop_object_recv_ioctl -- * Receive an array from the kernel using the specified ioctl. */ static int _prop_object_recv_ioctl(int fd, unsigned long cmd, prop_object_t *objp, prop_type_t type) { int rv; struct plistref pref; rv = ioctl(fd, cmd, &pref); if (rv == -1) return errno; rv = _prop_object_internalize_from_pref_with_type(&pref, objp, type); if (rv != 0) { errno = rv; /* pass up error value in errno */ return rv; } else return 0; } _PROP_EXPORT int prop_object_recv_ioctl(int fd, unsigned long cmd, prop_object_t *objp) { return _prop_object_recv_ioctl(fd, cmd, objp, PROP_TYPE_UNKNOWN); } _PROP_EXPORT int prop_array_recv_ioctl(int fd, unsigned long cmd, prop_array_t *arrayp) { return _prop_object_recv_ioctl(fd, cmd, (prop_object_t *)arrayp, PROP_TYPE_ARRAY); } _PROP_EXPORT int prop_dictionary_recv_ioctl(int fd, unsigned long cmd, prop_dictionary_t *dictp) { return _prop_object_recv_ioctl(fd, cmd, (prop_object_t *)dictp, PROP_TYPE_DICTIONARY); } /* * prop_object_sendrecv_ioctl -- * Combination send/receive an object to/from the kernel using * the specified ioctl. */ static int _prop_object_sendrecv_ioctl(prop_object_t obj, int fd, unsigned long cmd, prop_object_t *objp, prop_type_t type) { struct plistref pref; char *buf; int error; error = _prop_object_externalize_to_pref(obj, &pref, &buf); if (error != 0) { errno = error; return error; } if (ioctl(fd, cmd, &pref) == -1) error = errno; else error = 0; free(buf); if (error != 0) return error; error = _prop_object_internalize_from_pref_with_type(&pref, objp, type); if (error != 0) { errno = error; /* pass up error value in errno */ return error; } else return 0; } _PROP_EXPORT int prop_object_sendrecv_ioctl(prop_object_t obj, int fd, unsigned long cmd, prop_object_t *objp) { return _prop_object_sendrecv_ioctl(obj, fd, cmd, objp, PROP_TYPE_UNKNOWN); } _PROP_EXPORT int prop_dictionary_sendrecv_ioctl(prop_dictionary_t dict, int fd, unsigned long cmd, prop_dictionary_t *dictp) { return _prop_object_sendrecv_ioctl(dict, fd, cmd, (prop_object_t *)dictp, PROP_TYPE_DICTIONARY); } #endif /* !_KERNEL && !_STANDALONE */ #if defined(_KERNEL) #include #include #include #include #include #include #include #include #include #include "prop_object_impl.h" /* Arbitrary limit ioctl input to 128KB */ unsigned int prop_object_copyin_limit = 128 * 1024; /* initialize proplib for use in the kernel */ void prop_kern_init(void) { __link_set_decl(prop_linkpools, struct prop_pool_init); struct prop_pool_init * const *pi; __link_set_foreach(pi, prop_linkpools) pool_init((*pi)->pp, (*pi)->size, 0, 0, 0, (*pi)->wchan, &pool_allocator_nointr, IPL_NONE); } static int _prop_object_copyin_size(const struct plistref *pref, prop_object_t *objp, size_t lim, prop_type_t type) { prop_object_t obj = NULL; char *buf; int error; if (pref->pref_len >= lim) return E2BIG; /* * Allocate an extra byte so we can guarantee NUL-termination. */ buf = malloc(pref->pref_len + 1, M_TEMP, M_WAITOK); if (buf == NULL) return (ENOMEM); error = copyin(pref->pref_plist, buf, pref->pref_len); if (error) { free(buf, M_TEMP); return (error); } buf[pref->pref_len] = '\0'; obj = prop_object_internalize(buf); if (obj != NULL && type != PROP_TYPE_UNKNOWN && prop_object_type(obj) != type) { prop_object_release(obj); obj = NULL; } free(buf, M_TEMP); if (obj == NULL) { error = EIO; } else { *objp = obj; } return (error); } int prop_object_copyin_size(const struct plistref *pref, prop_object_t *objp, size_t lim) { return _prop_object_copyin_size(pref, objp, lim, PROP_TYPE_UNKNOWN); } int prop_array_copyin_size(const struct plistref *pref, prop_array_t *arrayp, size_t lim) { return _prop_object_copyin_size(pref, (prop_object_t *)arrayp, lim, PROP_TYPE_ARRAY); } int prop_dictionary_copyin_size(const struct plistref *pref, prop_dictionary_t *dictp, size_t lim) { return _prop_object_copyin_size(pref, (prop_object_t *)dictp, lim, PROP_TYPE_DICTIONARY); } int prop_object_copyin(const struct plistref *pref, prop_object_t *objp) { return _prop_object_copyin_size(pref, objp, prop_object_copyin_limit, PROP_TYPE_UNKNOWN); } int prop_array_copyin(const struct plistref *pref, prop_array_t *arrayp) { return _prop_object_copyin_size(pref, (prop_object_t *)arrayp, prop_object_copyin_limit, PROP_TYPE_ARRAY); } int prop_dictionary_copyin(const struct plistref *pref, prop_dictionary_t *dictp) { return _prop_object_copyin_size(pref, (prop_object_t *)dictp, prop_object_copyin_limit, PROP_TYPE_DICTIONARY); } static int _prop_object_copyin_ioctl_size(const struct plistref *pref, const u_long cmd, prop_object_t *objp, size_t lim, prop_type_t type) { if ((cmd & IOC_IN) == 0) return (EFAULT); return _prop_object_copyin_size(pref, objp, lim, type); } int prop_object_copyin_ioctl_size(const struct plistref *pref, const u_long cmd, prop_object_t *objp, size_t lim) { return _prop_object_copyin_ioctl_size(pref, cmd, objp, lim, PROP_TYPE_UNKNOWN); } int prop_array_copyin_ioctl_size(const struct plistref *pref, const u_long cmd, prop_array_t *arrayp, size_t lim) { return _prop_object_copyin_ioctl_size(pref, cmd, (prop_object_t *)arrayp, lim, PROP_TYPE_ARRAY); } int prop_dictionary_copyin_ioctl_size(const struct plistref *pref, const u_long cmd, prop_dictionary_t *dictp, size_t lim) { return _prop_object_copyin_ioctl_size(pref, cmd, (prop_object_t *)dictp, lim, PROP_TYPE_DICTIONARY); } int prop_object_copyin_ioctl(const struct plistref *pref, const u_long cmd, prop_object_t *objp) { return _prop_object_copyin_ioctl_size(pref, cmd, objp, prop_object_copyin_limit, PROP_TYPE_UNKNOWN); } int prop_array_copyin_ioctl(const struct plistref *pref, const u_long cmd, prop_array_t *arrayp) { return _prop_object_copyin_ioctl_size(pref, cmd, (prop_object_t *)arrayp, prop_object_copyin_limit, PROP_TYPE_ARRAY); } int prop_dictionary_copyin_ioctl(const struct plistref *pref, const u_long cmd, prop_dictionary_t *dictp) { return _prop_object_copyin_ioctl_size(pref, cmd, (prop_object_t *)dictp, prop_object_copyin_limit, PROP_TYPE_DICTIONARY); } int prop_object_copyout(struct plistref *pref, prop_object_t obj) { struct lwp *l = curlwp; /* XXX */ struct proc *p = l->l_proc; char *buf; void *uaddr; size_t len, rlen; int error = 0; buf = prop_object_externalize(obj); if (buf == NULL) return (ENOMEM); len = strlen(buf) + 1; rlen = round_page(len); uaddr = NULL; error = uvm_mmap_anon(p, &uaddr, rlen); if (error == 0) { error = copyout(buf, uaddr, len); if (error == 0) { pref->pref_plist = uaddr; pref->pref_len = len; } } free(buf, M_TEMP); return (error); } __strong_alias(prop_array_copyout, prop_object_copyout) __strong_alias(prop_dictionary_copyout, prop_object_copyout) int prop_object_copyout_ioctl(struct plistref *pref, const u_long cmd, prop_object_t obj) { if ((cmd & IOC_OUT) == 0) return (EFAULT); return prop_object_copyout(pref, obj); } __strong_alias(prop_array_copyout_ioctl, prop_object_copyout_ioctl) __strong_alias(prop_dictionary_copyout_ioctl, prop_object_copyout_ioctl) #endif /* _KERNEL */ #endif /* __NetBSD__ && !HAVE_NBTOOL_CONFIG_H */