[PATCH v2] tty: n_tty: use kvzalloc/kvfree for line discipline data

Xin Chen posted 1 patch 1 month, 1 week ago
drivers/tty/n_tty.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
[PATCH v2] tty: n_tty: use kvzalloc/kvfree for line discipline data
Posted by Xin Chen 1 month, 1 week ago
BT enable fails intermittently with -ETIMEDOUT (-110).  The kernel log
shows the HCI Read Local Version command was sent and the firmware
replied with status 0x00 (logged by hci_req_cmd_complete() BT_DBG),
but the waiter in __hci_cmd_sync_sk() never woke up and timed out
after 10 s:

  bluetooth hci0: Opcode 0xfc00              // __hci_cmd_sync_sk
  bluetooth hci0: opcode 0xfc00 plen 1       // hci_cmd_sync_add
  bluetooth hci0: skb len 4                  // hci_cmd_sync_alloc
  bluetooth hci0: length 1                   // hci_req_sync_run
  Bluetooth: hci0 cmd_cnt 1 cmd queued 1     // hci_cmd_work
  Bluetooth: hci0 type 1 len 4               // hci_send_frame
  Bluetooth: opcode 0xfc00 status 0x00       // hci_req_cmd_complete
  <-- req_skb NULL: req_complete_skb not set,
      hci_cmd_sync_complete() never called,
      req_status stays HCI_REQ_PEND            -->
  <-- 10 s later: wait_event_interruptible_timeout expires -->
  bluetooth hci0: end: err -110              // __hci_cmd_sync_sk

The root cause is that hci_send_cmd_sync() clones the sent command
into hdev->req_skb so that hci_req_cmd_complete() can locate the
registered completion callback.  Under memory pressure this
skb_clone() fails, leaving hdev->req_skb NULL.  The firmware reply
is received and processed, but hci_req_cmd_complete() finds NULL
req_skb, so hci_cmd_sync_complete() is never called, req_status
stays HCI_REQ_PEND, and the waiter times out with -ETIMEDOUT.

The memory pressure is caused by n_tty_open().  When a BT UART
transport is opened, serdev_device_open() may be called multiple
times in quick succession, each triggering n_tty_open().  n_tty_open()
uses vzalloc() for the ~10 KB n_tty_data structure, which always
allocates page-by-page from the buddy order-0 free list.  Repeated
vzalloc() calls drain enough order-0 pages that the subsequent
skb_clone(GFP_KERNEL) in hci_send_cmd_sync() cannot get a page.

Replace vzalloc/vfree with kvzalloc_obj/kvfree.  kvzalloc_obj() tries
kmalloc first and falls back to vmalloc only on failure.  The
~10 KB n_tty_data is served from the kmalloc-16384 slab (backed
by an order-2 compound page), leaving the order-0 free list intact
for the subsequent skb_clone() calls.

This issue was first observed as a use-after-free in ttyport_close()
when ttyport_open() failed, which was investigated in an earlier
patch series [1].  That investigation led to the discovery of the
true root cause described above.

[1] https://lore.kernel.org/all/20250430111617.1151390-1-quic_cxin@quicinc.com/

Fixes: 20bafb3d23d1 ("n_tty: Move buffers into n_tty_data")
Cc: stable@vger.kernel.org
Signed-off-by: Xin Chen <xin.chen2@oss.qualcomm.com>
---
Changes in v2:
- Add Fixes: tag pointing to 20bafb3d23d1
- Add Cc: stable@vger.kernel.org
- Drop the Note paragraph about kvzalloc_obj
- Use kvzalloc_obj() instead of kvzalloc(sizeof(*ldata), GFP_KERNEL)

 drivers/tty/n_tty.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
index e6a0f5b40d0a..81baef6110c0 100644
--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -1870,7 +1870,7 @@ static void n_tty_close(struct tty_struct *tty)
 		n_tty_packet_mode_flush(tty);
 
 	guard(rwsem_write)(&tty->termios_rwsem);
