1*4882a593Smuzhiyun /* SPDX-License-Identifier: GPL-2.0-only */
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (c) 2020 Christoph Hellwig.
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Support for "universal" pointers that can point to either kernel or userspace
6*4882a593Smuzhiyun * memory.
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun #ifndef _LINUX_SOCKPTR_H
9*4882a593Smuzhiyun #define _LINUX_SOCKPTR_H
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun #include <linux/slab.h>
12*4882a593Smuzhiyun #include <linux/uaccess.h>
13*4882a593Smuzhiyun
14*4882a593Smuzhiyun typedef struct {
15*4882a593Smuzhiyun union {
16*4882a593Smuzhiyun void *kernel;
17*4882a593Smuzhiyun void __user *user;
18*4882a593Smuzhiyun };
19*4882a593Smuzhiyun bool is_kernel : 1;
20*4882a593Smuzhiyun } sockptr_t;
21*4882a593Smuzhiyun
sockptr_is_kernel(sockptr_t sockptr)22*4882a593Smuzhiyun static inline bool sockptr_is_kernel(sockptr_t sockptr)
23*4882a593Smuzhiyun {
24*4882a593Smuzhiyun return sockptr.is_kernel;
25*4882a593Smuzhiyun }
26*4882a593Smuzhiyun
KERNEL_SOCKPTR(void * p)27*4882a593Smuzhiyun static inline sockptr_t KERNEL_SOCKPTR(void *p)
28*4882a593Smuzhiyun {
29*4882a593Smuzhiyun return (sockptr_t) { .kernel = p, .is_kernel = true };
30*4882a593Smuzhiyun }
31*4882a593Smuzhiyun
USER_SOCKPTR(void __user * p)32*4882a593Smuzhiyun static inline sockptr_t USER_SOCKPTR(void __user *p)
33*4882a593Smuzhiyun {
34*4882a593Smuzhiyun return (sockptr_t) { .user = p };
35*4882a593Smuzhiyun }
36*4882a593Smuzhiyun
sockptr_is_null(sockptr_t sockptr)37*4882a593Smuzhiyun static inline bool sockptr_is_null(sockptr_t sockptr)
38*4882a593Smuzhiyun {
39*4882a593Smuzhiyun if (sockptr_is_kernel(sockptr))
40*4882a593Smuzhiyun return !sockptr.kernel;
41*4882a593Smuzhiyun return !sockptr.user;
42*4882a593Smuzhiyun }
43*4882a593Smuzhiyun
copy_from_sockptr_offset(void * dst,sockptr_t src,size_t offset,size_t size)44*4882a593Smuzhiyun static inline int copy_from_sockptr_offset(void *dst, sockptr_t src,
45*4882a593Smuzhiyun size_t offset, size_t size)
46*4882a593Smuzhiyun {
47*4882a593Smuzhiyun if (!sockptr_is_kernel(src))
48*4882a593Smuzhiyun return copy_from_user(dst, src.user + offset, size);
49*4882a593Smuzhiyun memcpy(dst, src.kernel + offset, size);
50*4882a593Smuzhiyun return 0;
51*4882a593Smuzhiyun }
52*4882a593Smuzhiyun
copy_from_sockptr(void * dst,sockptr_t src,size_t size)53*4882a593Smuzhiyun static inline int copy_from_sockptr(void *dst, sockptr_t src, size_t size)
54*4882a593Smuzhiyun {
55*4882a593Smuzhiyun return copy_from_sockptr_offset(dst, src, 0, size);
56*4882a593Smuzhiyun }
57*4882a593Smuzhiyun
copy_to_sockptr_offset(sockptr_t dst,size_t offset,const void * src,size_t size)58*4882a593Smuzhiyun static inline int copy_to_sockptr_offset(sockptr_t dst, size_t offset,
59*4882a593Smuzhiyun const void *src, size_t size)
60*4882a593Smuzhiyun {
61*4882a593Smuzhiyun if (!sockptr_is_kernel(dst))
62*4882a593Smuzhiyun return copy_to_user(dst.user + offset, src, size);
63*4882a593Smuzhiyun memcpy(dst.kernel + offset, src, size);
64*4882a593Smuzhiyun return 0;
65*4882a593Smuzhiyun }
66*4882a593Smuzhiyun
memdup_sockptr(sockptr_t src,size_t len)67*4882a593Smuzhiyun static inline void *memdup_sockptr(sockptr_t src, size_t len)
68*4882a593Smuzhiyun {
69*4882a593Smuzhiyun void *p = kmalloc_track_caller(len, GFP_USER | __GFP_NOWARN);
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun if (!p)
72*4882a593Smuzhiyun return ERR_PTR(-ENOMEM);
73*4882a593Smuzhiyun if (copy_from_sockptr(p, src, len)) {
74*4882a593Smuzhiyun kfree(p);
75*4882a593Smuzhiyun return ERR_PTR(-EFAULT);
76*4882a593Smuzhiyun }
77*4882a593Smuzhiyun return p;
78*4882a593Smuzhiyun }
79*4882a593Smuzhiyun
memdup_sockptr_nul(sockptr_t src,size_t len)80*4882a593Smuzhiyun static inline void *memdup_sockptr_nul(sockptr_t src, size_t len)
81*4882a593Smuzhiyun {
82*4882a593Smuzhiyun char *p = kmalloc_track_caller(len + 1, GFP_KERNEL);
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun if (!p)
85*4882a593Smuzhiyun return ERR_PTR(-ENOMEM);
86*4882a593Smuzhiyun if (copy_from_sockptr(p, src, len)) {
87*4882a593Smuzhiyun kfree(p);
88*4882a593Smuzhiyun return ERR_PTR(-EFAULT);
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun p[len] = '\0';
91*4882a593Smuzhiyun return p;
92*4882a593Smuzhiyun }
93*4882a593Smuzhiyun
strncpy_from_sockptr(char * dst,sockptr_t src,size_t count)94*4882a593Smuzhiyun static inline long strncpy_from_sockptr(char *dst, sockptr_t src, size_t count)
95*4882a593Smuzhiyun {
96*4882a593Smuzhiyun if (sockptr_is_kernel(src)) {
97*4882a593Smuzhiyun size_t len = min(strnlen(src.kernel, count - 1) + 1, count);
98*4882a593Smuzhiyun
99*4882a593Smuzhiyun memcpy(dst, src.kernel, len);
100*4882a593Smuzhiyun return len;
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun return strncpy_from_user(dst, src.user, count);
103*4882a593Smuzhiyun }
104*4882a593Smuzhiyun
105*4882a593Smuzhiyun #endif /* _LINUX_SOCKPTR_H */
106