From nobody Sun Apr 5 16:46:32 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id DE4D0C54EE9 for ; Fri, 16 Sep 2022 05:00:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229961AbiIPFAi (ORCPT ); Fri, 16 Sep 2022 01:00:38 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57804 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230036AbiIPFAM (ORCPT ); Fri, 16 Sep 2022 01:00:12 -0400 Received: from mail-pg1-x536.google.com (mail-pg1-x536.google.com [IPv6:2607:f8b0:4864:20::536]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 77822D132 for ; Thu, 15 Sep 2022 21:59:34 -0700 (PDT) Received: by mail-pg1-x536.google.com with SMTP id h188so19274569pgc.12 for ; Thu, 15 Sep 2022 21:59:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-disposition:mime-version:message-id:subject:cc:to:from:date :from:to:cc:subject:date; bh=t0avrKIMP0UQaUH/P/OoHzGEYHXZaMR0kwqmmd/PGrw=; b=imkqTNF+Yke7W7jY0bHZs9SmVvA4j6gEmyDu61nARhqn2CSaxIm0KLesuxnlhwKDJy ABj3IwuWXuKa/2CL0k1pAalQPIajKITl9yi3yZtajsAppKYGDDrAW9k7gRfejHTSpllc f/lQsqjXfPx4mCScgWDs+JRnoG7UUiU1WIF1KFdyh1JID5U48dYAmytoVEV1ZfIQbilL fO7ngeyWyuv5k9b0m2nyn4JF+ukwKFCaatNY+31/KwFbxSSTqDFvgCi0R5E00K16DY60 cq4Mso5mKNaIi3XO+EWRuMZ7f8lqr77a8qWCjKQLhnpU+UKKaGL++kUNfnkOxlDmTWXQ SsTg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-disposition:mime-version:message-id:subject:cc:to:from:date :x-gm-message-state:from:to:cc:subject:date; bh=t0avrKIMP0UQaUH/P/OoHzGEYHXZaMR0kwqmmd/PGrw=; b=Yesnb3hL7RDaMU6hc2Vsiw2QN1UjMlFDU6Emr4tleaEuAWtakhwl+0CsUtwfBSyDdO Cv1tYRR2fCP5ZRMhpEBI0ipPgqI0TiuK6YFZjg0sQYPkvcb2YBMplyUtJy+J1VuJRPcb nKDDO0es6M5eDyMbfCQlgWSjAJYfDqN8FbPtfetfo3JZrK3Ui9UjNT2AmpVRdkb9+8rb 1Jo0WdznrmBHQnA0czRl9GjX7q6CYPB+atXI+4rvxDJD+Ampjn5VW2+UsufIULqh8wDo JGIPec8U3j/5x0QzBlLnGcER8umnZSZmbFom7tU6ecVXG6v5EDJsih8s0QD/JdZxoBXl Pj0Q== X-Gm-Message-State: ACrzQf1YhRoFosZotIDSa+DVYqXB4PlNt8ES905IJkQh2NazfarfEZUX lcgkXpMLqANIgMtf2pK8XZvyNZJ5pao= X-Google-Smtp-Source: AMsMyM5u93dSNsfVZsbYAtR/44h2kS4kcpQ1ikDFS3USbfrXomX4+HHjHreQM742zef/tmKDch8bdw== X-Received: by 2002:a05:6a00:8c8:b0:52c:887d:fa25 with SMTP id s8-20020a056a0008c800b0052c887dfa25mr2965286pfu.86.1663304373635; Thu, 15 Sep 2022 21:59:33 -0700 (PDT) Received: from ubuntu ([175.124.254.119]) by smtp.gmail.com with ESMTPSA id f16-20020aa79d90000000b005403b8f4bacsm13261160pfq.137.2022.09.15.21.59.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 15 Sep 2022 21:59:33 -0700 (PDT) Date: Thu, 15 Sep 2022 21:59:29 -0700 From: Hyunwoo Kim To: laforge@gnumonks.org, arnd@arndb.de, gregkh@linuxfoundation.org Cc: linux-kernel@vger.kernel.org, imv4bel@gmail.com Subject: [PATCH] char: pcmcia: cm4000_cs: Fix use-after-free in cm4000_fops Message-ID: <20220916045929.GA188153@ubuntu> MIME-Version: 1.0 Content-Disposition: inline Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" A race condition may occur if the user physically removes the pcmcia device while calling open() for this char device node. This is a race condition between the cmm_open() function and the cm4000_detach() function, which may eventually result in UAF. So, add a refcount check to cm4000_detach() to free the "dev" structure after the char device node is close()d. Signed-off-by: Hyunwoo Kim --- drivers/char/pcmcia/cm4000_cs.c | 73 ++++++++++++++++++++++----------- 1 file changed, 50 insertions(+), 23 deletions(-) diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_c= s.c index adaec8fd4b16..3cb0342189d3 100644 --- a/drivers/char/pcmcia/cm4000_cs.c +++ b/drivers/char/pcmcia/cm4000_cs.c @@ -104,6 +104,8 @@ static int major; /* major number we get from the kern= el */ =20 struct cm4000_dev { struct pcmcia_device *p_dev; + struct mutex lock; + struct kref refcnt; =20 unsigned char atr[MAX_ATR]; unsigned char rbuf[512]; @@ -146,6 +148,9 @@ struct cm4000_dev { =20 #define ZERO_DEV(dev) memset(&((dev)->init), 0, sizeof((dev)->init)) =20 +static void stop_monitor(struct cm4000_dev *dev); +static void cm4000_delete(struct kref *kref); + static struct pcmcia_device *dev_table[CM4000_MAX_DEV]; static struct class *cmm_class; =20 @@ -416,6 +421,30 @@ static struct card_fixup card_fixups[] =3D { }, }; =20 + +static void cm4000_delete(struct kref *kref) +{ + struct cm4000_dev *dev =3D container_of(kref, struct cm4000_dev, refcnt); + struct pcmcia_device *link =3D dev->p_dev; + int devno; + + /* find device */ + for (devno =3D 0; devno < CM4000_MAX_DEV; devno++) + if (dev_table[devno] =3D=3D link) + break; + if (devno =3D=3D CM4000_MAX_DEV) + return; + + stop_monitor(dev); + + cm4000_release(link); + + dev_table[devno] =3D NULL; + kfree(dev); + + device_destroy(cmm_class, MKDEV(major, devno)); +} + static void set_cardparameter(struct cm4000_dev *dev) { int i; @@ -1632,16 +1661,18 @@ static int cmm_open(struct inode *inode, struct fil= e *filp) mutex_lock(&cmm_mutex); link =3D dev_table[minor]; if (link =3D=3D NULL || !pcmcia_dev_present(link)) { - ret =3D -ENODEV; - goto out; + mutex_unlock(&cmm_mutex); + return -ENODEV; } =20 if (link->open) { - ret =3D -EBUSY; - goto out; + mutex_unlock(&cmm_mutex); + return -EBUSY; } =20 dev =3D link->priv; + mutex_lock(&dev->lock); + filp->private_data =3D dev; =20 DEBUGP(2, dev, "-> cmm_open(device=3D%d.%d process=3D%s,%d)\n", @@ -1660,8 +1691,9 @@ static int cmm_open(struct inode *inode, struct file = *filp) * inserted) */ if (filp->f_flags & O_NONBLOCK) { - ret =3D -EAGAIN; - goto out; + mutex_unlock(&cmm_mutex); + mutex_unlock(&dev->lock); + return -EAGAIN; } =20 dev->mdelay =3D T_50MSEC; @@ -1673,8 +1705,12 @@ static int cmm_open(struct inode *inode, struct file= *filp) =20 DEBUGP(2, dev, "<- cmm_open\n"); ret =3D stream_open(inode, filp); -out: + + kref_get(&dev->refcnt); + mutex_unlock(&cmm_mutex); + mutex_unlock(&dev->lock); + return ret; } =20 @@ -1703,6 +1739,8 @@ static int cmm_close(struct inode *inode, struct file= *filp) link->open =3D 0; /* only one open per device */ wake_up(&dev->devq); /* socket removed? */ =20 + kref_put(&dev->refcnt, cm4000_delete); + DEBUGP(2, dev, "cmm_close\n"); return 0; } @@ -1808,6 +1846,8 @@ static int cm4000_probe(struct pcmcia_device *link) init_waitqueue_head(&dev->ioq); init_waitqueue_head(&dev->atrq); init_waitqueue_head(&dev->readq); + kref_init(&dev->refcnt); + mutex_init(&dev->lock); =20 ret =3D cm4000_config(link, i); if (ret) { @@ -1824,23 +1864,10 @@ static int cm4000_probe(struct pcmcia_device *link) static void cm4000_detach(struct pcmcia_device *link) { struct cm4000_dev *dev =3D link->priv; - int devno; - - /* find device */ - for (devno =3D 0; devno < CM4000_MAX_DEV; devno++) - if (dev_table[devno] =3D=3D link) - break; - if (devno =3D=3D CM4000_MAX_DEV) - return; - - stop_monitor(dev); =20 - cm4000_release(link); - - dev_table[devno] =3D NULL; - kfree(dev); - - device_destroy(cmm_class, MKDEV(major, devno)); + mutex_lock(&dev->lock); + kref_put(&dev->refcnt, cm4000_delete); + mutex_unlock(&dev->lock); =20 return; } --=20 2.25.1