Received: by 2002:a05:6358:489b:b0:bb:da1:e618 with SMTP id x27csp3612836rwn; Sat, 10 Sep 2022 16:12:34 -0700 (PDT) X-Google-Smtp-Source: AA6agR4jReHb/d4/qHn2eRHX/dhz7SRApdmHl/fSp3yYxKRhjbnQLEbTKnIU4kQXyrYR3S3/m1Jl X-Received: by 2002:a17:906:ee86:b0:741:89bc:27a1 with SMTP id wt6-20020a170906ee8600b0074189bc27a1mr14512306ejb.725.1662851554242; Sat, 10 Sep 2022 16:12:34 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1662851554; cv=none; d=google.com; s=arc-20160816; b=r5KqX4ntME+IZIxcbknc8+LTmJf8fnA0Xb4nFEttUJa6vAONoX/WWNp3N87ejdedcR oP2QTD/QmxNEtK9miDdD8mikkSfTz5dvuNRjOXPdhanFIH2f75bN0aESx9aklh1OV7b4 HDpcuUldI+z0gDwdPGA2S8y2XfXennrl61c0DcU5tXhpdKcruOcDAb5C4cCBe7kmxBVn waoghnZ2tPujjAYtzHFw5unT+96okjFeRnCJ3M/w6ZqK+R1T6eyOd2l1+pd0alN3+g02 ZSiSCJaatqpi1uX4g5Z4EVEJR9ehPQuuvQe3VAPpvxMQwPonZrlY9TXRZRkcBWyBq2PI 2GOQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:date:mime-version:references:subject:cc:to:from :dkim-signature:dkim-signature:message-id; bh=o5EroeZfB4NRvl63EpSxfqZIvT4RtM7/V27p+d+Wyos=; b=pJthDu8fh1e0f/QQ5GI0v/cpe0vXFuKfH/R+VG7iaa4c23Vq+rpduJ2kSBokk5aqcl bLDtpTPBFsBubh5/vMG8gej4gMx3njxI2O1m/pTJf9GXp8JnIMti0vbnjjVkBCHTkG7o R6TeKhx6HZR/GeHpxbsduoRHUebeoxT6vJ8P4V1fxtFFM70OU6/EikHjl4TLq6aMGrfw uP0kc6EplQUGZlw66+OfqvquItyYl/jKv8KXxfyz1vbfORdFI24aSipIyyrKhv0ZHwEY OaHpKgBfjht5WkSjA26pNSEiJ+kQ/+stihRA3F4EVt8fwVpF8ZlFg3LCTw7j2/y2AmOw dT8w== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linutronix.de header.s=2020 header.b=Lq9KRRbR; dkim=neutral (no key) header.i=@linutronix.de header.s=2020e; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=linutronix.de Return-Path: Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id r13-20020a05640251cd00b0044f2fb68fe6si4315607edd.495.2022.09.10.16.12.08; Sat, 10 Sep 2022 16:12:34 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@linutronix.de header.s=2020 header.b=Lq9KRRbR; dkim=neutral (no key) header.i=@linutronix.de header.s=2020e; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=linutronix.de Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230260AbiIJW2R (ORCPT + 99 others); Sat, 10 Sep 2022 18:28:17 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45770 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229982AbiIJW1o (ORCPT ); Sat, 10 Sep 2022 18:27:44 -0400 Received: from galois.linutronix.de (Galois.linutronix.de [193.142.43.55]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7AE703D5A0 for ; Sat, 10 Sep 2022 15:27:43 -0700 (PDT) Message-ID: <20220910222300.712668210@linutronix.de> DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020; t=1662848861; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: references:references; bh=o5EroeZfB4NRvl63EpSxfqZIvT4RtM7/V27p+d+Wyos=; b=Lq9KRRbRCX0Ak4qz8PNcmnbY1qO4QNuFUisqEyBcaC5V2qUJOYoGBlTG0ZgaRxHg4CRI78 G323bD21b5keHOyH+Je+VPkSF2+aXuajMFh8PlD4ejaIig7y9+BGqkcmuxmUMxLLk/ZJlG JS6hS0K9YILffaYp7z/VAm6Obqb3AkoU73qZcUAiAWbTV1m2enP0INBpikmYVyMVaQezS2 PfYjSVb4htLORkATadQbQAyA2evBdVOlOuDBZBLlSPgMEz2RTZ7fxXbAEbdtjPBSniWcni 9NnwSLKJaRWNi5Gt/y1iGVoJi5hOiwjcbTtph597AlCSKOiKvrVU/Cam5ML3jA== DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020e; t=1662848861; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: references:references; bh=o5EroeZfB4NRvl63EpSxfqZIvT4RtM7/V27p+d+Wyos=; b=sYK6VGhDAuF/Gqfwp7U/eUg7y/W1Byds18bfIvK6JOlosn9b09WsD0fVaMKF95jHZnCAym IpH7WSzU4DPAXiAQ== From: Thomas Gleixner To: LKML Cc: John Ogness , Petr Mladek , Sergey Senozhatsky , Steven Rostedt , Linus Torvalds , Peter Zijlstra , "Paul E. McKenney" , Daniel Vetter , Greg Kroah-Hartman , Helge Deller , Jason Wessel , Daniel Thompson Subject: [patch RFC 06/29] printk: Protect [un]register_console() with a mutex References: <20220910221947.171557773@linutronix.de> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Date: Sun, 11 Sep 2022 00:27:41 +0200 (CEST) X-Spam-Status: No, score=-4.4 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_MED,SPF_HELO_NONE, SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Unprotected list walks are a brilliant idea. Especially in the context of hotpluggable consoles. Signed-off-by: Thomas Gleixner --- include/linux/console.h | 30 ++++++++++++++++-- kernel/printk/printk.c | 79 ++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 93 insertions(+), 16 deletions(-) --- a/include/linux/console.h +++ b/include/linux/console.h @@ -157,10 +157,34 @@ struct console { struct console *next; }; -/* - * for_each_console() allows you to iterate on each console +#ifdef CONFIG_LOCKDEP +extern void lockdep_assert_console_list_lock_held(void); +#else +static inline void lockdep_assert_console_list_lock_held(void) { } +#endif + +extern void console_list_lock(void) __acquires(console_mutex); +extern void console_list_unlock(void) __releases(console_mutex); + +/** + * for_each_registered_console() - Iterator over registered consoles + * @con: struct console pointer used as loop cursor + * + * Requires console_list_lock to be held. Can only be invoked from + * preemptible context. + */ +#define for_each_registered_console(con) \ + lockdep_assert_console_list_lock_held(); \ + for (con = console_drivers; con != NULL; con = con->next) + +/** + * for_each_console() - Iterator over registered consoles + * @con: struct console pointer used as loop cursor + * + * Requires console_lock to be held which guarantees that the + * list is immutable. */ -#define for_each_console(con) \ +#define for_each_console(con) \ for (con = console_drivers; con != NULL; con = con->next) extern int console_set_on_cmdline; --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -79,10 +79,14 @@ int oops_in_progress; EXPORT_SYMBOL(oops_in_progress); /* - * console_sem protects the console_drivers list, and also - * provides serialisation for access to the entire console - * driver system. + * console_sem protects the console_drivers list, and also provides + * serialization for access to the entire console driver system. + * + * console_mutex serializes register/unregister. console_sem has to be + * taken for any list manipulation inside the console_mutex locked + * section to keep the console BKL machinery happy. */ +static DEFINE_MUTEX(console_mutex); static DEFINE_SEMAPHORE(console_sem); struct console *console_drivers; EXPORT_SYMBOL_GPL(console_drivers); @@ -103,6 +107,12 @@ static int __read_mostly suppress_panic_ static struct lockdep_map console_lock_dep_map = { .name = "console_lock" }; + +void lockdep_assert_console_list_lock_held(void) +{ + lockdep_assert_held(&console_mutex); +} + #endif enum devkmsg_log_bits { @@ -220,6 +230,26 @@ int devkmsg_sysctl_set_loglvl(struct ctl } #endif /* CONFIG_PRINTK && CONFIG_SYSCTL */ +/** + * console_list_lock - Lock the console list + * + * For non-console related list walks, e.g. procfs, sysfs... + */ +void console_list_lock(void) +{ + mutex_lock(&console_mutex); +} + +/** + * console_list_unlock - Unlock the console list + * + * Counterpart to console_list_lock() + */ +void console_list_unlock(void) +{ + mutex_unlock(&console_mutex); +} + /* * Helper macros to handle lockdep when locking/unlocking console_sem. We use * macros instead of functions so that _RET_IP_ contains useful information. @@ -2978,17 +3008,21 @@ struct tty_driver *console_device(int *i void console_stop(struct console *console) { __pr_flush(console, 1000, true); + console_list_lock(); console_lock(); console->flags &= ~CON_ENABLED; console_unlock(); + console_list_unlock(); } EXPORT_SYMBOL(console_stop); void console_start(struct console *console) { + console_list_lock(); console_lock(); console->flags |= CON_ENABLED; console_unlock(); + console_list_unlock(); __pr_flush(console, 1000, true); } EXPORT_SYMBOL(console_start); @@ -3081,6 +3115,8 @@ static void try_enable_default_console(s (con->flags & CON_BOOT) ? "boot" : "", \ con->name, con->index, ##__VA_ARGS__) +static int console_unregister_locked(struct console *console); + /* * The console driver calls this routine during kernel initialization * to register the console printing procedure with printk() and to @@ -3107,13 +3143,14 @@ void register_console(struct console *ne bool realcon_enabled = false; int err; - for_each_console(con) { + console_list_lock(); + for_each_registered_console(con) { if (WARN(con == newcon, "console '%s%d' already registered\n", con->name, con->index)) - return; + goto unlock; } - for_each_console(con) { + for_each_registered_console(con) { if (con->flags & CON_BOOT) bootcon_enabled = true; else @@ -3124,7 +3161,7 @@ void register_console(struct console *ne if (newcon->flags & CON_BOOT && realcon_enabled) { pr_info("Too late to register bootconsole %s%d\n", newcon->name, newcon->index); - return; + goto unlock; } /* @@ -3155,7 +3192,7 @@ void register_console(struct console *ne /* printk() messages are not printed to the Braille console. */ if (err || newcon->flags & CON_BRL) - return; + goto unlock; /* * If we have a bootconsole, and are switching to a real console, @@ -3209,14 +3246,17 @@ void register_console(struct console *ne if (bootcon_enabled && ((newcon->flags & (CON_CONSDEV | CON_BOOT)) == CON_CONSDEV) && !keep_bootcon) { - for_each_console(con) + for_each_console(con) { if (con->flags & CON_BOOT) - unregister_console(con); + console_unregister_locked(con); + } } +unlock: + console_list_unlock(); } EXPORT_SYMBOL(register_console); -int unregister_console(struct console *console) +static int console_unregister_locked(struct console *console) { struct console *con; int res; @@ -3269,6 +3309,16 @@ int unregister_console(struct console *c return res; } + +int unregister_console(struct console *console) +{ + int res; + + console_list_lock(); + res = console_unregister_locked(console); + console_list_unlock(); + return res; +} EXPORT_SYMBOL(unregister_console); /* @@ -3320,7 +3370,8 @@ static int __init printk_late_init(void) struct console *con; int ret; - for_each_console(con) { + console_list_lock(); + for_each_registered_console(con) { if (!(con->flags & CON_BOOT)) continue; @@ -3337,9 +3388,11 @@ static int __init printk_late_init(void) */ pr_warn("bootconsole [%s%d] uses init memory and must be disabled even before the real one is ready\n", con->name, con->index); - unregister_console(con); + console_unregister_locked(con); } } + console_list_unlock(); + ret = cpuhp_setup_state_nocalls(CPUHP_PRINTK_DEAD, "printk:dead", NULL, console_cpu_notify); WARN_ON(ret < 0);