Message ID | 1675864487-18620-1-git-send-email-quic_prashk@quicinc.com |
---|---|
State | New |
Headers |
Return-Path: <linux-kernel-owner@vger.kernel.org> Delivered-To: ouuuleilei@gmail.com Received: by 2002:adf:eb09:0:0:0:0:0 with SMTP id s9csp3472431wrn; Wed, 8 Feb 2023 06:04:36 -0800 (PST) X-Google-Smtp-Source: AK7set9nwjG0Mv5N/iwkNVcJ2lHcuSykqhP4cFC9ss5NMkcEaluT97KmXWr7sxUetfFYBCDSH3e/ X-Received: by 2002:a05:6402:34d5:b0:4ab:dde:dea0 with SMTP id w21-20020a05640234d500b004ab0ddedea0mr1822275edc.2.1675865076000; Wed, 08 Feb 2023 06:04:36 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1675865075; cv=none; d=google.com; s=arc-20160816; b=g88fdC0dzoIOoJM5o+0YnFYgwm+Yfkz4aTSCtY2DuNSWGT8lQ0FSu0KFlRdQgsbkw7 JLEpe0p1dJARUCi+HNyQ2cFw0zb6Ha7YvuHHc8ZVlsGjeyxHeLIX+MYh/ASCSpLk78xR +Ou1BXJqcXtdFTGXdcvgJaIiaipw6VTyw7UMx2yq4YNrJzCptEs10P9FFnI0Q/14prEE nl/7YNCiX85sR42Uy/FklwHCbs8HRVVwBWSAR635d1xDqd/uqRiiKccQduZNJwN82bi3 eLoQ8EneU6zVNAs/hpE2QgZ998Ml+vqLGbUV5/XuxdGRI7fN+PYC0PfuWPaGdDIZ9Jko +wDw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:mime-version:message-id:date:subject:cc:to:from :dkim-signature; bh=984loDL0V2FFZOKxWEBpTvfRYvQFpHwHbhUyylx6Mis=; b=irL4R0dswfY+dQxUI2KS03ox442v48kXeELBK+iBjruhlLbRD0x5jTRMvB0SySkAnf WGVOTDZasHhCgmbods4W1A67nDNToNIX0xfO/CIRgXLihK2TWOezTNjshPC/XYtgT94d PayOh2JyM4FwF1tnarEjczH0YbHEShETF+Z6eIbPHnDxtyxffmdL8f8CX0+kbcEbFpB1 b3ciDW6wxiVhgJlJ2/7p1a+o97HXq0i8zjLdJ3AdkQDFTpHJHPlNOX+8U6NJCr+P4TuU dPMjK12+3I9ot20vVL4HTiBOP0KIRFPo5WsBz09vFlKQQ2AUrRN4e+PUhTMAmJkP/E2g PgaA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@quicinc.com header.s=qcppdkim1 header.b=Vp0bQfkK; 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=NONE dis=NONE) header.from=quicinc.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id p9-20020a056402500900b004a08f43bb56si1996865eda.527.2023.02.08.06.04.11; Wed, 08 Feb 2023 06:04:35 -0800 (PST) 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=@quicinc.com header.s=qcppdkim1 header.b=Vp0bQfkK; 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=NONE dis=NONE) header.from=quicinc.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231373AbjBHNzG (ORCPT <rfc822;ivan.orlov0322@gmail.com> + 99 others); Wed, 8 Feb 2023 08:55:06 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51084 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231334AbjBHNzF (ORCPT <rfc822;linux-kernel@vger.kernel.org>); Wed, 8 Feb 2023 08:55:05 -0500 Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3096255BD; Wed, 8 Feb 2023 05:55:04 -0800 (PST) Received: from pps.filterd (m0279869.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 318DfIQf028449; Wed, 8 Feb 2023 13:54:56 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=quicinc.com; h=from : to : cc : subject : date : message-id : mime-version : content-type; s=qcppdkim1; bh=984loDL0V2FFZOKxWEBpTvfRYvQFpHwHbhUyylx6Mis=; b=Vp0bQfkKK7hmRUERVmPERu2Tw1mZ116OiMsyRtnIxa42sTo1xXv2eif57eObyZur5NOw DauGpMs90BYYOKvA02RjNyzBQ0NRYkKj8YPqJek5CakOR3Y+4DIMnLzEoVSwW1bNxrR2 EmE+Z7BGs35NCtmH5YH/ezRwJa+6+A9gRB2j2aG07bsdYSUlX22C7wecCHtvnPL9a4Vi SBDJFwda49RMJdXIrMENA5VQz+WmdnjEN5J9Cmqb7X4VCIz45E0592LNye/rjPMQl7nM I752EC6ABluQUxP/AXwCiLz3BogD29WT46DmB8PCYfY0s0t529bAmAEhJnx3tx0SjIDU +Q== Received: from nalasppmta03.qualcomm.com (Global_NAT1.qualcomm.com [129.46.96.20]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 3nkk2d3v30-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 08 Feb 2023 13:54:56 +0000 Received: from nalasex01a.na.qualcomm.com (nalasex01a.na.qualcomm.com [10.47.209.196]) by NALASPPMTA03.qualcomm.com (8.17.1.5/8.17.1.5) with ESMTPS id 318DstF6012640 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 8 Feb 2023 13:54:55 GMT Received: from hu-prashk-hyd.qualcomm.com (10.80.80.8) by nalasex01a.na.qualcomm.com (10.47.209.196) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.986.36; Wed, 8 Feb 2023 05:54:52 -0800 From: Prashanth K <quic_prashk@quicinc.com> To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Christophe JAILLET <christophe.jaillet@wanadoo.fr>, Xiu Jianfeng <xiujianfeng@huawei.com> CC: Pratham Pratap <quic_ppratap@quicinc.com>, Jack Pham <quic_jackp@quicinc.com>, <linux-usb@vger.kernel.org>, <linux-kernel@vger.kernel.org>, Prashanth K <quic_prashk@quicinc.com> Subject: [PATCH] usb: gadget: u_serial: Add null pointer check in gserial_resume Date: Wed, 8 Feb 2023 19:24:47 +0530 Message-ID: <1675864487-18620-1-git-send-email-quic_prashk@quicinc.com> X-Mailer: git-send-email 2.7.4 MIME-Version: 1.0 Content-Type: text/plain X-Originating-IP: [10.80.80.8] X-ClientProxiedBy: nasanex01b.na.qualcomm.com (10.46.141.250) To nalasex01a.na.qualcomm.com (10.47.209.196) X-QCInternal: smtphost X-Proofpoint-Virus-Version: vendor=nai engine=6200 definitions=5800 signatures=585085 X-Proofpoint-GUID: piPd7KRLe2ExGOqf9zeBqyCpFD4njzrN X-Proofpoint-ORIG-GUID: piPd7KRLe2ExGOqf9zeBqyCpFD4njzrN X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.219,Aquarius:18.0.930,Hydra:6.0.562,FMLib:17.11.122.1 definitions=2023-02-08_05,2023-02-08_02,2022-06-22_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 bulkscore=0 spamscore=0 impostorscore=0 phishscore=0 lowpriorityscore=0 suspectscore=0 mlxlogscore=582 clxscore=1011 mlxscore=0 adultscore=0 priorityscore=1501 malwarescore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2212070000 definitions=main-2302080121 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,SPF_HELO_NONE,SPF_PASS 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: <linux-kernel.vger.kernel.org> X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1757271898122549896?= X-GMAIL-MSGID: =?utf-8?q?1757271898122549896?= |
Series |
usb: gadget: u_serial: Add null pointer check in gserial_resume
|
|
Commit Message
Prashanth K
Feb. 8, 2023, 1:54 p.m. UTC
Consider a case where gserial_disconnect has already cleared
gser->ioport. And if a wakeup interrupt triggers afterwards,
gserial_resume gets called, which will lead to accessing of
gserial->port and thus causing null pointer dereference.Add
a null pointer check to prevent this.
Fixes: aba3a8d01d62 (" usb: gadget: u_serial: add suspend resume callbacks")
Signed-off-by: Prashanth K <quic_prashk@quicinc.com>
---
drivers/usb/gadget/function/u_serial.c | 3 +++
1 file changed, 3 insertions(+)
Comments
On Wed, Feb 08, 2023 at 07:24:47PM +0530, Prashanth K wrote: > Consider a case where gserial_disconnect has already cleared > gser->ioport. And if a wakeup interrupt triggers afterwards, > gserial_resume gets called, which will lead to accessing of > gserial->port and thus causing null pointer dereference.Add > a null pointer check to prevent this. > > Fixes: aba3a8d01d62 (" usb: gadget: u_serial: add suspend resume callbacks") Nit, and our tools will complain, no " " before the "usb:" string here, right? > Signed-off-by: Prashanth K <quic_prashk@quicinc.com> > --- > drivers/usb/gadget/function/u_serial.c | 3 +++ > 1 file changed, 3 insertions(+) > > diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c > index 840626e..98be2b8 100644 > --- a/drivers/usb/gadget/function/u_serial.c > +++ b/drivers/usb/gadget/function/u_serial.c > @@ -1428,6 +1428,9 @@ void gserial_resume(struct gserial *gser) > struct gs_port *port = gser->ioport; > unsigned long flags; > > + if (!port) > + return; > + What prevents port from going to NULL right after this check? thanks, greg k-h
On 08-02-23 08:24 pm, Greg Kroah-Hartman wrote: > On Wed, Feb 08, 2023 at 07:24:47PM +0530, Prashanth K wrote: >> Consider a case where gserial_disconnect has already cleared >> gser->ioport. And if a wakeup interrupt triggers afterwards, >> gserial_resume gets called, which will lead to accessing of >> gserial->port and thus causing null pointer dereference.Add >> a null pointer check to prevent this. >> >> Fixes: aba3a8d01d62 (" usb: gadget: u_serial: add suspend resume callbacks") > > Nit, and our tools will complain, no " " before the "usb:" string here, > right? > Will fix it in next patch. > > >> Signed-off-by: Prashanth K <quic_prashk@quicinc.com> >> --- >> drivers/usb/gadget/function/u_serial.c | 3 +++ >> 1 file changed, 3 insertions(+) >> >> diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c >> index 840626e..98be2b8 100644 >> --- a/drivers/usb/gadget/function/u_serial.c >> +++ b/drivers/usb/gadget/function/u_serial.c >> @@ -1428,6 +1428,9 @@ void gserial_resume(struct gserial *gser) >> struct gs_port *port = gser->ioport; >> unsigned long flags; >> >> + if (!port) >> + return; >> + > > What prevents port from going to NULL right after this check? In our case we got a null pointer de-reference while performing USB compliance tests, as the gser->port was null. Because in gserial_resume, spinlock_irq_save(&port->port_lock) accesses a null-pointer as port was already marked null by gserial_disconnect. And after gserial_resume acquires the spinlock, gserial_disconnect cant mark it null until the spinlock is released. We need to check if the port->lock is valid before accessing it, otherwise it can lead to the above mentioned scenario Issue Type: kernel panic issue Issue AutoSignature: pc : do_raw_spin_lock lr : _raw_spin_lock_irqsave Call trace: do_raw_spin_lock _raw_spin_lock_irqsave gserial_resume acm_resume composite_resume configfs_composite_resume dwc3_process_event_entry dwc3_process_event_buf dwc3_thread_interrupt irq_thread_fn irq_thread kthread ret_from_fork Thanks in advance, Prashanth > > thanks, > > greg k-h
On Wed, Feb 08, 2023 at 09:15:54PM +0530, Prashanth K wrote: > > > On 08-02-23 08:24 pm, Greg Kroah-Hartman wrote: > > On Wed, Feb 08, 2023 at 07:24:47PM +0530, Prashanth K wrote: > > > Consider a case where gserial_disconnect has already cleared > > > gser->ioport. And if a wakeup interrupt triggers afterwards, > > > gserial_resume gets called, which will lead to accessing of > > > gserial->port and thus causing null pointer dereference.Add > > > a null pointer check to prevent this. > > > > > > Fixes: aba3a8d01d62 (" usb: gadget: u_serial: add suspend resume callbacks") > > > > Nit, and our tools will complain, no " " before the "usb:" string here, > > right? > > > Will fix it in next patch. > > > > > > > Signed-off-by: Prashanth K <quic_prashk@quicinc.com> > > > --- > > > drivers/usb/gadget/function/u_serial.c | 3 +++ > > > 1 file changed, 3 insertions(+) > > > > > > diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c > > > index 840626e..98be2b8 100644 > > > --- a/drivers/usb/gadget/function/u_serial.c > > > +++ b/drivers/usb/gadget/function/u_serial.c > > > @@ -1428,6 +1428,9 @@ void gserial_resume(struct gserial *gser) > > > struct gs_port *port = gser->ioport; > > > unsigned long flags; > > > + if (!port) > > > + return; > > > + > > > > What prevents port from going to NULL right after this check? > In our case we got a null pointer de-reference while performing USB > compliance tests, as the gser->port was null. Because in gserial_resume, > spinlock_irq_save(&port->port_lock) accesses a null-pointer as port was > already marked null by gserial_disconnect. > > And after gserial_resume acquires the spinlock, gserial_disconnect cant mark > it null until the spinlock is released. We need to check if the port->lock > is valid before accessing it, otherwise it can lead to the above mentioned > scenario What happens if gserial_disconnect sets gser->port to NULL immediately after your new check occurs, but before spinlock_irq_save(&port->port_lock) gets called? You may need to add a static spinlock to prevent this from happening. Alan Stern
On 09-02-23 01:51 am, Alan Stern wrote: > On Wed, Feb 08, 2023 at 09:15:54PM +0530, Prashanth K wrote: >> >> >> On 08-02-23 08:24 pm, Greg Kroah-Hartman wrote: >>> On Wed, Feb 08, 2023 at 07:24:47PM +0530, Prashanth K wrote: >>>> Consider a case where gserial_disconnect has already cleared >>>> gser->ioport. And if a wakeup interrupt triggers afterwards, >>>> gserial_resume gets called, which will lead to accessing of >>>> gserial->port and thus causing null pointer dereference.Add >>>> a null pointer check to prevent this. >>>> >>>> Fixes: aba3a8d01d62 (" usb: gadget: u_serial: add suspend resume callbacks") >>> >>> Nit, and our tools will complain, no " " before the "usb:" string here, >>> right? >>> >> Will fix it in next patch. >>> >>> >>>> Signed-off-by: Prashanth K <quic_prashk@quicinc.com> >>>> --- >>>> drivers/usb/gadget/function/u_serial.c | 3 +++ >>>> 1 file changed, 3 insertions(+) >>>> >>>> diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c >>>> index 840626e..98be2b8 100644 >>>> --- a/drivers/usb/gadget/function/u_serial.c >>>> +++ b/drivers/usb/gadget/function/u_serial.c >>>> @@ -1428,6 +1428,9 @@ void gserial_resume(struct gserial *gser) >>>> struct gs_port *port = gser->ioport; >>>> unsigned long flags; >>>> + if (!port) >>>> + return; >>>> + >>> >>> What prevents port from going to NULL right after this check? >> In our case we got a null pointer de-reference while performing USB >> compliance tests, as the gser->port was null. Because in gserial_resume, >> spinlock_irq_save(&port->port_lock) accesses a null-pointer as port was >> already marked null by gserial_disconnect. >> >> And after gserial_resume acquires the spinlock, gserial_disconnect cant mark >> it null until the spinlock is released. We need to check if the port->lock >> is valid before accessing it, otherwise it can lead to the above mentioned >> scenario > > What happens if gserial_disconnect sets gser->port to NULL immediately > after your new check occurs, but before > spinlock_irq_save(&port->port_lock) gets called? > > You may need to add a static spinlock to prevent this from happening. > > Alan Stern In that case i guess we have to make port_lock a global variable and take it out of gs_port structure. + static DEFINE_SPINLOCK(port_lock); struct gs_port { struct tty_port port; - spinlock_t port_lock; This will require us to change all the spinlock(port->port_lock) used in u_serial.c, what do you suggest?
On Thu, Feb 09, 2023 at 10:31:50AM +0530, Prashanth K wrote: > > > On 09-02-23 01:51 am, Alan Stern wrote: > > On Wed, Feb 08, 2023 at 09:15:54PM +0530, Prashanth K wrote: > > > > > > > > > On 08-02-23 08:24 pm, Greg Kroah-Hartman wrote: > > > > On Wed, Feb 08, 2023 at 07:24:47PM +0530, Prashanth K wrote: > > > > > Consider a case where gserial_disconnect has already cleared > > > > > gser->ioport. And if a wakeup interrupt triggers afterwards, > > > > > gserial_resume gets called, which will lead to accessing of > > > > > gserial->port and thus causing null pointer dereference.Add > > > > > a null pointer check to prevent this. > > > > > > > > > > Fixes: aba3a8d01d62 (" usb: gadget: u_serial: add suspend resume callbacks") > > > > > > > > Nit, and our tools will complain, no " " before the "usb:" string here, > > > > right? > > > > > > > Will fix it in next patch. > > > > > > > > > > > > > Signed-off-by: Prashanth K <quic_prashk@quicinc.com> > > > > > --- > > > > > drivers/usb/gadget/function/u_serial.c | 3 +++ > > > > > 1 file changed, 3 insertions(+) > > > > > > > > > > diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c > > > > > index 840626e..98be2b8 100644 > > > > > --- a/drivers/usb/gadget/function/u_serial.c > > > > > +++ b/drivers/usb/gadget/function/u_serial.c > > > > > @@ -1428,6 +1428,9 @@ void gserial_resume(struct gserial *gser) > > > > > struct gs_port *port = gser->ioport; > > > > > unsigned long flags; > > > > > + if (!port) > > > > > + return; > > > > > + > > > > > > > > What prevents port from going to NULL right after this check? > > > In our case we got a null pointer de-reference while performing USB > > > compliance tests, as the gser->port was null. Because in gserial_resume, > > > spinlock_irq_save(&port->port_lock) accesses a null-pointer as port was > > > already marked null by gserial_disconnect. > > > > > > And after gserial_resume acquires the spinlock, gserial_disconnect cant mark > > > it null until the spinlock is released. We need to check if the port->lock > > > is valid before accessing it, otherwise it can lead to the above mentioned > > > scenario > > > > What happens if gserial_disconnect sets gser->port to NULL immediately > > after your new check occurs, but before > > spinlock_irq_save(&port->port_lock) gets called? > > > > You may need to add a static spinlock to prevent this from happening. > > > > Alan Stern > In that case i guess we have to make port_lock a global variable and take it > out of gs_port structure. > > + static DEFINE_SPINLOCK(port_lock); > > struct gs_port { > struct tty_port port; > - spinlock_t port_lock; > > This will require us to change all the spinlock(port->port_lock) used in > u_serial.c, what do you suggest? Yes, that would be the correct thing to do.
On 09-02-23 12:31 pm, Greg Kroah-Hartman wrote: > On Thu, Feb 09, 2023 at 10:31:50AM +0530, Prashanth K wrote: >> >> >> On 09-02-23 01:51 am, Alan Stern wrote: >>> On Wed, Feb 08, 2023 at 09:15:54PM +0530, Prashanth K wrote: >>>> >>>> >>>> On 08-02-23 08:24 pm, Greg Kroah-Hartman wrote: >>>>> On Wed, Feb 08, 2023 at 07:24:47PM +0530, Prashanth K wrote: >>>>>> Consider a case where gserial_disconnect has already cleared >>>>>> gser->ioport. And if a wakeup interrupt triggers afterwards, >>>>>> gserial_resume gets called, which will lead to accessing of >>>>>> gserial->port and thus causing null pointer dereference.Add >>>>>> a null pointer check to prevent this. >>>>>> >>>>>> Fixes: aba3a8d01d62 (" usb: gadget: u_serial: add suspend resume callbacks") >>>>> >>>>> Nit, and our tools will complain, no " " before the "usb:" string here, >>>>> right? >>>>> >>>> Will fix it in next patch. >>>>> >>>>> >>>>>> Signed-off-by: Prashanth K <quic_prashk@quicinc.com> >>>>>> --- >>>>>> drivers/usb/gadget/function/u_serial.c | 3 +++ >>>>>> 1 file changed, 3 insertions(+) >>>>>> >>>>>> diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c >>>>>> index 840626e..98be2b8 100644 >>>>>> --- a/drivers/usb/gadget/function/u_serial.c >>>>>> +++ b/drivers/usb/gadget/function/u_serial.c >>>>>> @@ -1428,6 +1428,9 @@ void gserial_resume(struct gserial *gser) >>>>>> struct gs_port *port = gser->ioport; >>>>>> unsigned long flags; >>>>>> + if (!port) >>>>>> + return; >>>>>> + >>>>> >>>>> What prevents port from going to NULL right after this check? >>>> In our case we got a null pointer de-reference while performing USB >>>> compliance tests, as the gser->port was null. Because in gserial_resume, >>>> spinlock_irq_save(&port->port_lock) accesses a null-pointer as port was >>>> already marked null by gserial_disconnect. >>>> >>>> And after gserial_resume acquires the spinlock, gserial_disconnect cant mark >>>> it null until the spinlock is released. We need to check if the port->lock >>>> is valid before accessing it, otherwise it can lead to the above mentioned >>>> scenario >>> >>> What happens if gserial_disconnect sets gser->port to NULL immediately >>> after your new check occurs, but before >>> spinlock_irq_save(&port->port_lock) gets called? >>> >>> You may need to add a static spinlock to prevent this from happening. >>> >>> Alan Stern >> In that case i guess we have to make port_lock a global variable and take it >> out of gs_port structure. >> >> + static DEFINE_SPINLOCK(port_lock); >> >> struct gs_port { >> struct tty_port port; >> - spinlock_t port_lock; >> >> This will require us to change all the spinlock(port->port_lock) used in >> u_serial.c, what do you suggest? > > Yes, that would be the correct thing to do. will do it and share next patch Thanks for the suggestions Prashanth K
On 09-02-23 12:33 pm, Prashanth K wrote: > > > On 09-02-23 12:31 pm, Greg Kroah-Hartman wrote: >> On Thu, Feb 09, 2023 at 10:31:50AM +0530, Prashanth K wrote: >>> >>> >>> On 09-02-23 01:51 am, Alan Stern wrote: >>>> On Wed, Feb 08, 2023 at 09:15:54PM +0530, Prashanth K wrote: >>>>> >>>>> >>>>> On 08-02-23 08:24 pm, Greg Kroah-Hartman wrote: >>>>>> On Wed, Feb 08, 2023 at 07:24:47PM +0530, Prashanth K wrote: >>>>>>> Consider a case where gserial_disconnect has already cleared >>>>>>> gser->ioport. And if a wakeup interrupt triggers afterwards, >>>>>>> gserial_resume gets called, which will lead to accessing of >>>>>>> gserial->port and thus causing null pointer dereference.Add >>>>>>> a null pointer check to prevent this. >>>>>>> >>>>>>> Fixes: aba3a8d01d62 (" usb: gadget: u_serial: add suspend resume >>>>>>> callbacks") >>>>>> >>>>>> Nit, and our tools will complain, no " " before the "usb:" string >>>>>> here, >>>>>> right? >>>>>> >>>>> Will fix it in next patch. >>>>>> >>>>>> >>>>>>> Signed-off-by: Prashanth K <quic_prashk@quicinc.com> >>>>>>> --- >>>>>>> drivers/usb/gadget/function/u_serial.c | 3 +++ >>>>>>> 1 file changed, 3 insertions(+) >>>>>>> >>>>>>> diff --git a/drivers/usb/gadget/function/u_serial.c >>>>>>> b/drivers/usb/gadget/function/u_serial.c >>>>>>> index 840626e..98be2b8 100644 >>>>>>> --- a/drivers/usb/gadget/function/u_serial.c >>>>>>> +++ b/drivers/usb/gadget/function/u_serial.c >>>>>>> @@ -1428,6 +1428,9 @@ void gserial_resume(struct gserial *gser) >>>>>>> struct gs_port *port = gser->ioport; >>>>>>> unsigned long flags; >>>>>>> + if (!port) >>>>>>> + return; >>>>>>> + >>>>>> >>>>>> What prevents port from going to NULL right after this check? >>>>> In our case we got a null pointer de-reference while performing USB >>>>> compliance tests, as the gser->port was null. Because in >>>>> gserial_resume, >>>>> spinlock_irq_save(&port->port_lock) accesses a null-pointer as port >>>>> was >>>>> already marked null by gserial_disconnect. >>>>> >>>>> And after gserial_resume acquires the spinlock, gserial_disconnect >>>>> cant mark >>>>> it null until the spinlock is released. We need to check if the >>>>> port->lock >>>>> is valid before accessing it, otherwise it can lead to the above >>>>> mentioned >>>>> scenario >>>> >>>> What happens if gserial_disconnect sets gser->port to NULL immediately >>>> after your new check occurs, but before >>>> spinlock_irq_save(&port->port_lock) gets called? >>>> >>>> You may need to add a static spinlock to prevent this from happening. >>>> >>>> Alan Stern >>> In that case i guess we have to make port_lock a global variable and >>> take it >>> out of gs_port structure. >>> >>> + static DEFINE_SPINLOCK(port_lock); >>> >>> struct gs_port { >>> struct tty_port port; >>> - spinlock_t port_lock; >>> >>> This will require us to change all the spinlock(port->port_lock) used in >>> u_serial.c, what do you suggest? >> >> Yes, that would be the correct thing to do. Hi Greg/Alan, One general doubt, if we make the spinlock static/global, wouldn't that be a problem when there are multiple instances, and also multiple interfaces can use u_serial at same time. Asking this because u_serial can be used by f_serial (gser) as well as f_acm (acm). Thanks Prahanth K > will do it and share next patch > > Thanks for the suggestions > Prashanth K
On Thu, Feb 09, 2023 at 07:37:01PM +0530, Prashanth K wrote: > > > On 09-02-23 12:33 pm, Prashanth K wrote: > > > > > > On 09-02-23 12:31 pm, Greg Kroah-Hartman wrote: > > > On Thu, Feb 09, 2023 at 10:31:50AM +0530, Prashanth K wrote: > > > > In that case i guess we have to make port_lock a global variable > > > > and take it > > > > out of gs_port structure. > > > > > > > > + static DEFINE_SPINLOCK(port_lock); > > > > > > > > struct gs_port { > > > > struct tty_port port; > > > > - spinlock_t port_lock; > > > > > > > > This will require us to change all the spinlock(port->port_lock) used in > > > > u_serial.c, what do you suggest? > > > > > > Yes, that would be the correct thing to do. > Hi Greg/Alan, One general doubt, if we make the spinlock static/global, > wouldn't that be a problem when there are multiple instances, and also > multiple interfaces can use u_serial at same time. Asking this because > u_serial can be used by f_serial (gser) as well as f_acm (acm). You should consider having _two_ spinlocks: One in the gs_port structure (the way it is now) and a separate global lock. The first would be used in situations where you know you have a valid pointer. The second would be used in situations where you don't know if the pointer is non-NULL or where you are changing the pointer's value. Alan Stern
On 09-02-23 08:39 pm, Alan Stern wrote: > On Thu, Feb 09, 2023 at 07:37:01PM +0530, Prashanth K wrote: >> >> >> On 09-02-23 12:33 pm, Prashanth K wrote: >>> >>> >>> On 09-02-23 12:31 pm, Greg Kroah-Hartman wrote: >>>> On Thu, Feb 09, 2023 at 10:31:50AM +0530, Prashanth K wrote: >>>>> In that case i guess we have to make port_lock a global variable >>>>> and take it >>>>> out of gs_port structure. >>>>> >>>>> + static DEFINE_SPINLOCK(port_lock); >>>>> >>>>> struct gs_port { >>>>> struct tty_port port; >>>>> - spinlock_t port_lock; >>>>> >>>>> This will require us to change all the spinlock(port->port_lock) used in >>>>> u_serial.c, what do you suggest? >>>> >>>> Yes, that would be the correct thing to do. >> Hi Greg/Alan, One general doubt, if we make the spinlock static/global, >> wouldn't that be a problem when there are multiple instances, and also >> multiple interfaces can use u_serial at same time. Asking this because >> u_serial can be used by f_serial (gser) as well as f_acm (acm). > > You should consider having _two_ spinlocks: One in the gs_port structure > (the way it is now) and a separate global lock. The first would be used > in situations where you know you have a valid pointer. The second would > be used in situations where you don't know if the pointer is non-NULL > or where you are changing the pointer's value. Lets say we replaced the existing spinlock in gserial_resume and gserial_disconnect with a new static spinlock, and kept the spinlocks in other functions unchanged. In that case, wouldn't it cause additional race conditions as we are using 2 different locks. Thanks, Prashanth K > > Alan Stern
On Thu, Feb 09, 2023 at 09:13:37PM +0530, Prashanth K wrote: > > > On 09-02-23 08:39 pm, Alan Stern wrote: > > You should consider having _two_ spinlocks: One in the gs_port structure > > (the way it is now) and a separate global lock. The first would be used > > in situations where you know you have a valid pointer. The second would > > be used in situations where you don't know if the pointer is non-NULL > > or where you are changing the pointer's value. > Lets say we replaced the existing spinlock in gserial_resume and > gserial_disconnect with a new static spinlock, and kept the spinlocks in > other functions unchanged. In that case, wouldn't it cause additional race > conditions as we are using 2 different locks. Not race conditions, but possibilities for deadlock. Indeed, you would have to be very careful about avoiding deadlock scenarios. In particular, you would have to ensure that the code never tries to acquire the global spinlock while already holding one of the per-port spinlocks. Alan Stern
On 09-02-23 09:33 pm, Alan Stern wrote: > On Thu, Feb 09, 2023 at 09:13:37PM +0530, Prashanth K wrote: >> >> >> On 09-02-23 08:39 pm, Alan Stern wrote: >>> You should consider having _two_ spinlocks: One in the gs_port structure >>> (the way it is now) and a separate global lock. The first would be used >>> in situations where you know you have a valid pointer. The second would >>> be used in situations where you don't know if the pointer is non-NULL >>> or where you are changing the pointer's value. >> Lets say we replaced the existing spinlock in gserial_resume and >> gserial_disconnect with a new static spinlock, and kept the spinlocks in >> other functions unchanged. In that case, wouldn't it cause additional race >> conditions as we are using 2 different locks. > > Not race conditions, but possibilities for deadlock. > > Indeed, you would have to be very careful about avoiding deadlock > scenarios. In particular, you would have to ensure that the code never > tries to acquire the global spinlock while already holding one of the > per-port spinlocks. > > Alan Stern Hi Alan, instead of doing these and causing potential regressions, can we just have the null pointer check which i suggested in the beginning? The major concern was that port might become null after the null pointer check. We mark gser->ioport as null pointer in gserial_disconnect, and in gserial_resume we copy the gser->ioport to *port in the beginning. struct gs_port *port = gser->ioport; And hence it wont cause null pointer deref after the check as we don't de-reference anything from gser->ioport afterwards. We only use the local pointer *port afterwards. Thanks, Prashanth K
On Thu, Feb 09, 2023 at 11:57:17PM +0530, Prashanth K wrote: > > > On 09-02-23 09:33 pm, Alan Stern wrote: > > On Thu, Feb 09, 2023 at 09:13:37PM +0530, Prashanth K wrote: > > > > > > > > > On 09-02-23 08:39 pm, Alan Stern wrote: > > > > You should consider having _two_ spinlocks: One in the gs_port structure > > > > (the way it is now) and a separate global lock. The first would be used > > > > in situations where you know you have a valid pointer. The second would > > > > be used in situations where you don't know if the pointer is non-NULL > > > > or where you are changing the pointer's value. > > > Lets say we replaced the existing spinlock in gserial_resume and > > > gserial_disconnect with a new static spinlock, and kept the spinlocks in > > > other functions unchanged. In that case, wouldn't it cause additional race > > > conditions as we are using 2 different locks. > > > > Not race conditions, but possibilities for deadlock. > > > > Indeed, you would have to be very careful about avoiding deadlock > > scenarios. In particular, you would have to ensure that the code never > > tries to acquire the global spinlock while already holding one of the > > per-port spinlocks. > > > > Alan Stern > Hi Alan, instead of doing these and causing potential regressions, can we > just have the null pointer check which i suggested in the beginning? The > major concern was that port might become null after the null pointer check. What you are describing is a data race: gserial_disconnect() can write to gser->ioport at the same time that gserial_resume() reads from it. Unless you're working on a fast path -- which this isn't -- you should strive to avoid data races by using proper locking. That means adding the extra spinlock, or finding some other way to make these two accesses be mutually exclusive. With a little care you can ensure there won't be any regressions. Just do what I said above: Make sure the code never tries to acquire the global spinlock while already holding one of the per-port spinlocks. > We mark gser->ioport as null pointer in gserial_disconnect, and in > gserial_resume we copy the gser->ioport to *port in the beginning. > > struct gs_port *port = gser->ioport; > > And hence it wont cause null pointer deref after the check as we don't > de-reference anything from gser->ioport afterwards. We only use the local > pointer *port afterwards. You cannot depend on this to work the way you want. The compiler will optimize your source code, and one of the optimizations might be to eliminate the "port" variable entirely and replace it with gser->ioport. Alan Stern
On 10-02-23 02:35 am, Alan Stern wrote: > On Thu, Feb 09, 2023 at 11:57:17PM +0530, Prashanth K wrote: >> >> >> On 09-02-23 09:33 pm, Alan Stern wrote: >>> On Thu, Feb 09, 2023 at 09:13:37PM +0530, Prashanth K wrote: >>>> >>>> >>>> On 09-02-23 08:39 pm, Alan Stern wrote: >>>>> You should consider having _two_ spinlocks: One in the gs_port structure >>>>> (the way it is now) and a separate global lock. The first would be used >>>>> in situations where you know you have a valid pointer. The second would >>>>> be used in situations where you don't know if the pointer is non-NULL >>>>> or where you are changing the pointer's value. >>>> Lets say we replaced the existing spinlock in gserial_resume and >>>> gserial_disconnect with a new static spinlock, and kept the spinlocks in >>>> other functions unchanged. In that case, wouldn't it cause additional race >>>> conditions as we are using 2 different locks. >>> >>> Not race conditions, but possibilities for deadlock. >>> >>> Indeed, you would have to be very careful about avoiding deadlock >>> scenarios. In particular, you would have to ensure that the code never >>> tries to acquire the global spinlock while already holding one of the >>> per-port spinlocks. >>> >>> Alan Stern >> Hi Alan, instead of doing these and causing potential regressions, can we >> just have the null pointer check which i suggested in the beginning? The >> major concern was that port might become null after the null pointer check. > > What you are describing is a data race: gserial_disconnect() can write > to gser->ioport at the same time that gserial_resume() reads from it. > Unless you're working on a fast path -- which this isn't -- you should > strive to avoid data races by using proper locking. That means adding > the extra spinlock, or finding some other way to make these two accesses > be mutually exclusive. > > With a little care you can ensure there won't be any regressions. Just > do what I said above: Make sure the code never tries to acquire the > global spinlock while already holding one of the per-port spinlocks. > >> We mark gser->ioport as null pointer in gserial_disconnect, and in >> gserial_resume we copy the gser->ioport to *port in the beginning. >> >> struct gs_port *port = gser->ioport; >> >> And hence it wont cause null pointer deref after the check as we don't >> de-reference anything from gser->ioport afterwards. We only use the local >> pointer *port afterwards. > > You cannot depend on this to work the way you want. The compiler will > optimize your source code, and one of the optimizations might be to > eliminate the "port" variable entirely and replace it with gser->ioport. > > Alan Stern Hi Alan, Thanks for the detailed info. I checked and included few cases here. This would cause a deadlock if gserial_disconnect acquires port_lock and gserial_resume acquires static_lock. gserial_disconnect { spin_lock(port) ... spin_lock(static) gser->ioport = NULL; spin_unlock(static) ... spin_unlock(port) } gserial_resume { struct gs_port *port = gser->ioport; spin_lock(static) if (!port) return spin_lock(port) spin_unlock(static) ... spin_unlock(port) } ------------------------------------------------------------------ This would cause additional races when gserial_disconnect releases port_lock and some other functions acquire it. gserial_disconnect { spin_lock(port) ... spin_unlock(port) spin_lock(static) gser->ioport = NULL; spin_unlock(static) spin_lock(port) ... spin_unlock(port) } gserial_resume { struct gs_port *port = gser->ioport; spin_lock(static) if (!port) return spin_lock(port) spin_unlock(static) ... spin_unlock(port) } ------------------------------------------------------------------ And this seems like a viable option to me, what do you suggest? gserial_disconnect { spin_lock(static) spin_lock(port) ... gser->ioport = NULL; ... spin_lock(port) spin_unlock(static) } gserial_resume { struct gs_port *port = gser->ioport; spin_lock(static) if (!port) return spin_lock(port) ... spin_unlock(port) spin_unlock(static) } Thanks, Prashanth K
> And this seems like a viable option to me, what do you suggest? > > gserial_disconnect { > spin_lock(static) > spin_lock(port) > ... > gser->ioport = NULL; > ... > spin_lock(port) > spin_unlock(static) > > } > > gserial_resume { > struct gs_port *port = gser->ioport; > > spin_lock(static) > if (!port) spin_unlock(static) > return > spin_lock(port) > > ... > spin_unlock(port) > spin_unlock(static) > } > > Thanks, > Prashanth K Small correction inlined.
On Fri, Feb 10, 2023 at 12:26:52PM +0530, Prashanth K wrote: > > > > And this seems like a viable option to me, what do you suggest? > > > > gserial_disconnect { > > spin_lock(static) > > spin_lock(port) > > ... > > gser->ioport = NULL; > > ... > > spin_lock(port) > > spin_unlock(static) > > > > } > > > > gserial_resume { > > struct gs_port *port = gser->ioport; > > > > spin_lock(static) > > if (!port) > spin_unlock(static) > > return > > spin_lock(port) If you want, you could move the spin_unlock(static) up to here. It probably doesn't matter. > > > > ... > > spin_unlock(port) > > spin_unlock(static) > > } I agree, that should work fine. Alan Stern
On 10-02-23 09:17 pm, Alan Stern wrote: > > I agree, that should work fine. > > Alan Stern Thanks Alan for the help! will create v2 patch.
diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index 840626e..98be2b8 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -1428,6 +1428,9 @@ void gserial_resume(struct gserial *gser) struct gs_port *port = gser->ioport; unsigned long flags; + if (!port) + return; + spin_lock_irqsave(&port->port_lock, flags); port->suspended = false; if (!port->start_delayed) {