Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.9-rc2 356 lines 7.2 kB view raw
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 */ 36struct dcookie_struct { 37 struct path path; 38 struct list_head hash_list; 39}; 40 41static LIST_HEAD(dcookie_users); 42static DEFINE_MUTEX(dcookie_mutex); 43static struct kmem_cache *dcookie_cache __read_mostly; 44static struct list_head *dcookie_hashtable __read_mostly; 45static size_t hash_size __read_mostly; 46 47static 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 */ 54static inline unsigned long dcookie_value(struct dcookie_struct * dcs) 55{ 56 return (unsigned long)dcs->path.dentry; 57} 58 59 60static size_t dcookie_hash(unsigned long dcookie) 61{ 62 return (dcookie >> L1_CACHE_SHIFT) & (hash_size - 1); 63} 64 65 66static 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 list = dcookie_hashtable + dcookie_hash(dcookie); 74 75 list_for_each(pos, list) { 76 dcs = list_entry(pos, struct dcookie_struct, hash_list); 77 if (dcookie_value(dcs) == dcookie) { 78 found = dcs; 79 break; 80 } 81 } 82 83 return found; 84} 85 86 87static void hash_dcookie(struct dcookie_struct * dcs) 88{ 89 struct list_head * list = dcookie_hashtable + dcookie_hash(dcookie_value(dcs)); 90 list_add(&dcs->hash_list, list); 91} 92 93 94static struct dcookie_struct *alloc_dcookie(const struct path *path) 95{ 96 struct dcookie_struct *dcs = kmem_cache_alloc(dcookie_cache, 97 GFP_KERNEL); 98 struct dentry *d; 99 if (!dcs) 100 return NULL; 101 102 d = path->dentry; 103 spin_lock(&d->d_lock); 104 d->d_flags |= DCACHE_COOKIE; 105 spin_unlock(&d->d_lock); 106 107 dcs->path = *path; 108 path_get(path); 109 hash_dcookie(dcs); 110 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 */ 117int get_dcookie(const struct path *path, unsigned long *cookie) 118{ 119 int err = 0; 120 struct dcookie_struct * dcs; 121 122 mutex_lock(&dcookie_mutex); 123 124 if (!is_live()) { 125 err = -EINVAL; 126 goto out; 127 } 128 129 if (path->dentry->d_flags & DCACHE_COOKIE) { 130 dcs = find_dcookie((unsigned long)path->dentry); 131 } else { 132 dcs = alloc_dcookie(path); 133 if (!dcs) { 134 err = -ENOMEM; 135 goto out; 136 } 137 } 138 139 *cookie = dcookie_value(dcs); 140 141out: 142 mutex_unlock(&dcookie_mutex); 143 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 */ 150static int do_lookup_dcookie(u64 cookie64, char __user *buf, size_t len) 151{ 152 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 if (!capable(CAP_SYS_ADMIN)) 163 return -EPERM; 164 165 mutex_lock(&dcookie_mutex); 166 167 if (!is_live()) { 168 err = -EINVAL; 169 goto out; 170 } 171 172 if (!(dcs = find_dcookie(cookie))) 173 goto out; 174 175 err = -ENOMEM; 176 kbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); 177 if (!kbuf) 178 goto out; 179 180 /* FIXME: (deleted) ? */ 181 path = d_path(&dcs->path, kbuf, PAGE_SIZE); 182 183 mutex_unlock(&dcookie_mutex); 184 185 if (IS_ERR(path)) { 186 err = PTR_ERR(path); 187 goto out_free; 188 } 189 190 err = -ERANGE; 191 192 pathlen = kbuf + PAGE_SIZE - path; 193 if (pathlen <= len) { 194 err = pathlen; 195 if (copy_to_user(buf, path, pathlen)) 196 err = -EFAULT; 197 } 198 199out_free: 200 kfree(kbuf); 201 return err; 202out: 203 mutex_unlock(&dcookie_mutex); 204 return err; 205} 206 207SYSCALL_DEFINE3(lookup_dcookie, u64, cookie64, char __user *, buf, size_t, len) 208{ 209 return do_lookup_dcookie(cookie64, buf, len); 210} 211 212#ifdef CONFIG_COMPAT 213COMPAT_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 223static int dcookie_init(void) 224{ 225 struct list_head * d; 226 unsigned int i, hash_bits; 227 int err = -ENOMEM; 228 229 dcookie_cache = kmem_cache_create("dcookie_cache", 230 sizeof(struct dcookie_struct), 231 0, 0, NULL); 232 233 if (!dcookie_cache) 234 goto out; 235 236 dcookie_hashtable = kmalloc(PAGE_SIZE, GFP_KERNEL); 237 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 hash_size = PAGE_SIZE / sizeof(struct list_head); 248 hash_bits = 0; 249 do { 250 hash_bits++; 251 } while ((hash_size >> hash_bits) != 0); 252 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 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 d++; 266 i--; 267 } while (i); 268 269out: 270 return err; 271out_kmem: 272 kmem_cache_destroy(dcookie_cache); 273 goto out; 274} 275 276 277static void free_dcookie(struct dcookie_struct * dcs) 278{ 279 struct dentry *d = dcs->path.dentry; 280 281 spin_lock(&d->d_lock); 282 d->d_flags &= ~DCACHE_COOKIE; 283 spin_unlock(&d->d_lock); 284 285 path_put(&dcs->path); 286 kmem_cache_free(dcookie_cache, dcs); 287} 288 289 290static 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 for (i = 0; i < hash_size; ++i) { 299 list = dcookie_hashtable + i; 300 list_for_each_safe(pos, pos2, list) { 301 dcs = list_entry(pos, struct dcookie_struct, hash_list); 302 list_del(&dcs->hash_list); 303 free_dcookie(dcs); 304 } 305 } 306 307 kfree(dcookie_hashtable); 308 kmem_cache_destroy(dcookie_cache); 309} 310 311 312struct dcookie_user { 313 struct list_head next; 314}; 315 316struct dcookie_user * dcookie_register(void) 317{ 318 struct dcookie_user * user; 319 320 mutex_lock(&dcookie_mutex); 321 322 user = kmalloc(sizeof(struct dcookie_user), GFP_KERNEL); 323 if (!user) 324 goto out; 325 326 if (!is_live() && dcookie_init()) 327 goto out_free; 328 329 list_add(&user->next, &dcookie_users); 330 331out: 332 mutex_unlock(&dcookie_mutex); 333 return user; 334out_free: 335 kfree(user); 336 user = NULL; 337 goto out; 338} 339 340 341void dcookie_unregister(struct dcookie_user * user) 342{ 343 mutex_lock(&dcookie_mutex); 344 345 list_del(&user->next); 346 kfree(user); 347 348 if (!is_live()) 349 dcookie_exit(); 350 351 mutex_unlock(&dcookie_mutex); 352} 353 354EXPORT_SYMBOL_GPL(dcookie_register); 355EXPORT_SYMBOL_GPL(dcookie_unregister); 356EXPORT_SYMBOL_GPL(get_dcookie);