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.

kernfs: use namespace id instead of pointer for hashing and comparison

kernfs uses the namespace tag as both a hash seed (via init_name_hash())
and a comparison key in the rbtree. The resulting hash values are exposed
to userspace through directory seek positions (ctx->pos), and the raw
pointer comparisons in kernfs_name_compare() encode kernel pointer
ordering into the rbtree layout.

This constitutes a KASLR information leak since the hash and ordering
derived from kernel pointers can be observed from userspace.

Fix this by using the 64-bit namespace id (ns_common::ns_id) instead of
the raw pointer value for both hashing and comparison. The namespace id
is a stable, non-secret identifier that is already exposed to userspace
through other interfaces (e.g., /proc/pid/ns/, ioctl NS_GET_NSID).

Introduce kernfs_ns_id() as a helper that extracts the namespace id from
a potentially-NULL ns_common pointer, returning 0 for the no-namespace
case.

All namespace equality checks in the directory iteration and dentry
revalidation paths are also switched from pointer comparison to ns_id
comparison for consistency.

Signed-off-by: Christian Brauner <brauner@kernel.org>

+26 -7
+26 -7
fs/kernfs/dir.c
··· 14 14 #include <linux/slab.h> 15 15 #include <linux/security.h> 16 16 #include <linux/hash.h> 17 + #include <linux/ns_common.h> 17 18 18 19 #include "kernfs-internal.h" 19 20 ··· 307 306 return parent; 308 307 } 309 308 309 + /* 310 + * kernfs_ns_id - return the namespace id for a given namespace 311 + * @ns: namespace tag (may be NULL) 312 + * 313 + * Use the 64-bit namespace id instead of raw pointers for hashing 314 + * and comparison to avoid leaking kernel addresses to userspace. 315 + */ 316 + static u64 kernfs_ns_id(const struct ns_common *ns) 317 + { 318 + return ns ? ns->ns_id : 0; 319 + } 320 + 310 321 /** 311 322 * kernfs_name_hash - calculate hash of @ns + @name 312 323 * @name: Null terminated string to hash ··· 329 316 static unsigned int kernfs_name_hash(const char *name, 330 317 const struct ns_common *ns) 331 318 { 332 - unsigned long hash = init_name_hash(ns); 319 + unsigned long hash = init_name_hash(kernfs_ns_id(ns)); 333 320 unsigned int len = strlen(name); 334 321 while (len--) 335 322 hash = partial_name_hash(*name++, hash); ··· 346 333 static int kernfs_name_compare(unsigned int hash, const char *name, 347 334 const struct ns_common *ns, const struct kernfs_node *kn) 348 335 { 336 + u64 ns_id = kernfs_ns_id(ns); 337 + u64 kn_ns_id = kernfs_ns_id(kn->ns); 338 + 349 339 if (hash < kn->hash) 350 340 return -1; 351 341 if (hash > kn->hash) 352 342 return 1; 353 - if (ns < kn->ns) 343 + if (ns_id < kn_ns_id) 354 344 return -1; 355 - if (ns > kn->ns) 345 + if (ns_id > kn_ns_id) 356 346 return 1; 357 347 return strcmp(name, kernfs_rcu_name(kn)); 358 348 } ··· 1219 1203 1220 1204 /* The kernfs node has been moved to a different namespace */ 1221 1205 if (parent && kernfs_ns_enabled(parent) && 1222 - kernfs_info(dentry->d_sb)->ns != kn->ns) 1206 + kernfs_ns_id(kernfs_info(dentry->d_sb)->ns) != kernfs_ns_id(kn->ns)) 1223 1207 goto out_bad; 1224 1208 1225 1209 up_read(&root->kernfs_rwsem); ··· 1791 1775 old_name = kernfs_rcu_name(kn); 1792 1776 if (!new_name) 1793 1777 new_name = old_name; 1794 - if ((old_parent == new_parent) && (kn->ns == new_ns) && 1778 + if ((old_parent == new_parent) && 1779 + (kernfs_ns_id(kn->ns) == kernfs_ns_id(new_ns)) && 1795 1780 (strcmp(old_name, new_name) == 0)) 1796 1781 goto out; /* nothing to rename */ 1797 1782 ··· 1878 1861 } 1879 1862 } 1880 1863 /* Skip over entries which are dying/dead or in the wrong namespace */ 1881 - while (pos && (!kernfs_active(pos) || pos->ns != ns)) { 1864 + while (pos && (!kernfs_active(pos) || 1865 + kernfs_ns_id(pos->ns) != kernfs_ns_id(ns))) { 1882 1866 struct rb_node *node = rb_next(&pos->rb); 1883 1867 if (!node) 1884 1868 pos = NULL; ··· 1900 1882 pos = NULL; 1901 1883 else 1902 1884 pos = rb_to_kn(node); 1903 - } while (pos && (!kernfs_active(pos) || pos->ns != ns)); 1885 + } while (pos && (!kernfs_active(pos) || 1886 + kernfs_ns_id(pos->ns) != kernfs_ns_id(ns))); 1904 1887 } 1905 1888 return pos; 1906 1889 }