userspace: add functions for copying to/from user
We now have functions for handling all the details of copying data to/from user mode, including C strings and copying data into resource pool allocations. Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
This commit is contained in:
parent
b1dec2992e
commit
c8188f6722
2 changed files with 227 additions and 0 deletions
|
@ -125,6 +125,116 @@ extern void _thread_perms_all_clear(struct k_thread *thread);
|
|||
*/
|
||||
void _k_object_uninit(void *obj);
|
||||
|
||||
/**
|
||||
* @brief Obtain the size of a C string passed from user mode
|
||||
*
|
||||
* Given a C string pointer and a maximum size, obtain the true
|
||||
* size of the string (not including the trailing NULL byte) just as
|
||||
* if calling strnlen() on it, with the same semantics of strnlen() with
|
||||
* respect to the return value and the maxlen parameter.
|
||||
*
|
||||
* Any memory protection faults triggered by the examination of the string
|
||||
* will be safely handled and an error code returned.
|
||||
*
|
||||
* NOTE: Doesn't guarantee that user mode has actual access to this
|
||||
* string, you will need to still do a Z_SYSCALL_MEMORY_READ()
|
||||
* with the obtained size value to guarantee this.
|
||||
*
|
||||
* @param src String to measure size of
|
||||
* @param maxlen Maximum number of characters to examine
|
||||
* @param err Pointer to int, filled in with -1 on memory error, 0 on
|
||||
* success
|
||||
* @return undefined on error, or strlen(src) if that is less than maxlen, or
|
||||
* maxlen if there were no NULL terminating characters within the
|
||||
* first maxlen bytes.
|
||||
*/
|
||||
static inline size_t z_user_string_nlen(const char *src, size_t maxlen,
|
||||
int *err)
|
||||
{
|
||||
return z_arch_user_string_nlen(src, maxlen, err);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy data from userspace into a resource pool allocation
|
||||
*
|
||||
* Given a pointer and a size, allocate a similarly sized buffer in the
|
||||
* caller's resource pool and copy all the data within it to the newly
|
||||
* allocated buffer. This will need to be freed later with k_free().
|
||||
*
|
||||
* Checks are done to ensure that the current thread would have read
|
||||
* access to the provided buffer.
|
||||
*
|
||||
* @param src Source memory address
|
||||
* @param size Size of the memory buffer
|
||||
* @return An allocated buffer with the data copied within it, or NULL
|
||||
* if some error condition occurred
|
||||
*/
|
||||
extern void *z_user_alloc_from_copy(void *src, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Copy data from user mode
|
||||
*
|
||||
* Given a userspace pointer and a size, copies data from it into a provided
|
||||
* destination buffer, performing checks to ensure that the caller would have
|
||||
* appropriate access when in user mode.
|
||||
*
|
||||
* @param dst Destination memory buffer
|
||||
* @param src Source memory buffer, in userspace
|
||||
* @param size Number of bytes to copy
|
||||
* @retval 0 On success
|
||||
* @retval EFAULT On memory access error
|
||||
*/
|
||||
extern int z_user_from_copy(void *dst, void *src, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Copy data to user mode
|
||||
*
|
||||
* Given a userspace pointer and a size, copies data to it from a provided
|
||||
* source buffer, performing checks to ensure that the caller would have
|
||||
* appropriate access when in user mode.
|
||||
*
|
||||
* @param dst Destination memory buffer, in userspace
|
||||
* @param src Source memory buffer
|
||||
* @param size Number of bytes to copy
|
||||
* @retval 0 On success
|
||||
* @retval EFAULT On memory access error
|
||||
*/
|
||||
extern int z_user_to_copy(void *dst, void *src, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Copy a C string from userspace into a resource pool allocation
|
||||
*
|
||||
* Given a C string and maximum length, duplicate the string using an
|
||||
* allocation from the calling thread's resource pool. This will need to be
|
||||
* freed later with k_free().
|
||||
*
|
||||
* Checks are performed to ensure that the string is valid memory and that
|
||||
* the caller has access to it in user mode.
|
||||
*
|
||||
* @param src Source string pointer, in userspace
|
||||
* @param maxlen Maximum size of the string including trailing NULL
|
||||
* @return The duplicated string, or NULL if an error occurred.
|
||||
*/
|
||||
extern char *z_user_string_alloc_copy(char *src, size_t maxlen);
|
||||
|
||||
/**
|
||||
* @brief Copy a C string from userspace into a provided buffer
|
||||
*
|
||||
* Given a C string and maximum length, copy the string into a buffer.
|
||||
*
|
||||
* Checks are performed to ensure that the string is valid memory and that
|
||||
* the caller has access to it in user mode.
|
||||
*
|
||||
* @param dst Destination buffer
|
||||
* @param src Source string pointer, in userspace
|
||||
* @param maxlen Maximum size of the string including trailing NULL
|
||||
* @retval 0 on success
|
||||
* @retval EINVAL if the source string is too long with respect
|
||||
* to maxlen
|
||||
* @retval EFAULT On memory access error
|
||||
*/
|
||||
extern int z_user_string_copy(char *dst, char *src, size_t maxlen);
|
||||
|
||||
#define Z_OOPS(expr) \
|
||||
do { \
|
||||
if (expr) { \
|
||||
|
|
|
@ -475,6 +475,123 @@ void _k_object_uninit(void *object)
|
|||
ko->flags &= ~K_OBJ_FLAG_INITIALIZED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy to/from helper functions used in syscall handlers
|
||||
*/
|
||||
void *z_user_alloc_from_copy(void *src, size_t size)
|
||||
{
|
||||
void *dst = NULL;
|
||||
int key;
|
||||
|
||||
key = irq_lock();
|
||||
|
||||
/* Does the caller in user mode have access to read this memory? */
|
||||
if (Z_SYSCALL_MEMORY_READ(src, size)) {
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
dst = z_thread_malloc(size);
|
||||
if (!dst) {
|
||||
printk("out of thread resource pool memory (%zu)", size);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
memcpy(dst, src, size);
|
||||
out_err:
|
||||
irq_unlock(key);
|
||||
return dst;
|
||||
}
|
||||
|
||||
static int user_copy(void *dst, void *src, size_t size, bool to_user)
|
||||
{
|
||||
int ret = EFAULT;
|
||||
int key;
|
||||
|
||||
key = irq_lock();
|
||||
|
||||
/* Does the caller in user mode have access to this memory? */
|
||||
if (to_user ? Z_SYSCALL_MEMORY_WRITE(dst, size) :
|
||||
Z_SYSCALL_MEMORY_READ(src, size)) {
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
memcpy(dst, src, size);
|
||||
ret = 0;
|
||||
out_err:
|
||||
irq_unlock(key);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int z_user_from_copy(void *dst, void *src, size_t size)
|
||||
{
|
||||
return user_copy(dst, src, size, false);
|
||||
}
|
||||
|
||||
int z_user_to_copy(void *dst, void *src, size_t size)
|
||||
{
|
||||
return user_copy(dst, src, size, true);
|
||||
}
|
||||
|
||||
char *z_user_string_alloc_copy(char *src, size_t maxlen)
|
||||
{
|
||||
unsigned long actual_len;
|
||||
int key, err;
|
||||
char *ret = NULL;
|
||||
|
||||
key = irq_lock();
|
||||
actual_len = z_user_string_nlen(src, maxlen, &err);
|
||||
if (err) {
|
||||
goto out;
|
||||
}
|
||||
if (actual_len == maxlen) {
|
||||
/* Not NULL terminated */
|
||||
printk("string too long %p (%lu)\n", src, actual_len);
|
||||
goto out;
|
||||
}
|
||||
if (__builtin_uaddl_overflow(actual_len, 1, &actual_len)) {
|
||||
printk("overflow\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = z_user_alloc_from_copy(src, actual_len);
|
||||
out:
|
||||
irq_unlock(key);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int z_user_string_copy(char *dst, char *src, size_t maxlen)
|
||||
{
|
||||
unsigned long actual_len;
|
||||
int key, ret, err;
|
||||
|
||||
key = irq_lock();
|
||||
actual_len = z_user_string_nlen(src, maxlen, &err);
|
||||
if (err) {
|
||||
ret = EFAULT;
|
||||
goto out;
|
||||
}
|
||||
if (actual_len == maxlen) {
|
||||
/* Not NULL terminated */
|
||||
printk("string too long %p (%lu)\n", src, actual_len);
|
||||
ret = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (__builtin_uaddl_overflow(actual_len, 1, &actual_len)) {
|
||||
printk("overflow\n");
|
||||
ret = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = z_user_from_copy(dst, src, actual_len);
|
||||
out:
|
||||
irq_unlock(key);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Default handlers if otherwise unimplemented
|
||||
*/
|
||||
|
||||
static u32_t handler_bad_syscall(u32_t bad_id, u32_t arg2, u32_t arg3,
|
||||
u32_t arg4, u32_t arg5, u32_t arg6, void *ssf)
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue