Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-only
2 : : /*
3 : : * dcookies.c
4 : : *
5 : : * Copyright 2002 John Levon <levon@movementarian.org>
6 : : *
7 : : * Persistent cookie-path mappings. These are used by
8 : : * profilers to convert a per-task EIP value into something
9 : : * non-transitory that can be processed at a later date.
10 : : * This is done by locking the dentry/vfsmnt pair in the
11 : : * kernel until released by the tasks needing the persistent
12 : : * objects. The tag is simply an unsigned long that refers
13 : : * to the pair and can be looked up from userspace.
14 : : */
15 : :
16 : : #include <linux/syscalls.h>
17 : : #include <linux/export.h>
18 : : #include <linux/slab.h>
19 : : #include <linux/list.h>
20 : : #include <linux/mount.h>
21 : : #include <linux/capability.h>
22 : : #include <linux/dcache.h>
23 : : #include <linux/mm.h>
24 : : #include <linux/err.h>
25 : : #include <linux/errno.h>
26 : : #include <linux/dcookies.h>
27 : : #include <linux/mutex.h>
28 : : #include <linux/path.h>
29 : : #include <linux/compat.h>
30 : : #include <linux/uaccess.h>
31 : :
32 : : /* The dcookies are allocated from a kmem_cache and
33 : : * hashed onto a small number of lists. None of the
34 : : * code here is particularly performance critical
35 : : */
36 : : struct dcookie_struct {
37 : : struct path path;
38 : : struct list_head hash_list;
39 : : };
40 : :
41 : : static LIST_HEAD(dcookie_users);
42 : : static DEFINE_MUTEX(dcookie_mutex);
43 : : static struct kmem_cache *dcookie_cache __read_mostly;
44 : : static struct list_head *dcookie_hashtable __read_mostly;
45 : : static size_t hash_size __read_mostly;
46 : :
47 : : static inline int is_live(void)
48 : : {
49 : : return !(list_empty(&dcookie_users));
50 : : }
51 : :
52 : :
53 : : /* The dentry is locked, its address will do for the cookie */
54 : : static inline unsigned long dcookie_value(struct dcookie_struct * dcs)
55 : : {
56 : 0 : return (unsigned long)dcs->path.dentry;
57 : : }
58 : :
59 : :
60 : : static size_t dcookie_hash(unsigned long dcookie)
61 : : {
62 : 0 : return (dcookie >> L1_CACHE_SHIFT) & (hash_size - 1);
63 : : }
64 : :
65 : :
66 : 0 : static struct dcookie_struct * find_dcookie(unsigned long dcookie)
67 : : {
68 : : struct dcookie_struct *found = NULL;
69 : : struct dcookie_struct * dcs;
70 : : struct list_head * pos;
71 : : struct list_head * list;
72 : :
73 : 0 : list = dcookie_hashtable + dcookie_hash(dcookie);
74 : :
75 [ # # ]: 0 : list_for_each(pos, list) {
76 : 0 : dcs = list_entry(pos, struct dcookie_struct, hash_list);
77 [ # # ]: 0 : if (dcookie_value(dcs) == dcookie) {
78 : 0 : found = dcs;
79 : 0 : break;
80 : : }
81 : : }
82 : :
83 : 0 : return found;
84 : : }
85 : :
86 : :
87 : : static void hash_dcookie(struct dcookie_struct * dcs)
88 : : {
89 : 0 : struct list_head * list = dcookie_hashtable + dcookie_hash(dcookie_value(dcs));
90 : 0 : list_add(&dcs->hash_list, list);
91 : : }
92 : :
93 : :
94 : 0 : static struct dcookie_struct *alloc_dcookie(const struct path *path)
95 : : {
96 : 0 : struct dcookie_struct *dcs = kmem_cache_alloc(dcookie_cache,
97 : : GFP_KERNEL);
98 : : struct dentry *d;
99 [ # # ]: 0 : if (!dcs)
100 : : return NULL;
101 : :
102 : 0 : d = path->dentry;
103 : : spin_lock(&d->d_lock);
104 : 0 : d->d_flags |= DCACHE_COOKIE;
105 : : spin_unlock(&d->d_lock);
106 : :
107 : 0 : dcs->path = *path;
108 : 0 : path_get(path);
109 : : hash_dcookie(dcs);
110 : 0 : return dcs;
111 : : }
112 : :
113 : :
114 : : /* This is the main kernel-side routine that retrieves the cookie
115 : : * value for a dentry/vfsmnt pair.
116 : : */
117 : 0 : int get_dcookie(const struct path *path, unsigned long *cookie)
118 : : {
119 : : int err = 0;
120 : : struct dcookie_struct * dcs;
121 : :
122 : 0 : mutex_lock(&dcookie_mutex);
123 : :
124 [ # # ]: 0 : if (!is_live()) {
125 : : err = -EINVAL;
126 : : goto out;
127 : : }
128 : :
129 [ # # ]: 0 : if (path->dentry->d_flags & DCACHE_COOKIE) {
130 : 0 : dcs = find_dcookie((unsigned long)path->dentry);
131 : : } else {
132 : 0 : dcs = alloc_dcookie(path);
133 [ # # ]: 0 : if (!dcs) {
134 : : err = -ENOMEM;
135 : : goto out;
136 : : }
137 : : }
138 : :
139 : 0 : *cookie = dcookie_value(dcs);
140 : :
141 : : out:
142 : 0 : mutex_unlock(&dcookie_mutex);
143 : 0 : return err;
144 : : }
145 : :
146 : :
147 : : /* And here is where the userspace process can look up the cookie value
148 : : * to retrieve the path.
149 : : */
150 : 0 : static int do_lookup_dcookie(u64 cookie64, char __user *buf, size_t len)
151 : : {
152 : 0 : unsigned long cookie = (unsigned long)cookie64;
153 : : int err = -EINVAL;
154 : : char * kbuf;
155 : : char * path;
156 : : size_t pathlen;
157 : : struct dcookie_struct * dcs;
158 : :
159 : : /* we could leak path information to users
160 : : * without dir read permission without this
161 : : */
162 [ # # ]: 0 : if (!capable(CAP_SYS_ADMIN))
163 : : return -EPERM;
164 : :
165 : 0 : mutex_lock(&dcookie_mutex);
166 : :
167 [ # # ]: 0 : if (!is_live()) {
168 : : err = -EINVAL;
169 : : goto out;
170 : : }
171 : :
172 [ # # ]: 0 : if (!(dcs = find_dcookie(cookie)))
173 : : goto out;
174 : :
175 : : err = -ENOMEM;
176 : : kbuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
177 [ # # ]: 0 : if (!kbuf)
178 : : goto out;
179 : :
180 : : /* FIXME: (deleted) ? */
181 : 0 : path = d_path(&dcs->path, kbuf, PAGE_SIZE);
182 : :
183 : 0 : mutex_unlock(&dcookie_mutex);
184 : :
185 [ # # ]: 0 : if (IS_ERR(path)) {
186 : : err = PTR_ERR(path);
187 : 0 : goto out_free;
188 : : }
189 : :
190 : : err = -ERANGE;
191 : :
192 : 0 : pathlen = kbuf + PAGE_SIZE - path;
193 [ # # ]: 0 : if (pathlen <= len) {
194 : : err = pathlen;
195 [ # # ]: 0 : if (copy_to_user(buf, path, pathlen))
196 : : err = -EFAULT;
197 : : }
198 : :
199 : : out_free:
200 : 0 : kfree(kbuf);
201 : 0 : return err;
202 : : out:
203 : 0 : mutex_unlock(&dcookie_mutex);
204 : 0 : return err;
205 : : }
206 : :
207 : 0 : SYSCALL_DEFINE3(lookup_dcookie, u64, cookie64, char __user *, buf, size_t, len)
208 : : {
209 : 0 : return do_lookup_dcookie(cookie64, buf, len);
210 : : }
211 : :
212 : : #ifdef CONFIG_COMPAT
213 : : COMPAT_SYSCALL_DEFINE4(lookup_dcookie, u32, w0, u32, w1, char __user *, buf, compat_size_t, len)
214 : : {
215 : : #ifdef __BIG_ENDIAN
216 : : return do_lookup_dcookie(((u64)w0 << 32) | w1, buf, len);
217 : : #else
218 : : return do_lookup_dcookie(((u64)w1 << 32) | w0, buf, len);
219 : : #endif
220 : : }
221 : : #endif
222 : :
223 : 0 : static int dcookie_init(void)
224 : : {
225 : : struct list_head * d;
226 : : unsigned int i, hash_bits;
227 : : int err = -ENOMEM;
228 : :
229 : 0 : dcookie_cache = kmem_cache_create("dcookie_cache",
230 : : sizeof(struct dcookie_struct),
231 : : 0, 0, NULL);
232 : :
233 [ # # ]: 0 : if (!dcookie_cache)
234 : : goto out;
235 : :
236 : 0 : dcookie_hashtable = kmalloc(PAGE_SIZE, GFP_KERNEL);
237 [ # # ]: 0 : if (!dcookie_hashtable)
238 : : goto out_kmem;
239 : :
240 : : err = 0;
241 : :
242 : : /*
243 : : * Find the power-of-two list-heads that can fit into the allocation..
244 : : * We don't guarantee that "sizeof(struct list_head)" is necessarily
245 : : * a power-of-two.
246 : : */
247 : 0 : hash_size = PAGE_SIZE / sizeof(struct list_head);
248 : : hash_bits = 0;
249 : : do {
250 : 0 : hash_bits++;
251 [ # # ]: 0 : } while ((hash_size >> hash_bits) != 0);
252 : 0 : hash_bits--;
253 : :
254 : : /*
255 : : * Re-calculate the actual number of entries and the mask
256 : : * from the number of bits we can fit.
257 : : */
258 : 0 : hash_size = 1UL << hash_bits;
259 : :
260 : : /* And initialize the newly allocated array */
261 : : d = dcookie_hashtable;
262 : : i = hash_size;
263 : : do {
264 : : INIT_LIST_HEAD(d);
265 : 0 : d++;
266 : 0 : i--;
267 [ # # ]: 0 : } while (i);
268 : :
269 : : out:
270 : 0 : return err;
271 : : out_kmem:
272 : 0 : kmem_cache_destroy(dcookie_cache);
273 : 0 : goto out;
274 : : }
275 : :
276 : :
277 : 0 : static void free_dcookie(struct dcookie_struct * dcs)
278 : : {
279 : 0 : struct dentry *d = dcs->path.dentry;
280 : :
281 : : spin_lock(&d->d_lock);
282 : 0 : d->d_flags &= ~DCACHE_COOKIE;
283 : : spin_unlock(&d->d_lock);
284 : :
285 : 0 : path_put(&dcs->path);
286 : 0 : kmem_cache_free(dcookie_cache, dcs);
287 : 0 : }
288 : :
289 : :
290 : 0 : static void dcookie_exit(void)
291 : : {
292 : : struct list_head * list;
293 : : struct list_head * pos;
294 : : struct list_head * pos2;
295 : : struct dcookie_struct * dcs;
296 : : size_t i;
297 : :
298 [ # # ]: 0 : for (i = 0; i < hash_size; ++i) {
299 : 0 : list = dcookie_hashtable + i;
300 [ # # ]: 0 : list_for_each_safe(pos, pos2, list) {
301 : 0 : dcs = list_entry(pos, struct dcookie_struct, hash_list);
302 : : list_del(&dcs->hash_list);
303 : 0 : free_dcookie(dcs);
304 : : }
305 : : }
306 : :
307 : 0 : kfree(dcookie_hashtable);
308 : 0 : kmem_cache_destroy(dcookie_cache);
309 : 0 : }
310 : :
311 : :
312 : : struct dcookie_user {
313 : : struct list_head next;
314 : : };
315 : :
316 : 0 : struct dcookie_user * dcookie_register(void)
317 : : {
318 : : struct dcookie_user * user;
319 : :
320 : 0 : mutex_lock(&dcookie_mutex);
321 : :
322 : : user = kmalloc(sizeof(struct dcookie_user), GFP_KERNEL);
323 [ # # ]: 0 : if (!user)
324 : : goto out;
325 : :
326 [ # # # # ]: 0 : if (!is_live() && dcookie_init())
327 : : goto out_free;
328 : :
329 : 0 : list_add(&user->next, &dcookie_users);
330 : :
331 : : out:
332 : 0 : mutex_unlock(&dcookie_mutex);
333 : 0 : return user;
334 : : out_free:
335 : 0 : kfree(user);
336 : : user = NULL;
337 : 0 : goto out;
338 : : }
339 : :
340 : :
341 : 0 : void dcookie_unregister(struct dcookie_user * user)
342 : : {
343 : 0 : mutex_lock(&dcookie_mutex);
344 : :
345 : : list_del(&user->next);
346 : 0 : kfree(user);
347 : :
348 [ # # ]: 0 : if (!is_live())
349 : 0 : dcookie_exit();
350 : :
351 : 0 : mutex_unlock(&dcookie_mutex);
352 : 0 : }
353 : :
354 : : EXPORT_SYMBOL_GPL(dcookie_register);
355 : : EXPORT_SYMBOL_GPL(dcookie_unregister);
356 : : EXPORT_SYMBOL_GPL(get_dcookie);
|