123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- /*
- * Copyright (c) 2023 Agustina Arzille.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
- #include <iovec.h>
- #include <kern/ipc.h>
- #include <kern/unwind.h>
- #include <kern/user.h>
- static int
- user_copy_impl (char *dst, const char *src, size_t size)
- {
- struct unw_fixup fixup;
- int error = unw_fixup_save (&fixup);
- if (unlikely (error))
- return (error);
- memcpy (dst, src, size);
- return (0);
- }
- int
- user_copy_to (void *udst, const void *src, size_t size)
- {
- return (user_check_range (udst, size) ?
- user_copy_impl (udst, src, size) : EFAULT);
- }
- int
- user_copy_from (void *dst, const void *usrc, size_t size)
- {
- return (user_check_range (usrc, size) ?
- user_copy_impl (dst, usrc, size) : EFAULT);
- }
- static bool
- user_check_iov_iter (struct ipc_iov_iter *iov)
- {
- return (iov->begin == &iov->head ||
- user_check_range (iov->begin, iov->end * sizeof (*iov->begin)));
- }
- static ssize_t
- user_copyv_impl (struct ipc_iov_iter *dst,
- struct ipc_iov_iter *src, int to_user)
- {
- struct unw_fixup fixup;
- ssize_t ret = unw_fixup_save (&fixup);
- if (unlikely (ret))
- return (-ret);
- else if (!user_check_iov_iter (to_user ? dst : src))
- return (-EFAULT);
- ssize_t *err1 = to_user ? &ret : NULL;
- ssize_t *err2 = to_user ? NULL : &ret;
- while (1)
- {
- struct iovec *dv = ipc_iov_iter_usrnext (dst, err1);
- if (! dv)
- return (ret);
- struct iovec *sv = ipc_iov_iter_usrnext (src, err2);
- if (! sv)
- return (ret);
- size_t nbytes = MIN (dv->iov_len, sv->iov_len);
- if (unlikely ((ret += nbytes) < 0))
- return (-EOVERFLOW);
- memcpy (dv->iov_base, sv->iov_base, nbytes);
- iovec_adv (dv, nbytes);
- iovec_adv (sv, nbytes);
- }
- }
- ssize_t
- user_copyv_to (struct ipc_iov_iter *udst, struct ipc_iov_iter *src)
- {
- return (user_copyv_impl (udst, src, 1));
- }
- ssize_t
- user_copyv_from (struct ipc_iov_iter *dst, struct ipc_iov_iter *usrc)
- {
- return (user_copyv_impl (dst, usrc, 0));
- }
- bool
- user_check_struct (const void *uptr, size_t min_size)
- {
- if (!user_check_range (uptr, sizeof (uint32_t)))
- return (false);
- struct unw_fixup fixup;
- int error = unw_fixup_save (&fixup);
- if (unlikely (error))
- return (false);
- uint32_t rsize = ((const union user_ua *)uptr)->u4;
- return (rsize >= min_size && user_check_range (uptr, rsize));
- }
- int
- user_read_struct (void *dst, const void *usrc, size_t size)
- {
- if (!user_check_range (usrc, sizeof (uint32_t)))
- return (EFAULT);
- struct unw_fixup fixup;
- int error = unw_fixup_save (&fixup);
- if (unlikely (error))
- return (error);
- size_t rsize = ((const union user_ua *)usrc)->u4;
- if (size < rsize)
- rsize = size;
- if (!user_check_range (usrc, rsize))
- return (EFAULT);
- memcpy (dst, usrc, rsize);
- return (0);
- }
- int
- user_write_struct (void *udst, const void *src, size_t size)
- {
- if (!user_check_range (udst, sizeof (uint32_t)))
- return (EFAULT);
- struct unw_fixup fixup;
- int error = unw_fixup_save (&fixup);
- if (unlikely (error))
- return (error);
- size_t rsize = ((const union user_ua *)udst)->u4;
- if (rsize < sizeof (uint32_t))
- return (EFAULT);
- else if (size < rsize)
- rsize = size;
- if (!user_check_range (udst, rsize))
- return (EFAULT);
- memcpy ((char *)udst + sizeof (uint32_t),
- (const char *)src + sizeof (uint32_t),
- rsize - sizeof (uint32_t));
- return (0);
- }
|