From nobody Fri Apr 3 03:48:31 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 9B81FECAAD8 for ; Fri, 16 Sep 2022 13:50:23 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231247AbiIPNuV (ORCPT ); Fri, 16 Sep 2022 09:50:21 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41312 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231210AbiIPNuO (ORCPT ); Fri, 16 Sep 2022 09:50:14 -0400 Received: from mail-pf1-x432.google.com (mail-pf1-x432.google.com [IPv6:2607:f8b0:4864:20::432]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7159A558C5 for ; Fri, 16 Sep 2022 06:50:08 -0700 (PDT) Received: by mail-pf1-x432.google.com with SMTP id l65so21293321pfl.8 for ; Fri, 16 Sep 2022 06:50:08 -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=mg2W9f9guObOQ3nCr3SRnmWfaXFbUbY8UMTcwJAN+3o=; b=e9oEDoBeKMaq5Xg9ri6m/f8YEXTD8oGbUcX3BbARWV9fdfK8nSDspeUsHvmMTyzSBx 3miEyDBqdk8z1Ub4HvK484Bc3H3FSqoRXQhOSJ+fdcePlBi8UDWuO68AVTEkkp/hHitO ygiG+acNQ9mnqtwFnITS7ajc3LF6AWlWHf/e1lFh3jLVaUTs75XCiQrA7xo58J444M9I FwJTN6ny6onDgLtdlEcaNKMoJv2hR2JbOdTzRpmoIQEfzYfIwNTMzuM2KlKZSNcv7yQ/ EMEFhlGfK0XPFMZI/fOdRTkuk6bZhCfGG2HV1iGgyq8vRy6Eirdj2x4SGYE7Sryxv/Nc 8RRA== 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=mg2W9f9guObOQ3nCr3SRnmWfaXFbUbY8UMTcwJAN+3o=; b=IehfVIvqDEWV2fMWsJnsJoLOsM6GgF8dkr7FJadoH0TFtgxC4B82aYA7NxqgWU1vZg EmThgUsFcI+pz5bdV5A4YeY2omDEf6/q4Xppq5q0HaTuIB409BidhggBeCZkCH85jBO8 WsI1PwAjMLL3IY59in7D7lPPJMCmw7ICtepc4W1gtCp+NakFSkoOwagwWZPJcxjORf/6 0xeDdX6dd9QcHuy5pOdqYPJ17Tk5gwlVjNU+r3UTmBlmCqesUmsdPa1U+vVWzb3iW8HB yly6GsaZoh6+0oORlnbFRp3ZHfIZurMsrtr/YqdqhumEAcb4EFzqeic/8NExMrrjGpLu EpyQ== X-Gm-Message-State: ACrzQf2IorYgjUSsDETYxpC0LaEck6cVPyCaoFtKI0SYZRQq2xCaZ4+7 hbYiNBvDYA8pCO3UhhEXmjE= X-Google-Smtp-Source: AMsMyM4Nv77D56j8XB41KqJxvapYrQJM3bAvOrOMDEeHwMOle9M/kxZkgCwGhU+XBThOosGz1eElkA== X-Received: by 2002:a62:a50e:0:b0:54b:7915:8ff2 with SMTP id v14-20020a62a50e000000b0054b79158ff2mr1617677pfm.30.1663336207845; Fri, 16 Sep 2022 06:50:07 -0700 (PDT) Received: from ubuntu ([175.124.254.119]) by smtp.gmail.com with ESMTPSA id i24-20020a17090acf9800b0020071acaecasm1524226pju.42.2022.09.16.06.50.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 16 Sep 2022 06:50:07 -0700 (PDT) Date: Fri, 16 Sep 2022 06:50:03 -0700 From: Hyunwoo Kim To: laforge@gnumonks.org Cc: linux-kernel@vger.kernel.org, imv4bel@gmail.com, arnd@arndb.de, gregkh@linuxfoundation.or Subject: [PATCH v2] char: pcmcia: cm4000_cs: Fix use-after-free in cm4000_fops Message-ID: <20220916135003.GA234913@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 | 81 +++++++++++++++++++++++---------- 1 file changed, 58 insertions(+), 23 deletions(-) diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_c= s.c index adaec8fd4b16..9e54ad892056 100644 --- a/drivers/char/pcmcia/cm4000_cs.c +++ b/drivers/char/pcmcia/cm4000_cs.c @@ -55,6 +55,7 @@ } while (0) =20 static DEFINE_MUTEX(cmm_mutex); +static DEFINE_MUTEX(remove_mutex); =20 #define T_1SEC (HZ) #define T_10MSEC msecs_to_jiffies(10) @@ -104,6 +105,8 @@ static int major; /* major number we get from the kern= el */ =20 struct cm4000_dev { struct pcmcia_device *p_dev; + struct kref refcnt; + int removed; =20 unsigned char atr[MAX_ATR]; unsigned char rbuf[512]; @@ -146,6 +149,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 +422,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; @@ -1629,21 +1659,30 @@ static int cmm_open(struct inode *inode, struct fil= e *filp) if (minor >=3D CM4000_MAX_DEV) return -ENODEV; =20 + mutex_lock(&remove_mutex); 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); + mutex_unlock(&remove_mutex); + return -ENODEV; } =20 if (link->open) { - ret =3D -EBUSY; - goto out; + mutex_unlock(&cmm_mutex); + mutex_unlock(&remove_mutex); + return -EBUSY; } =20 dev =3D link->priv; filp->private_data =3D dev; =20 + if (dev->removed =3D=3D 1) { + mutex_unlock(&cmm_mutex); + mutex_unlock(&remove_mutex); + return -ENODEV; + } + DEBUGP(2, dev, "-> cmm_open(device=3D%d.%d process=3D%s,%d)\n", imajor(inode), minor, current->comm, current->pid); =20 @@ -1660,8 +1699,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(&remove_mutex); + return -EAGAIN; } =20 dev->mdelay =3D T_50MSEC; @@ -1673,8 +1713,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(&remove_mutex); + return ret; } =20 @@ -1703,6 +1747,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 +1854,7 @@ 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); =20 ret =3D cm4000_config(link, i); if (ret) { @@ -1824,23 +1871,11 @@ 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; =20 - /* 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)); + mutex_lock(&remove_mutex); + kref_put(&dev->refcnt, cm4000_delete); + dev->removed =3D 1; + mutex_unlock(&remove_mutex); =20 return; } --=20 2.25.1