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/compiler.h> 12 #include <linux/slab.h> 13 #include <linux/uaccess.h> 14 15 #ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE 16 typedef union { 17 void *kernel; 18 void __user *user; 19 } sockptr_t; 20 21 static inline bool sockptr_is_kernel(sockptr_t sockptr) 22 { 23 return (unsigned long)sockptr.kernel >= TASK_SIZE; 24 } 25 26 static inline sockptr_t KERNEL_SOCKPTR(void *p) 27 { 28 return (sockptr_t) { .kernel = p }; 29 } 30 31 static inline int __must_check init_user_sockptr(sockptr_t *sp, void __user *p) 32 { 33 if ((unsigned long)p >= TASK_SIZE) 34 return -EFAULT; 35 sp->user = p; 36 return 0; 37 } 38 #else /* CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE */ 39 typedef struct { 40 union { 41 void *kernel; 42 void __user *user; 43 }; 44 bool is_kernel : 1; 45 } sockptr_t; 46 47 static inline bool sockptr_is_kernel(sockptr_t sockptr) 48 { 49 return sockptr.is_kernel; 50 } 51 52 static inline sockptr_t KERNEL_SOCKPTR(void *p) 53 { 54 return (sockptr_t) { .kernel = p, .is_kernel = true }; 55 } 56 57 static inline int __must_check init_user_sockptr(sockptr_t *sp, void __user *p) 58 { 59 sp->user = p; 60 sp->is_kernel = false; 61 return 0; 62 } 63 #endif /* CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE */ 64 65 static inline bool sockptr_is_null(sockptr_t sockptr) 66 { 67 if (sockptr_is_kernel(sockptr)) 68 return !sockptr.kernel; 69 return !sockptr.user; 70 } 71 72 static inline int copy_from_sockptr(void *dst, sockptr_t src, size_t size) 73 { 74 if (!sockptr_is_kernel(src)) 75 return copy_from_user(dst, src.user, size); 76 memcpy(dst, src.kernel, size); 77 return 0; 78 } 79 80 static inline int copy_to_sockptr(sockptr_t dst, const void *src, size_t size) 81 { 82 if (!sockptr_is_kernel(dst)) 83 return copy_to_user(dst.user, src, size); 84 memcpy(dst.kernel, src, size); 85 return 0; 86 } 87 88 static inline void *memdup_sockptr(sockptr_t src, size_t len) 89 { 90 void *p = kmalloc_track_caller(len, GFP_USER | __GFP_NOWARN); 91 92 if (!p) 93 return ERR_PTR(-ENOMEM); 94 if (copy_from_sockptr(p, src, len)) { 95 kfree(p); 96 return ERR_PTR(-EFAULT); 97 } 98 return p; 99 } 100 101 static inline void *memdup_sockptr_nul(sockptr_t src, size_t len) 102 { 103 char *p = kmalloc_track_caller(len + 1, GFP_KERNEL); 104 105 if (!p) 106 return ERR_PTR(-ENOMEM); 107 if (copy_from_sockptr(p, src, len)) { 108 kfree(p); 109 return ERR_PTR(-EFAULT); 110 } 111 p[len] = '\0'; 112 return p; 113 } 114 115 static inline void sockptr_advance(sockptr_t sockptr, size_t len) 116 { 117 if (sockptr_is_kernel(sockptr)) 118 sockptr.kernel += len; 119 else 120 sockptr.user += len; 121 } 122 123 static inline long strncpy_from_sockptr(char *dst, sockptr_t src, size_t count) 124 { 125 if (sockptr_is_kernel(src)) { 126 size_t len = min(strnlen(src.kernel, count - 1) + 1, count); 127 128 memcpy(dst, src.kernel, len); 129 return len; 130 } 131 return strncpy_from_user(dst, src.user, count); 132 } 133 134 #endif /* _LINUX_SOCKPTR_H */ 135