Skip to content

Commit ba423fd

Browse files
Christoph Hellwigdavem330
authored andcommitted
net: add a new sockptr_t type
Add a uptr_t type that can hold a pointer to either a user or kernel memory region, and simply helpers to copy to and from it. Signed-off-by: Christoph Hellwig <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent d200cf6 commit ba423fd

File tree

1 file changed

+104
-0
lines changed

1 file changed

+104
-0
lines changed

include/linux/sockptr.h

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
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+
static inline bool sockptr_is_kernel(sockptr_t sockptr)
23+
{
24+
return sockptr.is_kernel;
25+
}
26+
27+
static inline sockptr_t KERNEL_SOCKPTR(void *p)
28+
{
29+
return (sockptr_t) { .kernel = p, .is_kernel = true };
30+
}
31+
32+
static inline sockptr_t USER_SOCKPTR(void __user *p)
33+
{
34+
return (sockptr_t) { .user = p };
35+
}
36+
37+
static inline bool sockptr_is_null(sockptr_t sockptr)
38+
{
39+
return !sockptr.user && !sockptr.kernel;
40+
}
41+
42+
static inline int copy_from_sockptr(void *dst, sockptr_t src, size_t size)
43+
{
44+
if (!sockptr_is_kernel(src))
45+
return copy_from_user(dst, src.user, size);
46+
memcpy(dst, src.kernel, size);
47+
return 0;
48+
}
49+
50+
static inline int copy_to_sockptr(sockptr_t dst, const void *src, size_t size)
51+
{
52+
if (!sockptr_is_kernel(dst))
53+
return copy_to_user(dst.user, src, size);
54+
memcpy(dst.kernel, src, size);
55+
return 0;
56+
}
57+
58+
static inline void *memdup_sockptr(sockptr_t src, size_t len)
59+
{
60+
void *p = kmalloc_track_caller(len, GFP_USER | __GFP_NOWARN);
61+
62+
if (!p)
63+
return ERR_PTR(-ENOMEM);
64+
if (copy_from_sockptr(p, src, len)) {
65+
kfree(p);
66+
return ERR_PTR(-EFAULT);
67+
}
68+
return p;
69+
}
70+
71+
static inline void *memdup_sockptr_nul(sockptr_t src, size_t len)
72+
{
73+
char *p = kmalloc_track_caller(len + 1, GFP_KERNEL);
74+
75+
if (!p)
76+
return ERR_PTR(-ENOMEM);
77+
if (copy_from_sockptr(p, src, len)) {
78+
kfree(p);
79+
return ERR_PTR(-EFAULT);
80+
}
81+
p[len] = '\0';
82+
return p;
83+
}
84+
85+
static inline void sockptr_advance(sockptr_t sockptr, size_t len)
86+
{
87+
if (sockptr_is_kernel(sockptr))
88+
sockptr.kernel += len;
89+
else
90+
sockptr.user += len;
91+
}
92+
93+
static inline long strncpy_from_sockptr(char *dst, sockptr_t src, size_t count)
94+
{
95+
if (sockptr_is_kernel(src)) {
96+
size_t len = min(strnlen(src.kernel, count - 1) + 1, count);
97+
98+
memcpy(dst, src.kernel, len);
99+
return len;
100+
}
101+
return strncpy_from_user(dst, src.user, count);
102+
}
103+
104+
#endif /* _LINUX_SOCKPTR_H */

0 commit comments

Comments
 (0)