public inbox for stable@vger.kernel.org
 help / color / mirror / Atom feed
From: Sasha Levin <sashal@kernel.org>
To: patches@lists.linux.dev, stable@vger.kernel.org
Cc: Jakub Lewalski <jakub.lewalski@nokia.com>,
	Elodie Decerle <elodie.decerle@nokia.com>,
	Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	Sasha Levin <sashal@kernel.org>,
	jacmet@sunsite.dk, linux-serial@vger.kernel.org
Subject: [PATCH AUTOSEL 5.4 4/7] tty: serial: uartlite: register uart driver in init
Date: Mon,  9 Jun 2025 09:48:37 -0400	[thread overview]
Message-ID: <20250609134840.1345797-4-sashal@kernel.org> (raw)
In-Reply-To: <20250609134840.1345797-1-sashal@kernel.org>

From: Jakub Lewalski <jakub.lewalski@nokia.com>

[ Upstream commit 6bd697b5fc39fd24e2aa418c7b7d14469f550a93 ]

When two instances of uart devices are probing, a concurrency race can
occur. If one thread calls uart_register_driver function, which first
allocates and assigns memory to 'uart_state' member of uart_driver
structure, the other instance can bypass uart driver registration and
call ulite_assign. This calls uart_add_one_port, which expects the uart
driver to be fully initialized. This leads to a kernel panic due to a
null pointer dereference:

[    8.143581] BUG: kernel NULL pointer dereference, address: 00000000000002b8
[    8.156982] #PF: supervisor write access in kernel mode
[    8.156984] #PF: error_code(0x0002) - not-present page
[    8.156986] PGD 0 P4D 0
...
[    8.180668] RIP: 0010:mutex_lock+0x19/0x30
[    8.188624] Call Trace:
[    8.188629]  ? __die_body.cold+0x1a/0x1f
[    8.195260]  ? page_fault_oops+0x15c/0x290
[    8.209183]  ? __irq_resolve_mapping+0x47/0x80
[    8.209187]  ? exc_page_fault+0x64/0x140
[    8.209190]  ? asm_exc_page_fault+0x22/0x30
[    8.209196]  ? mutex_lock+0x19/0x30
[    8.223116]  uart_add_one_port+0x60/0x440
[    8.223122]  ? proc_tty_register_driver+0x43/0x50
[    8.223126]  ? tty_register_driver+0x1ca/0x1e0
[    8.246250]  ulite_probe+0x357/0x4b0 [uartlite]

To prevent it, move uart driver registration in to init function. This
will ensure that uart_driver is always registered when probe function
is called.

Signed-off-by: Jakub Lewalski <jakub.lewalski@nokia.com>
Signed-off-by: Elodie Decerle <elodie.decerle@nokia.com>
Link: https://lore.kernel.org/r/20250331160732.2042-1-elodie.decerle@nokia.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---

Based on my comprehensive analysis, I can now provide a definitive
assessment:

**YES**

## Extensive Analysis

This commit **should be backported** to stable kernel trees. Here's my
detailed analysis:

### **1. Critical Bug Fix Nature**

The commit fixes a **serious race condition** that causes kernel panics
with NULL pointer dereferences. The stack trace in the commit message
shows:

```
[    8.143581] BUG: kernel NULL pointer dereference, address:
00000000000002b8
[    8.180668] RIP: 0010:mutex_lock+0x19/0x30
[    8.223116]  uart_add_one_port+0x60/0x440
```

This is a **critical system stability issue** that can crash the kernel
during device initialization.

### **2. Race Condition Analysis**

**The Problem:**
- **Before the fix:** `uart_register_driver()` was called in
  `ulite_probe()` (lines 883-891 in current code)
- **Race scenario:** When two UART devices probe simultaneously:
  1. Thread 1 calls `uart_register_driver()` and starts allocating
     `uart_driver.state`
  2. Thread 2 sees `!ulite_uart_driver.state` as false, bypasses
     registration, and calls `ulite_assign()`
  3. Thread 2's `ulite_assign()` calls `uart_add_one_port()` (line 678)
     expecting a fully initialized driver
  4. Thread 1's registration hasn't completed, causing NULL pointer
     dereference in `uart_add_one_port()`

**The Fix:**
- **After the fix:** `uart_register_driver()` is moved to `ulite_init()`
  ensuring:
  1. Driver registration completes **before** any platform driver
     registration
  2. **No race window** exists between driver registration and device
     probing
  3. All probe calls find a **fully initialized** uart driver

### **3. Code Changes Analysis**

**Changes in `ulite_init()`:**
```c
static int __init ulite_init(void)
{
+   int ret;
+
+   pr_debug("uartlite: calling uart_register_driver()\n");
+   ret = uart_register_driver(&ulite_uart_driver);
+   if (ret)
+       return ret;

    pr_debug("uartlite: calling platform_driver_register()\n");
- return platform_driver_register(&ulite_platform_driver);
+   ret = platform_driver_register(&ulite_platform_driver);
+   if (ret)
+       uart_unregister_driver(&ulite_uart_driver);
+
+   return ret;
}
```

