printk changes for 7.3

-----BEGIN PGP SIGNATURE-----
 
 iQJPBAABCAA5FiEESH4wyp42V4tXvYsjUqAMR0iAlPIFAmqFYyEbFIAAAAAABAAO
 bWFudTIsMi41KzEuMTIsMiwyAAoJEFKgDEdIgJTyAlwP+gL59ZVbiHyzTRlVMetf
 6wYxjnM4B0Efe/BC/526sVZ0ZMvCaT8oNgRCUDYWEBmB1NWmxieQ2YEy5yg9N/3v
 nDgUenq7gdVLPzv+n3Xrb2ZqzsTssnP6iD/hi4S2uD2/1Bphu0f13K2yLHcgfsgR
 EKqj2y3bOR4b6Z2YyNWA3wcbbYHccIt1c6pM0t2IJi9L5gbncwfzqdG5qdsG/Ioi
 Vbd7CVyDTnPArrDNOMe19cvDNxuJcpogNUaAUjtCl2rWqSFFLC9w06LphrjmZ4L2
 lpS5C9VVPlsQVxAEoeYQ13v6glL1Iakj2nhKz/uDq5U0hMY5BxRKZNdh96aMEbhZ
 Yy4DtvmXOQsPBERzjYvMhIK92kTcLW/VgKyt5Ug62IL7r4P0xCtNvdFCRu28PISu
 pBzzwXcbiSPxVW5iXP/cR6NUsDrty3W3LHlkj1C1SE2RAnJk7XUQRjvpH5hgtzUM
 A5EDrAO+6MB/fUBzpC1YAwXBiNSZjrBQK9BReybfJA2yYLIrMz2C27+gz5PCAEp8
 brozPVQUN9KKL0ZY53mJ+De8x/U9KUtdr8ejVanrOjefVrWt/fzWwbii7UWrve72
 u2bfUQ3V4es9raS4BfVD1OiImkrQIBJQNNRX07o/MgWv7yrxAWBHDXQu3X1KkjpV
 4pKsBEI4s8/mTNkT/MiE6YAo
 =Uw8w
 -----END PGP SIGNATURE-----

Merge tag 'printk-for-7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/printk/linux

Pull printk updates from Petr Mladek:

 - Prevent a possible out-of-bound access and a use-after-free in rather
   theoretical situations

 - Make no_hash_pointers take effect early

 - Some fixes and clean up of the ratelimit KUnit test

* tag 'printk-for-7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/printk/linux:
  printk: Handle pre-enabled consoles in the top-level register_console()
  printk: Fix possible console use-after-free
  lib/tests: test_ratelimit: fix stress test thread lifecycle and leak
  lib/vsprintf: Make no_hash_pointers take effect early
This commit is contained in:
Linus Torvalds 2026-08-20 15:34:31 -07:00
commit 77ae27fd98
3 changed files with 40 additions and 23 deletions

View File

