Commit de1c831a authored by Kees Cook's avatar Kees Cook Committed by Petr Mladek
Browse files

slab: Decouple slab_debug and no_hash_pointers

Some system owners use slab_debug=FPZ (or similar) as a hardening option,
but do not want to be forced into having kernel addresses exposed due
to the implicit "no_hash_pointers" boot param setting.[1]

Introduce the "hash_pointers" boot param, which defaults to "auto"
(the current behavior), but also includes "always" (forcing on hashing
even when "slab_debug=..." is defined), and "never". The existing
"no_hash_pointers" boot param becomes an alias for "hash_pointers=never".

This makes it possible to boot with "slab_debug=FPZ hash_pointers=always".

Link: https://github.com/KSPP/linux/issues/368

 [1]
Fixes: 79270291 ("slub: force on no_hash_pointers when slub_debug is enabled")
Co-developed-by: default avatarSergio Perez Gonzalez <sperezglz@gmail.com>
Signed-off-by: default avatarSergio Perez Gonzalez <sperezglz@gmail.com>
Acked-by: default avatarVlastimil Babka <vbabka@suse.cz>
Acked-by: default avatarDavid Rientjes <rientjes@google.com>
Reviewed-by: default avatarBagas Sanjaya <bagasdotme@gmail.com>
Signed-off-by: default avatarKees Cook <kees@kernel.org>
Reviewed-by: default avatarHarry Yoo <harry.yoo@oracle.com>
Acked-by: default avatarRafael Aquini <raquini@redhat.com>
Tested-by: default avatarPetr Mladek <pmladek@suse.com>
Reviewed-by: default avatarPetr Mladek <pmladek@suse.com>
Link: https://patch.msgid.link/20250415170232.it.467-kees@kernel.org


[kees@kernel.org: Add note about hash_pointers into slab_debug kernel parameter documentation.]
Signed-off-by: default avatarPetr Mladek <pmladek@suse.com>
parent af54a3a1
Loading
Loading
Loading
Loading
+26 −12
Original line number Diff line number Diff line
@@ -1798,6 +1798,27 @@
			backtraces on all cpus.
			Format: 0 | 1

	hash_pointers=
			[KNL,EARLY]
			By default, when pointers are printed to the console
			or buffers via the %p format string, that pointer is
			"hashed", i.e. obscured by hashing the pointer value.
			This is a security feature that hides actual kernel
			addresses from unprivileged users, but it also makes
			debugging the kernel more difficult since unequal
			pointers can no longer be compared. The choices are:
			Format: { auto | always | never }
			Default: auto

			auto   - Hash pointers unless slab_debug is enabled.
			always - Always hash pointers (even if slab_debug is
				 enabled).
			never  - Never hash pointers. This option should only
				 be specified when debugging the kernel. Do
				 not use on production kernels. The boot
				 param "no_hash_pointers" is an alias for
				 this mode.

	hashdist=	[KNL,NUMA] Large hashes allocated during boot
			are distributed across NUMA nodes.  Defaults on
			for 64-bit NUMA, off otherwise.
@@ -4120,18 +4141,7 @@

	no_hash_pointers
			[KNL,EARLY]
			Force pointers printed to the console or buffers to be
			unhashed.  By default, when a pointer is printed via %p
			format string, that pointer is "hashed", i.e. obscured
			by hashing the pointer value.  This is a security feature
			that hides actual kernel addresses from unprivileged
			users, but it also makes debugging the kernel more
			difficult since unequal pointers can no longer be
			compared.  However, if this command-line option is
			specified, then all normal pointers will have their true
			value printed. This option should only be specified when
			debugging the kernel.  Please do not use on production
			kernels.
			Alias for "hash_pointers=never".

	nohibernate	[HIBERNATION] Disable hibernation and resume.

@@ -6481,6 +6491,10 @@
			Documentation/mm/slub.rst.
			(slub_debug legacy name also accepted for now)

			Using this option implies the "no_hash_pointers"
			option which can be undone by adding the
			"hash_pointers=always" option.

	slab_max_order= [MM]
			Determines the maximum allowed order for slabs.
			A high setting may cause OOMs due to memory
+1 −1
Original line number Diff line number Diff line
@@ -22,7 +22,7 @@ __scanf(2, 0) int vsscanf(const char *, const char *, va_list);