**Changes in `ulite_probe()`:**
```c
// REMOVED the problematic race-prone code:
- if (!ulite_uart_driver.state) {
- dev_dbg(&pdev->dev, "uartlite: calling uart_register_driver()\n");
- ret = uart_register_driver(&ulite_uart_driver);
- if (ret < 0) {
- dev_err(&pdev->dev, "Failed to register driver\n");
- clk_disable_unprepare(pdata->clk);
- return ret;
- }
- }
```

**Changes in `ulite_exit()`:**
```c
static void __exit ulite_exit(void)
{
    platform_driver_unregister(&ulite_platform_driver);
- if (ulite_uart_driver.state)
- uart_unregister_driver(&ulite_uart_driver);
+   uart_unregister_driver(&ulite_uart_driver);
}
```

### **4. Follows Kernel Best Practices**

The fix aligns with **established kernel patterns**. My analysis of
other UART drivers shows that well-architected drivers like 8250,
altera, atmel, and liteuart all register their uart drivers in `__init`
functions, not probe functions. This architectural pattern:

- **Separates concerns:** Driver registration (what) vs device binding
  (when)
- **Eliminates races:** Driver available before any device probing
- **Improves reliability:** Predictable initialization order

### **5. Stable Tree Suitability Criteria**

✅ **Fixes important bug:** Kernel panic with NULL pointer dereference
✅ **Small and contained:** Moves existing code, no new functionality
✅ **Minimal risk:** Follows established kernel patterns
✅ **Clear side effects:** Better initialization order, no regressions
expected
✅ **Confined to subsystem:** Only affects uartlite driver
✅ **Important for users:** Prevents boot crashes on Xilinx platforms

### **6. Similar Historical Pattern**

This fix is very similar to **Similar Commit #5** (Status: YES) which
also fixed race conditions in TTY/serial code by ensuring proper
initialization order. Both commits:
- Fix race conditions in serial/TTY subsystem
- Make architectural improvements to initialization order
- Prevent NULL pointer dereferences
- Have clear stable tree value

### **7. Low Regression Risk**

The change is **architecturally sound** because:
- It ensures `uart_register_driver()` completes before any
  `uart_add_one_port()` calls
- Proper error handling ensures cleanup if platform driver registration
  fails
- The exit sequence properly reverses the initialization sequence
- No functional behavior changes, only timing/ordering improvements

### **Conclusion**

This commit fixes a **critical kernel panic** caused by a race condition
in the uartlite driver. The fix is **small, contained, and follows
established kernel patterns**. It has **minimal regression risk** and
provides **significant stability improvements** for users of Xilinx UART
devices. The architectural change from probe-time to init-time driver
registration is a **best practice** that eliminates the race condition
entirely.

 drivers/tty/serial/uartlite.c | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c
index 9a4049c894f7a..e323e9c0a321e 100644
--- a/drivers/tty/serial/uartlite.c
+++ b/drivers/tty/serial/uartlite.c
@@ -874,16 +874,25 @@ static struct platform_driver ulite_platform_driver = {
 
 static int __init ulite_init(void)
 {
+	int ret;
+
+	pr_debug("uartlite: calling uart_register_driver()\n");
+	ret = uart_register_driver(&ulite_uart_driver);
+	if (ret)
+		return ret;
 
 	pr_debug("uartlite: calling platform_driver_register()\n");
-	return platform_driver_register(&ulite_platform_driver);
+	ret = platform_driver_register(&ulite_platform_driver);
+	if (ret)
+		uart_unregister_driver(&ulite_uart_driver);
+
+	return ret;
 }
 
 static void __exit ulite_exit(void)
 {
 	platform_driver_unregister(&ulite_platform_driver);
-	if (ulite_uart_driver.state)
-		uart_unregister_driver(&ulite_uart_driver);
+	uart_unregister_driver(&ulite_uart_driver);
 }
 
 module_init(ulite_init);
-- 
2.39.5


  parent reply	other threads:[~2025-06-09 13:48 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-06-09 13:48 [PATCH AUTOSEL 5.4 1/7] md/md-bitmap: fix dm-raid max_write_behind setting Sasha Levin
2025-06-09 13:48 ` [PATCH AUTOSEL 5.4 2/7] iio: pressure: zpa2326: Use aligned_s64 for the timestamp Sasha Levin
2025-06-09 13:48 ` [PATCH AUTOSEL 5.4 3/7] usb: potential integer overflow in usbg_make_tpg() Sasha Levin
2025-06-09 13:48 ` Sasha Levin [this message]
2025-06-09 13:48 ` [PATCH AUTOSEL 5.4 5/7] usb: Add checks for snprintf() calls in usb_alloc_dev() Sasha Levin
2025-06-09 13:48 ` [PATCH AUTOSEL 5.4 6/7] usb: cdc-wdm: avoid setting WDM_READ for ZLP-s Sasha Levin
2025-06-09 13:48 ` [PATCH AUTOSEL 5.4 7/7] usb: typec: displayport: Receive DP Status Update NAK request exit dp altmode Sasha Levin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250609134840.1345797-4-sashal@kernel.org \
    --to=sashal@kernel.org \
    --cc=elodie.decerle@nokia.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=jacmet@sunsite.dk \
    --cc=jakub.lewalski@nokia.com \
    --cc=linux-serial@vger.kernel.org \
    --cc=patches@lists.linux.dev \
    --cc=stable@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox