at for-next 4.1 kB view raw
1/* SPDX-License-Identifier: GPL-2.0-only */ 2/* 3 * Copyright (c) 2020 Christoph Hellwig. 4 * 5 * Support for "universal" pointers that can point to either kernel or userspace 6 * memory. 7 */ 8#ifndef _LINUX_SOCKPTR_H 9#define _LINUX_SOCKPTR_H 10 11#include <linux/slab.h> 12#include <linux/uaccess.h> 13 14typedef struct { 15 union { 16 void *kernel; 17 void __user *user; 18 }; 19 bool is_kernel : 1; 20} sockptr_t; 21 22static inline bool sockptr_is_kernel(sockptr_t sockptr) 23{ 24 return sockptr.is_kernel; 25} 26 27static inline sockptr_t KERNEL_SOCKPTR(void *p) 28{ 29 return (sockptr_t) { .kernel = p, .is_kernel = true }; 30} 31 32static inline sockptr_t USER_SOCKPTR(void __user *p) 33{ 34 return (sockptr_t) { .user = p }; 35} 36 37static inline bool sockptr_is_null(sockptr_t sockptr) 38{ 39 if (sockptr_is_kernel(sockptr)) 40 return !sockptr.kernel; 41 return !sockptr.user; 42} 43 44static inline int copy_from_sockptr_offset(void *dst, sockptr_t src, 45 size_t offset, size_t size) 46{ 47 if (!sockptr_is_kernel(src)) 48 return copy_from_user(dst, src.user + offset, size); 49 memcpy(dst, src.kernel + offset, size); 50 return 0; 51} 52 53/* Deprecated. 54 * This is unsafe, unless caller checked user provided optlen. 55 * Prefer copy_safe_from_sockptr() instead. 56 * 57 * Returns 0 for success, or number of bytes not copied on error. 58 */ 59static inline int copy_from_sockptr(void *dst, sockptr_t src, size_t size) 60{ 61 return copy_from_sockptr_offset(dst, src, 0, size); 62} 63 64/** 65 * copy_safe_from_sockptr: copy a struct from sockptr 66 * @dst: Destination address, in kernel space. This buffer must be @ksize 67 * bytes long. 68 * @ksize: Size of @dst struct. 69 * @optval: Source address. (in user or kernel space) 70 * @optlen: Size of @optval data. 71 * 72 * Returns: 73 * * -EINVAL: @optlen < @ksize 74 * * -EFAULT: access to userspace failed. 75 * * 0 : @ksize bytes were copied 76 */ 77static inline int copy_safe_from_sockptr(void *dst, size_t ksize, 78 sockptr_t optval, unsigned int optlen) 79{ 80 if (optlen < ksize) 81 return -EINVAL; 82 if (copy_from_sockptr(dst, optval, ksize)) 83 return -EFAULT; 84 return 0; 85} 86 87static inline int copy_struct_from_sockptr(void *dst, size_t ksize, 88 sockptr_t src, size_t usize) 89{ 90 size_t size = min(ksize, usize); 91 size_t rest = max(ksize, usize) - size; 92 93 if (!sockptr_is_kernel(src)) 94 return copy_struct_from_user(dst, ksize, src.user, size); 95 96 if (usize < ksize) { 97 memset(dst + size, 0, rest); 98 } else if (usize > ksize) { 99 char *p = src.kernel; 100 101 while (rest--) { 102 if (*p++) 103 return -E2BIG; 104 } 105 } 106 memcpy(dst, src.kernel, size); 107 return 0; 108} 109 110static inline int copy_to_sockptr_offset(sockptr_t dst, size_t offset, 111 const void *src, size_t size) 112{ 113 if (!sockptr_is_kernel(dst)) 114 return copy_to_user(dst.user + offset, src, size); 115 memcpy(dst.kernel + offset, src, size); 116 return 0; 117} 118 119static inline int copy_to_sockptr(sockptr_t dst, const void *src, size_t size) 120{ 121 return copy_to_sockptr_offset(dst, 0, src, size); 122} 123 124static inline void *memdup_sockptr_noprof(sockptr_t src, size_t len) 125{ 126 void *p = kmalloc_track_caller_noprof(len, GFP_USER | __GFP_NOWARN); 127 128 if (!p) 129 return ERR_PTR(-ENOMEM); 130 if (copy_from_sockptr(p, src, len)) { 131 kfree(p); 132 return ERR_PTR(-EFAULT); 133 } 134 return p; 135} 136#define memdup_sockptr(...) alloc_hooks(memdup_sockptr_noprof(__VA_ARGS__)) 137 138static inline void *memdup_sockptr_nul_noprof(sockptr_t src, size_t len) 139{ 140 char *p = kmalloc_track_caller_noprof(len + 1, GFP_KERNEL); 141 142 if (!p) 143 return ERR_PTR(-ENOMEM); 144 if (copy_from_sockptr(p, src, len)) { 145 kfree(p); 146 return ERR_PTR(-EFAULT); 147 } 148 p[len] = '\0'; 149 return p; 150} 151#define memdup_sockptr_nul(...) alloc_hooks(memdup_sockptr_nul_noprof(__VA_ARGS__)) 152 153static inline long strncpy_from_sockptr(char *dst, sockptr_t src, size_t count) 154{ 155 if (sockptr_is_kernel(src)) { 156 size_t len = min(strnlen(src.kernel, count - 1) + 1, count); 157 158 memcpy(dst, src.kernel, len); 159 return len; 160 } 161 return strncpy_from_user(dst, src.user, count); 162} 163 164static inline int check_zeroed_sockptr(sockptr_t src, size_t offset, 165 size_t size) 166{ 167 if (!sockptr_is_kernel(src)) 168 return check_zeroed_user(src.user + offset, size); 169 return memchr_inv(src.kernel + offset, 0, size) == NULL; 170} 171 172#endif /* _LINUX_SOCKPTR_H */