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 return !sockptr.user && !sockptr.kernel; 68 } 69 70 static inline int copy_from_sockptr(void *dst, sockptr_t src, size_t size) 71 { 72 if (!sockptr_is_kernel(src)) 73 return copy_from_user(dst, src.user, size); 74 memcpy(dst, src.kernel, size); 75 return 0; 76 } 77 78 static inline int copy_to_sockptr(sockptr_t dst, const void *src, size_t size) 79 { 80 if (!sockptr_is_kernel(dst)) 81 return copy_to_user(dst.user, src, size); 82 memcpy(dst.kernel, src, size); 83 return 0; 84 } 85 86 static inline void *memdup_sockptr(sockptr_t src, size_t len) 87 { 88 void *p = kmalloc_track_caller(len, GFP_USER | __GFP_NOWARN); 89 90 if (!p) 91 return ERR_PTR(-ENOMEM); 92 if (copy_from_sockptr(p, src, len)) { 93 kfree(p); 94 return ERR_PTR(-EFAULT); 95 } 96 return p; 97 } 98 99 static inline void *memdup_sockptr_nul(sockptr_t src, size_t len) 100 { 101 char *p = kmalloc_track_caller(len + 1, GFP_KERNEL); 102 103 if (!p) 104 return ERR_PTR(-ENOMEM); 105 if (copy_from_sockptr(p, src, len)) { 106 kfree(p); 107 return ERR_PTR(-EFAULT); 108 } 109 p[len] = '\0'; 110 return p; 111 } 112 113 static inline void sockptr_advance(sockptr_t sockptr, size_t len) 114 { 115 if (sockptr_is_kernel(sockptr)) 116 sockptr.kernel += len; 117 else 118 sockptr.user += len; 119 } 120 121 static inline long strncpy_from_sockptr(char *dst, sockptr_t src, size_t count) 122 { 123 if (sockptr_is_kernel(src)) { 124 size_t len = min(strnlen(src.kernel, count - 1) + 1, count); 125 126 memcpy(dst, src.kernel, len); 127 return len; 128 } 129 return strncpy_from_user(dst, src.user, count); 130 } 131 132 #endif /* _LINUX_SOCKPTR_H */ 133