@ -3264,10 +3264,8 @@ static bool console_flush_one_record(bool do_cond_resched, u64 *next_seq, bool *
if (flags & CON_NBCON) {
progress = nbcon_legacy_emit_next_record(con, handover, cookie,
!do_cond_resched);
printk_seq = nbcon_seq_read(con);
} else {
progress = console_emit_next_record(con, handover, cookie);
printk_seq = con->seq;
}
/*
@ -3277,6 +3275,15 @@ static bool console_flush_one_record(bool do_cond_resched, u64 *next_seq, bool *
if (*handover)
goto fail;
/*
* @con can be used here now that it is certain that this
* context is still holding the SRCU read lock.
*/
if (flags & CON_NBCON)
printk_seq = nbcon_seq_read(con);
else
printk_seq = con->seq;
/* Track the next of the highest seq flushed. */
if (printk_seq > *next_seq)
*next_seq = printk_seq;
@ -3890,9 +3897,6 @@ static int console_call_setup(struct console *newcon, char *options)
* the newly registered console with any of the ones selected
* by either the command line or add_preferred_console() and
* setup/enable it.
*
* Care need to be taken with consoles that are statically
* enabled such as netconsole
*/
static int try_enable_preferred_console(struct console *newcon,
bool user_specified)
@ -3933,14 +3937,6 @@ static int try_enable_preferred_console(struct console *newcon,
return 0;
}
/*
* Some consoles, such as pstore and netconsole, can be enabled even
* without matching. Accept the pre-enabled consoles only when match()
* and setup() had a chance to be called.
*/
if (newcon->flags & CON_ENABLED && c->user_specified == user_specified)
return 0;
return -ENOENT;
}
@ -4123,6 +4119,14 @@ void register_console(struct console *newcon)
if (err == -ENOENT)
err = try_enable_preferred_console(newcon, false);
/*
* Some consoles, such as pstore and netconsole, can be enabled even
* without matching. Accept them at this stage when they had a chance
* to match() and call setup().
*/
if (err == -ENOENT && (newcon->flags & CON_ENABLED))
err = 0;
/* printk() messages are not printed to the Braille console. */
if (err || newcon->flags & CON_BRL) {
if (newcon->flags & CON_NBCON)

View File

@ -68,7 +68,6 @@ static void test_ratelimit_smoke(struct kunit *test)
static struct ratelimit_state stressrl = RATELIMIT_STATE_INIT_FLAGS("stressrl", HZ / 10, 3,
RATELIMIT_MSG_ON_RELEASE);
static int doneflag;
static const int stress_duration = 2 * HZ;
struct stress_kthread {
@ -84,9 +83,8 @@ static int test_ratelimit_stress_child(void *arg)
struct stress_kthread *sktp = arg;
set_user_nice(current, MAX_NICE);
WARN_ON_ONCE(!sktp->tp);
while (!READ_ONCE(doneflag)) {
while (!kthread_should_stop()) {
sktp->nattempts++;
if (___ratelimit(&stressrl, __func__))
sktp->nunlimited++;
@ -105,26 +103,37 @@ static void test_ratelimit_stress(struct kunit *test)
const int n_stress_kthread = cpumask_weight(cpu_online_mask);
struct stress_kthread skt = { 0 };
struct stress_kthread *sktp = kzalloc_objs(*sktp, n_stress_kthread);
int n_started = 0;
KUNIT_EXPECT_NOT_NULL_MSG(test, sktp, "Memory allocation failure");
KUNIT_ASSERT_NOT_NULL_MSG(test, sktp, "Memory allocation failure");
for (i = 0; i < n_stress_kthread; i++) {
sktp[i].tp = kthread_run(test_ratelimit_stress_child, &sktp[i], "%s/%i",
"test_ratelimit_stress_child", i);
KUNIT_EXPECT_NOT_NULL_MSG(test, sktp, "kthread creation failure");
if (IS_ERR(sktp[i].tp)) {
KUNIT_FAIL(test, "kthread_run failed: %ld", PTR_ERR(sktp[i].tp));
goto out_stop;
}
n_started++;
pr_alert("Spawned test_ratelimit_stress_child %d\n", i);
}
schedule_timeout_idle(stress_duration);
WRITE_ONCE(doneflag, 1);
for (i = 0; i < n_stress_kthread; i++) {
out_stop:
for (i = 0; i < n_started; i++) {
kthread_stop(sktp[i].tp);
skt.nattempts += sktp[i].nattempts;
skt.nunlimited += sktp[i].nunlimited;
skt.nlimited += sktp[i].nlimited;
skt.nmissed += sktp[i].nmissed;
}
KUNIT_ASSERT_EQ_MSG(test, skt.nunlimited + skt.nlimited, skt.nattempts,
"Outcomes not equal to attempts");
KUNIT_ASSERT_EQ_MSG(test, skt.nlimited, skt.nmissed, "Misses not equal to limits");
if (n_started == n_stress_kthread) {
KUNIT_ASSERT_EQ_MSG(test, skt.nunlimited + skt.nlimited, skt.nattempts,
"Outcomes not equal to attempts");
KUNIT_ASSERT_EQ_MSG(test, skt.nlimited, skt.nmissed,
"Misses not equal to limits");
}
kfree(sktp);
}
static struct kunit_case ratelimit_test_cases[] = {

View File

@ -2360,6 +2360,9 @@ void __init hash_pointers_finalize(bool slub_debug)
static int __init hash_pointers_mode_parse(char *str)
{
/* Avoid stale no_hash_pointers state when hash_pointers overrides it */
no_hash_pointers = false;
if (!str) {
pr_warn("Hash pointers mode empty; falling back to auto.\n");
hash_pointers_mode = HASH_PTR_AUTO;
@ -2369,6 +2372,7 @@ static int __init hash_pointers_mode_parse(char *str)
} else if (strcmp(str, "never") == 0) {
pr_info("Hash pointers mode set to never.\n");
hash_pointers_mode = HASH_PTR_NEVER;
no_hash_pointers = true;
} else if (strcmp(str, "always") == 0) {
pr_info("Hash pointers mode set to always.\n");
hash_pointers_mode = HASH_PTR_ALWAYS;