From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 467B515A486; Mon, 29 Jan 2024 17:08:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706548113; cv=none; b=qox6WekTzWJiV8ItCxP74T/TokyxgzgL5Y9FSHkKI4PzkgdN5gUcTyNNL6qiBfYB6vzc7n3Ny8FbGJYZABJiCFuWWxnTu7/LWQEzmuTlACPwupPugn+00mxSoTE03dTr+JN9l5duUeYMb9hz/Ub41IptwpFOOZi0yESgURrRGGc= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706548113; c=relaxed/simple; bh=dP07iAzgHR0tatzzJuez4YsORbW1UPjPtsVJP4ILxUA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=pVwvehbKkskBI/jxoF2bOUSnUjB2oTtQt4Kx7osnQCAZyMKCU3bYHFW58d4q19J+QFDkzlBJPHnykrpjRZx1QvrPdPU1MUcyBl41abs7rdRW/RSEPVGF3+/wrkTc8f8mn4/xN2N8sd+ZtLYbH/B6oske1/t3lDESNB4VhdlU134= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.b=UjE8aJIF; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.b="UjE8aJIF" Received: by smtp.kernel.org (Postfix) with ESMTPSA id BEA83C433F1; Mon, 29 Jan 2024 17:08:31 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=linuxfoundation.org; s=korg; t=1706548111; bh=dP07iAzgHR0tatzzJuez4YsORbW1UPjPtsVJP4ILxUA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=UjE8aJIFc4W6QpiwFw1keOUFMG+MMbRm55OZUqyczNmj/WEldiPsGTkX0LI1z7muD rPZm7DNrAh6eTaI6VRgkzJu4RAnS/VS/oAh8yEdiitBx1kmCKwtKRqrpHCCGKca7jw 3gy8lziQWpsJ6yVgwDGYKasyx/zz8nsWCNeoX7bI= From: Greg Kroah-Hartman To: stable@vger.kernel.org Cc: Greg Kroah-Hartman , patches@lists.linux.dev, Yury Norov , Hugo Villeneuve Subject: [PATCH 6.7 134/346] serial: sc16is7xx: fix invalid sc16is7xx_lines bitfield in case of probe error Date: Mon, 29 Jan 2024 09:02:45 -0800 Message-ID: <20240129170020.329767435@linuxfoundation.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240129170016.356158639@linuxfoundation.org> References: <20240129170016.356158639@linuxfoundation.org> User-Agent: quilt/0.67 X-stable: review X-Patchwork-Hint: ignore Precedence: bulk X-Mailing-List: patches@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 6.7-stable review patch. If anyone has any objections, please let me know. ------------------ From: Hugo Villeneuve commit 8a1060ce974919f2a79807527ad82ac39336eda2 upstream. If an error occurs during probing, the sc16is7xx_lines bitfield may be left in a state that doesn't represent the correct state of lines allocation. For example, in a system with two SC16 devices, if an error occurs only during probing of channel (port) B of the second device, sc16is7xx_lines final state will be 00001011b instead of the expected 00000011b. This is caused in part because of the "i--" in the for/loop located in the out_ports: error path. Fix this by checking the return value of uart_add_one_port() and set line allocation bit only if this was successful. This allows the refactor of the obfuscated for(i--...) loop in the error path, and properly call uart_remove_one_port() only when needed, and properly unset line allocation bits. Also use same mechanism in remove() when calling uart_remove_one_port(). Fixes: c64349722d14 ("sc16is7xx: support multiple devices") Cc: Cc: Yury Norov Signed-off-by: Hugo Villeneuve Link: https://lore.kernel.org/r/20231221231823.2327894-2-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sc16is7xx.c | 44 ++++++++++++++++------------------------- 1 file changed, 18 insertions(+), 26 deletions(-) --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -408,19 +408,6 @@ static void sc16is7xx_port_update(struct regmap_update_bits(one->regmap, reg, mask, val); } -static int sc16is7xx_alloc_line(void) -{ - int i; - - BUILD_BUG_ON(SC16IS7XX_MAX_DEVS > BITS_PER_LONG); - - for (i = 0; i < SC16IS7XX_MAX_DEVS; i++) - if (!test_and_set_bit(i, &sc16is7xx_lines)) - break; - - return i; -} - static void sc16is7xx_power(struct uart_port *port, int on) { sc16is7xx_port_update(port, SC16IS7XX_IER_REG, @@ -1551,6 +1538,13 @@ static int sc16is7xx_probe(struct device SC16IS7XX_IOCONTROL_SRESET_BIT); for (i = 0; i < devtype->nr_uart; ++i) { + s->p[i].port.line = find_first_zero_bit(&sc16is7xx_lines, + SC16IS7XX_MAX_DEVS); + if (s->p[i].port.line >= SC16IS7XX_MAX_DEVS) { + ret = -ERANGE; + goto out_ports; + } + /* Initialize port data */ s->p[i].port.dev = dev; s->p[i].port.irq = irq; @@ -1570,14 +1564,8 @@ static int sc16is7xx_probe(struct device s->p[i].port.rs485_supported = sc16is7xx_rs485_supported; s->p[i].port.ops = &sc16is7xx_ops; s->p[i].old_mctrl = 0; - s->p[i].port.line = sc16is7xx_alloc_line(); s->p[i].regmap = regmaps[i]; - if (s->p[i].port.line >= SC16IS7XX_MAX_DEVS) { - ret = -ENOMEM; - goto out_ports; - } - mutex_init(&s->p[i].efr_lock); ret = uart_get_rs485_mode(&s->p[i].port); @@ -1595,8 +1583,13 @@ static int sc16is7xx_probe(struct device kthread_init_work(&s->p[i].tx_work, sc16is7xx_tx_proc); kthread_init_work(&s->p[i].reg_work, sc16is7xx_reg_proc); kthread_init_delayed_work(&s->p[i].ms_work, sc16is7xx_ms_proc); + /* Register port */ - uart_add_one_port(&sc16is7xx_uart, &s->p[i].port); + ret = uart_add_one_port(&sc16is7xx_uart, &s->p[i].port); + if (ret) + goto out_ports; + + set_bit(s->p[i].port.line, &sc16is7xx_lines); /* Enable EFR */ sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_LCR_REG, @@ -1654,10 +1647,9 @@ static int sc16is7xx_probe(struct device #endif out_ports: - for (i--; i >= 0; i--) { - uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port); - clear_bit(s->p[i].port.line, &sc16is7xx_lines); - } + for (i = 0; i < devtype->nr_uart; i++) + if (test_and_clear_bit(s->p[i].port.line, &sc16is7xx_lines)) + uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port); kthread_stop(s->kworker_task); @@ -1679,8 +1671,8 @@ static void sc16is7xx_remove(struct devi for (i = 0; i < s->devtype->nr_uart; i++) { kthread_cancel_delayed_work_sync(&s->p[i].ms_work); - uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port); - clear_bit(s->p[i].port.line, &sc16is7xx_lines); + if (test_and_clear_bit(s->p[i].port.line, &sc16is7xx_lines)) + uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port); sc16is7xx_power(&s->p[i].port, 0); }