Back to home page

OSCL-LXR

 
 

    


0001 /* SPDX-License-Identifier: GPL-2.0-only */
0002 /*
0003  * Copyright (c) 2020 Christoph Hellwig.
0004  *
0005  * Support for "universal" pointers that can point to either kernel or userspace
0006  * memory.
0007  */
0008 #ifndef _LINUX_SOCKPTR_H
0009 #define _LINUX_SOCKPTR_H
0010 
0011 #include <linux/slab.h>
0012 #include <linux/uaccess.h>
0013 
0014 typedef struct {
0015     union {
0016         void        *kernel;
0017         void __user *user;
0018     };
0019     bool        is_kernel : 1;
0020 } sockptr_t;
0021 
0022 static inline bool sockptr_is_kernel(sockptr_t sockptr)
0023 {
0024     return sockptr.is_kernel;
0025 }
0026 
0027 static inline sockptr_t KERNEL_SOCKPTR(void *p)
0028 {
0029     return (sockptr_t) { .kernel = p, .is_kernel = true };
0030 }
0031 
0032 static inline sockptr_t USER_SOCKPTR(void __user *p)
0033 {
0034     return (sockptr_t) { .user = p };
0035 }
0036 
0037 static inline bool sockptr_is_null(sockptr_t sockptr)
0038 {
0039     if (sockptr_is_kernel(sockptr))
0040         return !sockptr.kernel;
0041     return !sockptr.user;
0042 }
0043 
0044 static inline int copy_from_sockptr_offset(void *dst, sockptr_t src,
0045         size_t offset, size_t size)
0046 {
0047     if (!sockptr_is_kernel(src))
0048         return copy_from_user(dst, src.user + offset, size);
0049     memcpy(dst, src.kernel + offset, size);
0050     return 0;
0051 }
0052 
0053 static inline int copy_from_sockptr(void *dst, sockptr_t src, size_t size)
0054 {
0055     return copy_from_sockptr_offset(dst, src, 0, size);
0056 }
0057 
0058 static inline int copy_to_sockptr_offset(sockptr_t dst, size_t offset,
0059         const void *src, size_t size)
0060 {
0061     if (!sockptr_is_kernel(dst))
0062         return copy_to_user(dst.user + offset, src, size);
0063     memcpy(dst.kernel + offset, src, size);
0064     return 0;
0065 }
0066 
0067 static inline void *memdup_sockptr(sockptr_t src, size_t len)
0068 {
0069     void *p = kmalloc_track_caller(len, GFP_USER | __GFP_NOWARN);
0070 
0071     if (!p)
0072         return ERR_PTR(-ENOMEM);
0073     if (copy_from_sockptr(p, src, len)) {
0074         kfree(p);
0075         return ERR_PTR(-EFAULT);
0076     }
0077     return p;
0078 }
0079 
0080 static inline void *memdup_sockptr_nul(sockptr_t src, size_t len)
0081 {
0082     char *p = kmalloc_track_caller(len + 1, GFP_KERNEL);
0083 
0084     if (!p)
0085         return ERR_PTR(-ENOMEM);
0086     if (copy_from_sockptr(p, src, len)) {
0087         kfree(p);
0088         return ERR_PTR(-EFAULT);
0089     }
0090     p[len] = '\0';
0091     return p;
0092 }
0093 
0094 static inline long strncpy_from_sockptr(char *dst, sockptr_t src, size_t count)
0095 {
0096     if (sockptr_is_kernel(src)) {
0097         size_t len = min(strnlen(src.kernel, count - 1) + 1, count);
0098 
0099         memcpy(dst, src.kernel, len);
0100         return len;
0101     }
0102     return strncpy_from_user(dst, src.user, count);
0103 }
0104 
0105 static inline int check_zeroed_sockptr(sockptr_t src, size_t offset,
0106                        size_t size)
0107 {
0108     if (!sockptr_is_kernel(src))
0109         return check_zeroed_user(src.user + offset, size);
0110     return memchr_inv(src.kernel + offset, 0, size) == NULL;
0111 }
0112 
0113 #endif /* _LINUX_SOCKPTR_H */