From patchwork Wed Nov 30 09:05:55 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bartosz Golaszewski X-Patchwork-Id: 27697 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:adf:f944:0:0:0:0:0 with SMTP id q4csp815691wrr; Wed, 30 Nov 2022 01:15:50 -0800 (PST) X-Google-Smtp-Source: AA0mqf6JkufdrRtKgm32EixwTVSo+oUaP4FuZwFj6paMxGRlNdmj06e3Z93Zic5jgu9cw889ZWqi X-Received: by 2002:aa7:d518:0:b0:46a:727f:b659 with SMTP id y24-20020aa7d518000000b0046a727fb659mr29719955edq.420.1669799750660; Wed, 30 Nov 2022 01:15:50 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1669799750; cv=none; d=google.com; s=arc-20160816; b=Qa0PVCm3yUz4zkxJib82E8SF7HWG9IreIZzouL5ajWl0003QYqlMuMRdadKAh51Rhf H2jruVhOxOVJIfzmNNtuuY3LKjQ0ttJnPvXuk7xJG/iDsG7/L5FowiH824XbrVeVmsrY 9q1aj9eOp+wHjwEMNIF9lZR03OL7EQDBswhydw2B7cNS+EOAYdO1UcEkJts9zBjhcuaM t9LWEo6m1c6FyENIr55AULFavAurOZyh94RUh01i4CWmiDi//wWXVsJq3e4+zPOjuHiX 5RAtJ9n/vAPX9zBloXv8F+StJ8BKZcEmzB6VpzCB0sW1SsweT2qe0JDkY3hqKsXLHuzW U9yw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=ysZtnS5xnth5w0LemSDO+wCvjLXEmog+vjYaOIZiWJQ=; b=RyZAZdmQGMbbjhuosDhjM4fLFBu0U05Y3o9FBZIylcyZYvhGslP9xOLRZSNIYK/wNu ySQAGGQhBuXDCPbEb2HtFg/ZMr3LqucbP5eVopBwtkeOhuZ6QvTY2UW51utcnIvm5fVT LGIFa3OKUOzJLag84QsZgmMqcMrG7plwdpmgMoV9+WZxB0xUhfH114JtVRFr3BovdLno KLJ5YmuMKRrg4UA8QYCESIya4k8lpTAL8Ev7K0OvxsnywW7iQsrFEdbxOd5et38QiYjl v1BOeYYaI8vhDT8d+liap7Wfq14PFwDMtTWZmUcUjApWoXEY19S/EUCNcm7Kw7H2coMn jmBA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@bgdev-pl.20210112.gappssmtp.com header.s=20210112 header.b=kok9z+Bw; 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 Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id xf11-20020a17090731cb00b00792e39c31dcsi447490ejb.988.2022.11.30.01.15.25; Wed, 30 Nov 2022 01:15:50 -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=@bgdev-pl.20210112.gappssmtp.com header.s=20210112 header.b=kok9z+Bw; 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 Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229922AbiK3JGG (ORCPT + 99 others); Wed, 30 Nov 2022 04:06:06 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39324 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229921AbiK3JGC (ORCPT ); Wed, 30 Nov 2022 04:06:02 -0500 Received: from mail-wm1-x334.google.com (mail-wm1-x334.google.com [IPv6:2a00:1450:4864:20::334]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3BB4648436 for ; Wed, 30 Nov 2022 01:06:01 -0800 (PST) Received: by mail-wm1-x334.google.com with SMTP id o7-20020a05600c510700b003cffc0b3374so897760wms.0 for ; Wed, 30 Nov 2022 01:06:01 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bgdev-pl.20210112.gappssmtp.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=ysZtnS5xnth5w0LemSDO+wCvjLXEmog+vjYaOIZiWJQ=; b=kok9z+BwG6oqUKdWE2UkF36lieA5csfb1Agg11U6bznRNMDgpRnqDzb1jeSIU8gs8O WTIePJiOdCVxdjmuVGBX642nh8SR2pNcjQTeYiPm7NtJQ/jENCTwxqdse3Sk/fkK+Cwt wFfAFqdl1mz7leMXkwFWfmc84oWFdui/X14xPiSk7LITXUqWEUn1WcgvdlhYEII0am4P rYo0Y5f4DA2IQ+gGUNYPw/2WA6SANhApcEO7BbuVCsIoxgKajJDvi3nz4awkQhRQsy3V cvQX3DAwoI8JKYXqV965hE2k/cvS3I4XIpZSmsoBbBPYX89i8xOpYvYMTdzJ4B0C5L5l 6lWw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=ysZtnS5xnth5w0LemSDO+wCvjLXEmog+vjYaOIZiWJQ=; b=FEpBNWgtYLRR/rIIeoAr0FyJKJuzGjbLri/qgUbk3OzOtebzXbgHNGq/jLLf05qX6W woesuZ0dD8nUkaw93tsgm+Afg3IKgOqs4UEt9jVqOxbHaTtyKNFR6gEyd8C2jpCmDGOd D7EYkkNlrPm5Ss7/ka/Lj/UHgDRF0P19lLSuoeFuEWj7P19LrqDAYizR1z987JhIJBCf LM41USqxQ4tphodpknrgAwWDCap3EXGP1KAgvWIxort7GaMW9IKNmmvOTMpZgvc0U0ws vSKiuzvwkQ/eEFiiEkwzs4w91BikCyWpJtI1YhR5Ez+AglNQlpb2zvNHC3d/2447uTNr UldQ== X-Gm-Message-State: ANoB5pkw4/nisyLGS2p/0LstTaaB2zkMwvooDRwvEdUNkQdEXwjim15/ ovEZ0jq+6vU78W4siPkU+oVhEuAi/9yJVQ== X-Received: by 2002:a7b:cbc6:0:b0:3c6:b650:34dd with SMTP id n6-20020a7bcbc6000000b003c6b65034ddmr37797612wmi.45.1669799159746; Wed, 30 Nov 2022 01:05:59 -0800 (PST) Received: from brgl-uxlite.home ([2a01:cb1d:334:ac00:458c:6db9:e033:a468]) by smtp.gmail.com with ESMTPSA id v24-20020a05600c4d9800b003cfbe1da539sm1168841wmp.36.2022.11.30.01.05.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 30 Nov 2022 01:05:59 -0800 (PST) From: Bartosz Golaszewski To: Kent Gibson , Linus Walleij , Andy Shevchenko Cc: linux-gpio@vger.kernel.org, linux-kernel@vger.kernel.org, Bartosz Golaszewski Subject: [PATCH v4 1/2] gpiolib: cdev: fix NULL-pointer dereferences Date: Wed, 30 Nov 2022 10:05:55 +0100 Message-Id: <20221130090556.40280-2-brgl@bgdev.pl> X-Mailer: git-send-email 2.37.2 In-Reply-To: <20221130090556.40280-1-brgl@bgdev.pl> References: <20221130090556.40280-1-brgl@bgdev.pl> MIME-Version: 1.0 X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_NONE 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: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1750911943192494556?= X-GMAIL-MSGID: =?utf-8?q?1750911943192494556?= From: Bartosz Golaszewski There are several places where we can crash the kernel by requesting lines, unbinding the GPIO device, then calling any of the system calls relevant to the GPIO character device's annonymous file descriptors: ioctl(), read(), poll(). While I observed it with the GPIO simulator, it will also happen for any of the GPIO devices that can be hot-unplugged - for instance any HID GPIO expander (e.g. CP2112). This affects both v1 and v2 uAPI. This fixes it partially by checking if gdev->chip is not NULL but it doesn't entirely remedy the situation as we still have a race condition in which another thread can remove the device after the check. Fixes: d7c51b47ac11 ("gpio: userspace ABI for reading/writing GPIO lines") Fixes: 3c0d9c635ae2 ("gpiolib: cdev: support GPIO_V2_GET_LINE_IOCTL and GPIO_V2_LINE_GET_VALUES_IOCTL") Fixes: aad955842d1c ("gpiolib: cdev: support GPIO_V2_GET_LINEINFO_IOCTL and GPIO_V2_GET_LINEINFO_WATCH_IOCTL") Fixes: a54756cb24ea ("gpiolib: cdev: support GPIO_V2_LINE_SET_CONFIG_IOCTL") Fixes: 7b8e00d98168 ("gpiolib: cdev: support GPIO_V2_LINE_SET_VALUES_IOCTL") Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko --- drivers/gpio/gpiolib-cdev.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 0cb6b468f364..911d91668903 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -201,6 +201,9 @@ static long linehandle_ioctl(struct file *file, unsigned int cmd, unsigned int i; int ret; + if (!lh->gdev->chip) + return -ENODEV; + switch (cmd) { case GPIOHANDLE_GET_LINE_VALUES_IOCTL: /* NOTE: It's okay to read values of output lines */ @@ -1384,6 +1387,9 @@ static long linereq_ioctl(struct file *file, unsigned int cmd, struct linereq *lr = file->private_data; void __user *ip = (void __user *)arg; + if (!lr->gdev->chip) + return -ENODEV; + switch (cmd) { case GPIO_V2_LINE_GET_VALUES_IOCTL: return linereq_get_values(lr, ip); @@ -1410,6 +1416,9 @@ static __poll_t linereq_poll(struct file *file, struct linereq *lr = file->private_data; __poll_t events = 0; + if (!lr->gdev->chip) + return 0; + poll_wait(file, &lr->wait, wait); if (!kfifo_is_empty_spinlocked_noirqsave(&lr->events, @@ -1429,6 +1438,9 @@ static ssize_t linereq_read(struct file *file, ssize_t bytes_read = 0; int ret; + if (!lr->gdev->chip) + return -ENODEV; + if (count < sizeof(le)) return -EINVAL; @@ -1716,6 +1728,9 @@ static __poll_t lineevent_poll(struct file *file, struct lineevent_state *le = file->private_data; __poll_t events = 0; + if (!le->gdev->chip) + return 0; + poll_wait(file, &le->wait, wait); if (!kfifo_is_empty_spinlocked_noirqsave(&le->events, &le->wait.lock)) @@ -1740,6 +1755,9 @@ static ssize_t lineevent_read(struct file *file, ssize_t ge_size; int ret; + if (!le->gdev->chip) + return -ENODEV; + /* * When compatible system call is being used the struct gpioevent_data, * in case of at least ia32, has different size due to the alignment @@ -1821,6 +1839,9 @@ static long lineevent_ioctl(struct file *file, unsigned int cmd, void __user *ip = (void __user *)arg; struct gpiohandle_data ghd; + if (!le->gdev->chip) + return -ENODEV; + /* * We can get the value for an event line but not set it, * because it is input by definition. @@ -2407,6 +2428,9 @@ static __poll_t lineinfo_watch_poll(struct file *file, struct gpio_chardev_data *cdev = file->private_data; __poll_t events = 0; + if (!cdev->gdev->chip) + return 0; + poll_wait(file, &cdev->wait, pollt); if (!kfifo_is_empty_spinlocked_noirqsave(&cdev->events, @@ -2425,6 +2449,9 @@ static ssize_t lineinfo_watch_read(struct file *file, char __user *buf, int ret; size_t event_size; + if (!cdev->gdev->chip) + return -ENODEV; + #ifndef CONFIG_GPIO_CDEV_V1 event_size = sizeof(struct gpio_v2_line_info_changed); if (count < event_size) From patchwork Wed Nov 30 09:05:56 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bartosz Golaszewski X-Patchwork-Id: 27696 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:adf:f944:0:0:0:0:0 with SMTP id q4csp815652wrr; Wed, 30 Nov 2022 01:15:47 -0800 (PST) X-Google-Smtp-Source: AA0mqf78uC/7I2/MzzIwmIPtnvm6EoJSkUOq5Zdt3DF30iDSWEuAA8qe/W0pbnfj/qJbpS8Bpwdf X-Received: by 2002:a17:906:a89:b0:7bd:cc82:9abe with SMTP id y9-20020a1709060a8900b007bdcc829abemr9063617ejf.452.1669799747290; Wed, 30 Nov 2022 01:15:47 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1669799747; cv=none; d=google.com; s=arc-20160816; b=AamGeXKLZh0KkIJYll7s7AuQg2kyYStlULLhScQPaTPjsqf2xXVInfWq3YfVdBC9rg 2jMfR1OXYxLmBPJU97EtcnsBmHM4obf3jxYdWR1SMu/qHkO4IPBahETpYJTbbeQseDIE AVtsbyKy3O+JenSFaEzyqYLqXYfdFWMe7DgHVISfOswaI05rj3BqUAPYs6+Andgqr3P5 bivAgRZkWjIUVbRx9sr+HUsiJJQk6ZbzKb2bYs/jWjYTGQtzmRIh7R2LzGwGvJuNsBWX FoDIsNGm3kC7JwHRxx8wAqQDFRUrQgcNE25OTBy751RPCYG3yiPSDYSRdj+noQQgltTV mnmg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=AaWBRwWXBehjrIvGP4MX42mw8iXGH6zrJDG1Zu/gB00=; b=JX3KfOlVWqkBQO2IWJSBqPwtXHpBSTv37Lrsfp9f+MrkwVVPNHY7EYganwYbGf01F1 LL7H7k6JHIS1fQa2DY5lIvJUYnm46bY7NMasXDIU1ITWO73bEitrQgPbqd5EgCER+W+h y6a1Ez0oGc3qDFfbgiG5gu6yGPJCWLyUV+RAVu0/dpoZKltWVrJcKh9M6J7w1W7Wpbwj iT5Q0F6pj79o0CL3yVNX859JJZgq7bgEwJI6E1fpu55RQhiTV5pJHtvn45thhEjuwVPS fFF0XWUfSqFRp7CeiMrXDXkODJ0vbDo0QWU1FRxrPTtcrwuziEeukHqvQsjUH4tU1Owm rLFg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@bgdev-pl.20210112.gappssmtp.com header.s=20210112 header.b=qxjvlJdD; 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 Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id dd10-20020a1709069b8a00b007c08e3b0e1fsi902527ejc.246.2022.11.30.01.15.23; Wed, 30 Nov 2022 01:15:47 -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=@bgdev-pl.20210112.gappssmtp.com header.s=20210112 header.b=qxjvlJdD; 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 Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230105AbiK3JGJ (ORCPT + 99 others); Wed, 30 Nov 2022 04:06:09 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39342 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229790AbiK3JGD (ORCPT ); Wed, 30 Nov 2022 04:06:03 -0500 Received: from mail-wm1-x332.google.com (mail-wm1-x332.google.com [IPv6:2a00:1450:4864:20::332]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3E4E249B7A for ; Wed, 30 Nov 2022 01:06:02 -0800 (PST) Received: by mail-wm1-x332.google.com with SMTP id ay14-20020a05600c1e0e00b003cf6ab34b61so883067wmb.2 for ; Wed, 30 Nov 2022 01:06:02 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bgdev-pl.20210112.gappssmtp.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=AaWBRwWXBehjrIvGP4MX42mw8iXGH6zrJDG1Zu/gB00=; b=qxjvlJdDcX0EfPoIFyfLI3DER//zESGiJCLneb7tfs4yy5ZtVLmXUzTG66aw37WO1p gLHClHLAnT56UpSvPYpKj3eF552ZaXyrXMq0qIFQ+kpY+A/DehjYZMeMWqDaXS6bz7L2 qv3YNpYkELL87INh5ybaUM+PEDdkieqTAbmTHQPPt9VDDdRKzfQjSNMtltlqRoXa3ImR YGonit1nmC1y+rauofRyB75A1lISZuAVa33OVjknkfeVTfP7C+wktONQmT3zFMWhpbbu M6WFsHsqnenTAeKl473+nbczhhjzAlEfbuz10cMpw7UvG5xhRTnc4ce/ZHmELBQ8YVmu Q3Aw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=AaWBRwWXBehjrIvGP4MX42mw8iXGH6zrJDG1Zu/gB00=; b=OYUlE6YAO/BXshAH8LWKrCN+SFndhKjqlX83qziTV+H7iN3ZSjp8GLDC9AoDNuIBRN AjOaEz9cUor6y8ino2/AD8oRnXiyNC3/1uyQQktUauBRcSkL0eobgQRQtDTVdLpstuDk nFWCmpXyhDW+Sg7GqWcYIKlmYFTXZ2x9F684VshgZJ9qXtYztZgQCCBN8A1qGL/qPUgg WQv4eC8oj4qaqmxKUwoOAg3d+QXBD8w+hwjGMjvP+fvUP8TrgX8wu8XShwnhn/jZFqjl vlesRZn+W7vPOsTHcwJu8F2H5e631EpsFkJCM2AytaKyXoRY0K7y3tbgdMINUdMjHvir C1iQ== X-Gm-Message-State: ANoB5pk0IELR0FAZ1fiCG8PjkDsFFJphO4jz6/nlsITkGGDsNjRkKa1U S6ia0GL2PhJGPYW8uAIXLHeXaw== X-Received: by 2002:a1c:4b12:0:b0:3cf:5237:c0be with SMTP id y18-20020a1c4b12000000b003cf5237c0bemr42878687wma.163.1669799160694; Wed, 30 Nov 2022 01:06:00 -0800 (PST) Received: from brgl-uxlite.home ([2a01:cb1d:334:ac00:458c:6db9:e033:a468]) by smtp.gmail.com with ESMTPSA id v24-20020a05600c4d9800b003cfbe1da539sm1168841wmp.36.2022.11.30.01.05.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 30 Nov 2022 01:06:00 -0800 (PST) From: Bartosz Golaszewski To: Kent Gibson , Linus Walleij , Andy Shevchenko Cc: linux-gpio@vger.kernel.org, linux-kernel@vger.kernel.org, Bartosz Golaszewski Subject: [PATCH v4 2/2] gpiolib: protect the GPIO device against being dropped while in use by user-space Date: Wed, 30 Nov 2022 10:05:56 +0100 Message-Id: <20221130090556.40280-3-brgl@bgdev.pl> X-Mailer: git-send-email 2.37.2 In-Reply-To: <20221130090556.40280-1-brgl@bgdev.pl> References: <20221130090556.40280-1-brgl@bgdev.pl> MIME-Version: 1.0 X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_NONE 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: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1750911939787915871?= X-GMAIL-MSGID: =?utf-8?q?1750911939787915871?= From: Bartosz Golaszewski While any of the GPIO cdev syscalls is in progress, the kernel can call gpiochip_remove() (for instance, when a USB GPIO expander is disconnected) which will set gdev->chip to NULL after which any subsequent access will cause a crash. To avoid that: use an RW-semaphore in which the syscalls take it for reading (so that we don't needlessly prohibit the user-space from calling syscalls simultaneously) while gpiochip_remove() takes it for writing so that it can only happen once all syscalls return. Fixes: d7c51b47ac11 ("gpio: userspace ABI for reading/writing GPIO lines") Fixes: 3c0d9c635ae2 ("gpiolib: cdev: support GPIO_V2_GET_LINE_IOCTL and GPIO_V2_LINE_GET_VALUES_IOCTL") Fixes: aad955842d1c ("gpiolib: cdev: support GPIO_V2_GET_LINEINFO_IOCTL and GPIO_V2_GET_LINEINFO_WATCH_IOCTL") Fixes: a54756cb24ea ("gpiolib: cdev: support GPIO_V2_LINE_SET_CONFIG_IOCTL") Fixes: 7b8e00d98168 ("gpiolib: cdev: support GPIO_V2_LINE_SET_VALUES_IOCTL") Signed-off-by: Bartosz Golaszewski Reviewed-by: Kent Gibson Reviewed-by: Andy Shevchenko --- drivers/gpio/gpiolib-cdev.c | 163 +++++++++++++++++++++++++++++++----- drivers/gpio/gpiolib.c | 4 + drivers/gpio/gpiolib.h | 5 ++ 3 files changed, 150 insertions(+), 22 deletions(-) diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 911d91668903..98904832f4b4 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -84,6 +84,50 @@ struct linehandle_state { GPIOHANDLE_REQUEST_OPEN_DRAIN | \ GPIOHANDLE_REQUEST_OPEN_SOURCE) +typedef __poll_t (*poll_fn)(struct file *, struct poll_table_struct *); +typedef long (*ioctl_fn)(struct file *, unsigned int, unsigned long); +typedef ssize_t (*read_fn)(struct file *, char __user *, + size_t count, loff_t *); + +static __poll_t call_poll_locked(struct file *file, + struct poll_table_struct *wait, + struct gpio_device *gdev, poll_fn func) +{ + __poll_t ret; + + down_read(&gdev->sem); + ret = func(file, wait); + up_read(&gdev->sem); + + return ret; +} + +static long call_ioctl_locked(struct file *file, unsigned int cmd, + unsigned long arg, struct gpio_device *gdev, + ioctl_fn func) +{ + long ret; + + down_read(&gdev->sem); + ret = func(file, cmd, arg); + up_read(&gdev->sem); + + return ret; +} + +static ssize_t call_read_locked(struct file *file, char __user *buf, + size_t count, loff_t *f_ps, + struct gpio_device *gdev, read_fn func) +{ + ssize_t ret; + + down_read(&gdev->sem); + ret = func(file, buf, count, f_ps); + up_read(&gdev->sem); + + return ret; +} + static int linehandle_validate_flags(u32 flags) { /* Return an error if an unknown flag is set */ @@ -191,8 +235,8 @@ static long linehandle_set_config(struct linehandle_state *lh, return 0; } -static long linehandle_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) +static long linehandle_ioctl_unlocked(struct file *file, unsigned int cmd, + unsigned long arg) { struct linehandle_state *lh = file->private_data; void __user *ip = (void __user *)arg; @@ -250,6 +294,15 @@ static long linehandle_ioctl(struct file *file, unsigned int cmd, } } +static long linehandle_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct linehandle_state *lh = file->private_data; + + return call_ioctl_locked(file, cmd, arg, lh->gdev, + linehandle_ioctl_unlocked); +} + #ifdef CONFIG_COMPAT static long linehandle_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) @@ -1381,8 +1434,8 @@ static long linereq_set_config(struct linereq *lr, void __user *ip) return ret; } -static long linereq_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) +static long linereq_ioctl_unlocked(struct file *file, unsigned int cmd, + unsigned long arg) { struct linereq *lr = file->private_data; void __user *ip = (void __user *)arg; @@ -1402,6 +1455,15 @@ static long linereq_ioctl(struct file *file, unsigned int cmd, } } +static long linereq_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct linereq *lr = file->private_data; + + return call_ioctl_locked(file, cmd, arg, lr->gdev, + linereq_ioctl_unlocked); +} + #ifdef CONFIG_COMPAT static long linereq_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) @@ -1410,8 +1472,8 @@ static long linereq_ioctl_compat(struct file *file, unsigned int cmd, } #endif -static __poll_t linereq_poll(struct file *file, - struct poll_table_struct *wait) +static __poll_t linereq_poll_unlocked(struct file *file, + struct poll_table_struct *wait) { struct linereq *lr = file->private_data; __poll_t events = 0; @@ -1428,10 +1490,16 @@ static __poll_t linereq_poll(struct file *file, return events; } -static ssize_t linereq_read(struct file *file, - char __user *buf, - size_t count, - loff_t *f_ps) +static __poll_t linereq_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct linereq *lr = file->private_data; + + return call_poll_locked(file, wait, lr->gdev, linereq_poll_unlocked); +} + +static ssize_t linereq_read_unlocked(struct file *file, char __user *buf, + size_t count, loff_t *f_ps) { struct linereq *lr = file->private_data; struct gpio_v2_line_event le; @@ -1485,6 +1553,15 @@ static ssize_t linereq_read(struct file *file, return bytes_read; } +static ssize_t linereq_read(struct file *file, char __user *buf, + size_t count, loff_t *f_ps) +{ + struct linereq *lr = file->private_data; + + return call_read_locked(file, buf, count, f_ps, lr->gdev, + linereq_read_unlocked); +} + static void linereq_free(struct linereq *lr) { unsigned int i; @@ -1722,8 +1799,8 @@ struct lineevent_state { (GPIOEVENT_REQUEST_RISING_EDGE | \ GPIOEVENT_REQUEST_FALLING_EDGE) -static __poll_t lineevent_poll(struct file *file, - struct poll_table_struct *wait) +static __poll_t lineevent_poll_unlocked(struct file *file, + struct poll_table_struct *wait) { struct lineevent_state *le = file->private_data; __poll_t events = 0; @@ -1739,15 +1816,21 @@ static __poll_t lineevent_poll(struct file *file, return events; } +static __poll_t lineevent_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct lineevent_state *le = file->private_data; + + return call_poll_locked(file, wait, le->gdev, lineevent_poll_unlocked); +} + struct compat_gpioeevent_data { compat_u64 timestamp; u32 id; }; -static ssize_t lineevent_read(struct file *file, - char __user *buf, - size_t count, - loff_t *f_ps) +static ssize_t lineevent_read_unlocked(struct file *file, char __user *buf, + size_t count, loff_t *f_ps) { struct lineevent_state *le = file->private_data; struct gpioevent_data ge; @@ -1815,6 +1898,15 @@ static ssize_t lineevent_read(struct file *file, return bytes_read; } +static ssize_t lineevent_read(struct file *file, char __user *buf, + size_t count, loff_t *f_ps) +{ + struct lineevent_state *le = file->private_data; + + return call_read_locked(file, buf, count, f_ps, le->gdev, + lineevent_read_unlocked); +} + static void lineevent_free(struct lineevent_state *le) { if (le->irq) @@ -1832,8 +1924,8 @@ static int lineevent_release(struct inode *inode, struct file *file) return 0; } -static long lineevent_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) +static long lineevent_ioctl_unlocked(struct file *file, unsigned int cmd, + unsigned long arg) { struct lineevent_state *le = file->private_data; void __user *ip = (void __user *)arg; @@ -1864,6 +1956,15 @@ static long lineevent_ioctl(struct file *file, unsigned int cmd, return -EINVAL; } +static long lineevent_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct lineevent_state *le = file->private_data; + + return call_ioctl_locked(file, cmd, arg, le->gdev, + lineevent_ioctl_unlocked); +} + #ifdef CONFIG_COMPAT static long lineevent_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) @@ -2422,8 +2523,8 @@ static int lineinfo_changed_notify(struct notifier_block *nb, return NOTIFY_OK; } -static __poll_t lineinfo_watch_poll(struct file *file, - struct poll_table_struct *pollt) +static __poll_t lineinfo_watch_poll_unlocked(struct file *file, + struct poll_table_struct *pollt) { struct gpio_chardev_data *cdev = file->private_data; __poll_t events = 0; @@ -2440,8 +2541,17 @@ static __poll_t lineinfo_watch_poll(struct file *file, return events; } -static ssize_t lineinfo_watch_read(struct file *file, char __user *buf, - size_t count, loff_t *off) +static __poll_t lineinfo_watch_poll(struct file *file, + struct poll_table_struct *pollt) +{ + struct gpio_chardev_data *cdev = file->private_data; + + return call_poll_locked(file, pollt, cdev->gdev, + lineinfo_watch_poll_unlocked); +} + +static ssize_t lineinfo_watch_read_unlocked(struct file *file, char __user *buf, + size_t count, loff_t *off) { struct gpio_chardev_data *cdev = file->private_data; struct gpio_v2_line_info_changed event; @@ -2519,6 +2629,15 @@ static ssize_t lineinfo_watch_read(struct file *file, char __user *buf, return bytes_read; } +static ssize_t lineinfo_watch_read(struct file *file, char __user *buf, + size_t count, loff_t *off) +{ + struct gpio_chardev_data *cdev = file->private_data; + + return call_read_locked(file, buf, count, off, cdev->gdev, + lineinfo_watch_read_unlocked); +} + /** * gpio_chrdev_open() - open the chardev for ioctl operations * @inode: inode for this chardev diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 4756ea08894f..e0e73bd756ca 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -731,6 +731,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, spin_unlock_irqrestore(&gpio_lock, flags); BLOCKING_INIT_NOTIFIER_HEAD(&gdev->notifier); + init_rwsem(&gdev->sem); #ifdef CONFIG_PINCTRL INIT_LIST_HEAD(&gdev->pin_ranges); @@ -865,6 +866,8 @@ void gpiochip_remove(struct gpio_chip *gc) unsigned long flags; unsigned int i; + down_write(&gdev->sem); + /* FIXME: should the legacy sysfs handling be moved to gpio_device? */ gpiochip_sysfs_unregister(gdev); gpiochip_free_hogs(gc); @@ -899,6 +902,7 @@ void gpiochip_remove(struct gpio_chip *gc) * gone. */ gcdev_unregister(gdev); + up_write(&gdev->sem); put_device(&gdev->dev); } EXPORT_SYMBOL_GPL(gpiochip_remove); diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index d900ecdbac46..9ad68a0adf4a 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -15,6 +15,7 @@ #include #include #include +#include #define GPIOCHIP_NAME "gpiochip" @@ -39,6 +40,9 @@ * @list: links gpio_device:s together for traversal * @notifier: used to notify subscribers about lines being requested, released * or reconfigured + * @sem: protects the structure from a NULL-pointer dereference of @chip by + * user-space operations when the device gets unregistered during + * a hot-unplug event * @pin_ranges: range of pins served by the GPIO driver * * This state container holds most of the runtime variable data @@ -60,6 +64,7 @@ struct gpio_device { void *data; struct list_head list; struct blocking_notifier_head notifier; + struct rw_semaphore sem; #ifdef CONFIG_PINCTRL /*