/* These are for specific cases, do not use without real need */
extern bool no_hash_pointers;
int no_hash_pointers_enable(char *str);
void hash_pointers_finalize(bool slub_debug);

/* Used for Rust formatting ('%pA') */
char *rust_fmt_argument(char *buf, char *end, const void *ptr);
+57 −4
Original line number Diff line number Diff line
@@ -60,6 +60,20 @@
bool no_hash_pointers __ro_after_init;
EXPORT_SYMBOL_GPL(no_hash_pointers);

/*
 * Hashed pointers policy selected by "hash_pointers=..." boot param
 *
 * `auto`   - Hashed pointers enabled unless disabled by slub_debug_enabled=true
 * `always` - Hashed pointers enabled unconditionally
 * `never`  - Hashed pointers disabled unconditionally
 */
enum hash_pointers_policy {
	HASH_PTR_AUTO = 0,
	HASH_PTR_ALWAYS,
	HASH_PTR_NEVER
};
static enum hash_pointers_policy hash_pointers_mode __initdata;

noinline
static unsigned long long simple_strntoull(const char *startp, char **endp, unsigned int base, size_t max_chars)
{
@@ -2271,12 +2285,23 @@ char *resource_or_range(const char *fmt, char *buf, char *end, void *ptr,
	return resource_string(buf, end, ptr, spec, fmt);
}

int __init no_hash_pointers_enable(char *str)
void __init hash_pointers_finalize(bool slub_debug)
{
	if (no_hash_pointers)
		return 0;

	switch (hash_pointers_mode) {
	case HASH_PTR_ALWAYS:
		no_hash_pointers = false;
		break;
	case HASH_PTR_NEVER:
		no_hash_pointers = true;
		break;
	case HASH_PTR_AUTO:
	default:
		no_hash_pointers = slub_debug;
		break;
	}

	if (!no_hash_pointers)
		return;

	pr_warn("**********************************************************\n");
	pr_warn("**   NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE   **\n");
@@ -2289,11 +2314,39 @@ int __init no_hash_pointers_enable(char *str)
	pr_warn("** the kernel, report this immediately to your system   **\n");
	pr_warn("** administrator!                                       **\n");
	pr_warn("**                                                      **\n");
	pr_warn("** Use hash_pointers=always to force this mode off      **\n");
	pr_warn("**                                                      **\n");
	pr_warn("**   NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE   **\n");
	pr_warn("**********************************************************\n");
}

static int __init hash_pointers_mode_parse(char *str)
{
	if (!str) {
		pr_warn("Hash pointers mode empty; falling back to auto.\n");
		hash_pointers_mode = HASH_PTR_AUTO;
	} else if (strncmp(str, "auto", 4) == 0)   {
		pr_info("Hash pointers mode set to auto.\n");
		hash_pointers_mode = HASH_PTR_AUTO;
	} else if (strncmp(str, "never", 5) == 0) {
		pr_info("Hash pointers mode set to never.\n");
		hash_pointers_mode = HASH_PTR_NEVER;
	} else if (strncmp(str, "always", 6) == 0) {
		pr_info("Hash pointers mode set to always.\n");
		hash_pointers_mode = HASH_PTR_ALWAYS;
	} else {
		pr_warn("Unknown hash_pointers mode '%s' specified; assuming auto.\n", str);
		hash_pointers_mode = HASH_PTR_AUTO;
	}

	return 0;
}
early_param("hash_pointers", hash_pointers_mode_parse);

static int __init no_hash_pointers_enable(char *str)
{
	return hash_pointers_mode_parse("never");
}
early_param("no_hash_pointers", no_hash_pointers_enable);

/*
+2 −3
Original line number Diff line number Diff line
@@ -6314,9 +6314,8 @@ void __init kmem_cache_init(void)
	if (debug_guardpage_minorder())
		slub_max_order = 0;

	/* Print slub debugging pointers without hashing */
	if (__slub_debug_enabled())
		no_hash_pointers_enable(NULL);
	/* Inform pointer hashing choice about slub debugging state. */
	hash_pointers_finalize(__slub_debug_enabled());

	kmem_cache_node = &boot_kmem_cache_node;
	kmem_cache = &boot_kmem_cache;