Line data Source code
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 :
14 : typedef struct {
15 : union {
16 : void *kernel;
17 : void __user *user;
18 : };
19 : bool is_kernel : 1;
20 : } sockptr_t;
21 :
22 396 : static inline bool sockptr_is_kernel(sockptr_t sockptr)
23 : {
24 396 : return sockptr.is_kernel;
25 : }
26 :
27 0 : static inline sockptr_t KERNEL_SOCKPTR(void *p)
28 : {
29 0 : return (sockptr_t) { .kernel = p, .is_kernel = true };
30 : }
31 :
32 370 : static inline sockptr_t USER_SOCKPTR(void __user *p)
33 : {
34 370 : return (sockptr_t) { .user = p };
35 : }
36 :
37 : static 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 :
44 396 : static inline int copy_from_sockptr_offset(void *dst, sockptr_t src,
45 : size_t offset, size_t size)
46 : {
47 396 : if (!sockptr_is_kernel(src))
48 792 : return copy_from_user(dst, src.user + offset, size);
49 0 : memcpy(dst, src.kernel + offset, size);
50 0 : return 0;
51 : }
52 :
53 396 : static inline int copy_from_sockptr(void *dst, sockptr_t src, size_t size)
54 : {
55 396 : return copy_from_sockptr_offset(dst, src, 0, size);
56 : }
57 :
58 : static inline int copy_to_sockptr_offset(sockptr_t dst, size_t offset,
59 : const void *src, size_t size)
60 : {
61 : if (!sockptr_is_kernel(dst))
62 : return copy_to_user(dst.user + offset, src, size);
63 : memcpy(dst.kernel + offset, src, size);
64 : return 0;
65 : }
66 :
67 0 : static inline void *memdup_sockptr(sockptr_t src, size_t len)
68 : {
69 0 : void *p = kmalloc_track_caller(len, GFP_USER | __GFP_NOWARN);
70 :
71 0 : if (!p)
72 0 : return ERR_PTR(-ENOMEM);
73 0 : if (copy_from_sockptr(p, src, len)) {
74 0 : kfree(p);
75 0 : return ERR_PTR(-EFAULT);
76 : }
77 : return p;
78 : }
79 :
80 : static inline void *memdup_sockptr_nul(sockptr_t src, size_t len)
81 : {
82 : char *p = kmalloc_track_caller(len + 1, GFP_KERNEL);
83 :
84 : if (!p)
85 : return ERR_PTR(-ENOMEM);
86 : if (copy_from_sockptr(p, src, len)) {
87 : kfree(p);
88 : return ERR_PTR(-EFAULT);
89 : }
90 : p[len] = '\0';
91 : return p;
92 : }
93 :
94 0 : static inline long strncpy_from_sockptr(char *dst, sockptr_t src, size_t count)
95 : {
96 0 : if (sockptr_is_kernel(src)) {
97 0 : size_t len = min(strnlen(src.kernel, count - 1) + 1, count);
98 :
99 0 : memcpy(dst, src.kernel, len);
100 0 : return len;
101 : }
102 0 : return strncpy_from_user(dst, src.user, count);
103 : }
104 :
105 : #endif /* _LINUX_SOCKPTR_H */
|