-	vfree(ldata);
+	kvfree(ldata);
 	tty->disc_data = NULL;
 }
 
@@ -1887,7 +1887,7 @@ static int n_tty_open(struct tty_struct *tty)
 	struct n_tty_data *ldata;
 
 	/* Currently a malloc failure here can panic */
-	ldata = vzalloc(sizeof(*ldata));
+	ldata = kvzalloc_obj(ldata);
 	if (!ldata)
 		return -ENOMEM;
 
-- 
2.43.0
Re: [PATCH v2] tty: n_tty: use kvzalloc/kvfree for line discipline data
Posted by kernel test robot 1 month, 1 week ago
Hi Xin,

kernel test robot noticed the following build errors:

[auto build test ERROR on tty/tty-testing]
[also build test ERROR on tty/tty-next tty/tty-linus linus/master v7.2 next-20260819]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Xin-Chen/tty-n_tty-use-kvzalloc-kvfree-for-line-discipline-data/20260818-150324
base:   https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git tty-testing
patch link:    https://lore.kernel.org/r/20260818070324.136726-1-xin.chen2%40oss.qualcomm.com
patch subject: [PATCH v2] tty: n_tty: use kvzalloc/kvfree for line discipline data
config: x86_64-kexec (https://download.01.org/0day-ci/archive/20260821/202608211618.rZcRAXep-lkp@intel.com/config)
compiler: clang version 22.1.3 (https://github.com/llvm/llvm-project e9846648fd6183ee6d8cbdb4502213fcf902a211)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260821/202608211618.rZcRAXep-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202608211618.rZcRAXep-lkp@intel.com/

All errors (new ones prefixed by >>):

>> drivers/tty/n_tty.c:1890:8: error: incompatible pointer types assigning to 'struct n_tty_data *' from 'typeof (ldata) *' (aka 'struct n_tty_data **'); dereference with * [-Wincompatible-pointer-types]
    1890 |         ldata = kvzalloc_obj(ldata);
         |               ^ ~~~~~~~~~~~~~~~~~~~
         |                 *(                 )
   1 error generated.


vim +1890 drivers/tty/n_tty.c

  1876	
  1877	/**
  1878	 * n_tty_open		-	open an ldisc
  1879	 * @tty: terminal to open
  1880	 *
  1881	 * Called when this line discipline is being attached to the terminal device.
  1882	 * Can sleep. Called serialized so that no other events will occur in parallel.
  1883	 * No further open will occur until a close.
  1884	 */
  1885	static int n_tty_open(struct tty_struct *tty)
  1886	{
  1887		struct n_tty_data *ldata;
  1888	
  1889		/* Currently a malloc failure here can panic */
> 1890		ldata = kvzalloc_obj(ldata);
  1891		if (!ldata)
  1892			return -ENOMEM;
  1893	
  1894		ldata->overrun_time = jiffies;
  1895		mutex_init(&ldata->atomic_read_lock);
  1896		mutex_init(&ldata->output_lock);
  1897	
  1898		tty->disc_data = ldata;
  1899		tty->closing = 0;
  1900		/* indicate buffer work may resume */
  1901		clear_bit(TTY_LDISC_HALTED, &tty->flags);
  1902		n_tty_set_termios(tty, NULL);
  1903		tty_unthrottle(tty);
  1904		return 0;
  1905	}
  1906	

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Re: [PATCH v2] tty: n_tty: use kvzalloc/kvfree for line discipline data
Posted by kernel test robot 1 month, 1 week ago
Hi Xin,

kernel test robot noticed the following build errors:

[auto build test ERROR on tty/tty-testing]
[also build test ERROR on tty/tty-next tty/tty-linus linus/master v7.2 next-20260819]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Xin-Chen/tty-n_tty-use-kvzalloc-kvfree-for-line-discipline-data/20260818-150324
base:   https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git tty-testing
patch link:    https://lore.kernel.org/r/20260818070324.136726-1-xin.chen2%40oss.qualcomm.com
patch subject: [PATCH v2] tty: n_tty: use kvzalloc/kvfree for line discipline data
config: alpha-allnoconfig (https://download.01.org/0day-ci/archive/20260821/202608211421.J0U0ljJ6-lkp@intel.com/config)
compiler: alpha-linux-gcc (GCC) 16.1.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260821/202608211421.J0U0ljJ6-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202608211421.J0U0ljJ6-lkp@intel.com/

All errors (new ones prefixed by >>):

   drivers/tty/n_tty.c: In function 'n_tty_open':
>> drivers/tty/n_tty.c:1890:15: error: assignment to 'struct n_tty_data *' from incompatible pointer type 'struct n_tty_data **' [-Wincompatible-pointer-types]
    1890 |         ldata = kvzalloc_obj(ldata);
         |               ^


vim +1890 drivers/tty/n_tty.c

  1876	
  1877	/**
  1878	 * n_tty_open		-	open an ldisc
  1879	 * @tty: terminal to open
  1880	 *
  1881	 * Called when this line discipline is being attached to the terminal device.
  1882	 * Can sleep. Called serialized so that no other events will occur in parallel.
  1883	 * No further open will occur until a close.
  1884	 */
  1885	static int n_tty_open(struct tty_struct *tty)
  1886	{
  1887		struct n_tty_data *ldata;
  1888	
  1889		/* Currently a malloc failure here can panic */
> 1890		ldata = kvzalloc_obj(ldata);
  1891		if (!ldata)
  1892			return -ENOMEM;
  1893	
  1894		ldata->overrun_time = jiffies;
  1895		mutex_init(&ldata->atomic_read_lock);
  1896		mutex_init(&ldata->output_lock);
  1897	
  1898		tty->disc_data = ldata;
  1899		tty->closing = 0;
  1900		/* indicate buffer work may resume */
  1901		clear_bit(TTY_LDISC_HALTED, &tty->flags);
  1902		n_tty_set_termios(tty, NULL);
  1903		tty_unthrottle(tty);
  1904		return 0;
  1905	}
  1906	

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Re: [PATCH v2] tty: n_tty: use kvzalloc/kvfree for line discipline data
Posted by Jiri Slaby 1 month, 1 week ago
On 18. 08. 26, 9:03, Xin Chen wrote:
> Replace vzalloc/vfree with kvzalloc_obj/kvfree.  kvzalloc_obj() tries
> kmalloc first and falls back to vmalloc only on failure.  The
> ~10 KB n_tty_data is served from the kmalloc-16384 slab (backed
> by an order-2 compound page), leaving the order-0 free list intact
> for the subsequent skb_clone() calls.

Switching from order-0 to order-2? If you ran out of vmspace on some 
32bit platform, perhaps. But you apparently did not. So all this feels odd.

NACK

thanks,
-- 
js
suse labs
Re: [PATCH v2] tty: n_tty: use kvzalloc/kvfree for line discipline data
Posted by Xin Chen 1 month, 1 week ago
On Tue, Aug 18, 2026, Jiri Slaby wrote:
 > Switching from order-0 to order-2? If you ran out of vmspace on some
 > 32bit platform, perhaps. But you apparently did not. So all this
 > feels odd.

To clarify: the concern is not vmalloc address space exhaustion.
The issue is that vzalloc() allocates order-0 pages from the buddy
allocator via the bulk allocation path (alloc_pages_bulk_noprof),
which uses ALLOC_WMARK_LOW and does not perform direct reclaim.
When two back-to-back vzalloc() calls drain enough order-0 pages
to push the zone below the low watermark, a subsequent
skb_clone(GFP_KERNEL) in hci_send_cmd_sync() fails silently,
leaving hdev->req_skb NULL and causing BT enable to time out with
-ETIMEDOUT.

kvzalloc_obj() serves the ~10 KB n_tty_data from the kmalloc-16384
slab, which is backed by order-2 compound pages — a separate pool
that does not deplete the order-0 free list that skb_clone() depends
on.  The commit message could have been clearer on this point; I will
improve it in the next version.

Thanks,
Xin Chen