Received: by 2002:a05:6520:4211:b029:f4:110d:56bc with SMTP id o17csp1584535lkv; Wed, 19 May 2021 13:22:48 -0700 (PDT) X-Google-Smtp-Source: ABdhPJziUmS1wiaAwPHDTFEYautFvZlJ+gd7JqwnPeDtUmNscTzrtu4k/nIKGYFgpKN3ZR9YIkFP X-Received: by 2002:a05:6402:5174:: with SMTP id d20mr913956ede.248.1621455768654; Wed, 19 May 2021 13:22:48 -0700 (PDT) ARC-Seal: i=2; a=rsa-sha256; t=1621455768; cv=pass; d=google.com; s=arc-20160816; b=xm8ihQdV0ieO/f93xOpQjylwBStG5EhrYiQ8mGB+1NkfgtIdDRyVhZB1lXDuSQC846 78SEwMU89XlCunCYW8+aGHgUJvs3wtfv/39mxc1G/hNJ2lVVAwfrnUpa00Wj5/mOnO77 oTV68vAdxruOkhKd6Bc+ZR3cLVI/CGOiT6OC+BhWDlDngdtG4Qjh2pomCYKQwBaWOqZ1 +xFDXj2JBX9x6XMjvxQ5J/5wu51c2IWNPYEFoP+ilrIvzoqgBqH/GYkD7ixWCY6bjcC9 3fBM0S3ubcYnyImQ+m/6jR1cbo57qVHDr1KW3RrVXXmrzr607QTK4+VnE+I35BmkoUGb FpJA== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:in-reply-to:content-disposition:mime-version :references:message-id:subject:cc:to:from:date:dkim-signature; bh=kPdwiwE6lZBN2kAHXvK07bmx9Y+Is8JOiZE4Jiblt2A=; b=M39Lf+Zgmh0RnAUJX4uK+AlnTDp03//eaD5XcIEFNAXe/FEHcgjamV6l4kbve1WOxg DG1+r4yM55CevguMOGAcdBA6du4rFrO78I4McRQt8gmx40M+5tVMQARhTQLJAH8IMIee rkh3C6yfZI1HJCdDp7EIrCp2CDbuCi8MkXhUzb4XryKEa0cAdGa6ocyWvcKaYO/unBXK SYFWsX6UbHEKjYsfopfxa5FFbvQwrJioF2VyIuxUVsOJxy3RdYPMdm7v88lI4GK1rrWt vtIuEO7B32/1cUHMphmtrNGHGech3Sy4/83fI6fuhovCEpz11V2PvAHFdQnnJmggBJsy OcjQ== ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@anirudhrb.com header.s=zoho header.b=mUs0JgSi; arc=pass (i=1 spf=pass spfdomain=anirudhrb.com dkim=pass dkdomain=anirudhrb.com dmarc=pass fromdomain=anirudhrb.com>); spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id cx9si165135edb.174.2021.05.19.13.22.25; Wed, 19 May 2021 13:22:48 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@anirudhrb.com header.s=zoho header.b=mUs0JgSi; arc=pass (i=1 spf=pass spfdomain=anirudhrb.com dkim=pass dkdomain=anirudhrb.com dmarc=pass fromdomain=anirudhrb.com>); spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230338AbhESS5y (ORCPT + 99 others); Wed, 19 May 2021 14:57:54 -0400 Received: from sender4-of-o53.zoho.com ([136.143.188.53]:21340 "EHLO sender4-of-o53.zoho.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230145AbhESS5x (ORCPT ); Wed, 19 May 2021 14:57:53 -0400 ARC-Seal: i=1; a=rsa-sha256; t=1621450580; cv=none; d=zohomail.com; s=zohoarc; b=RbxLJCHc9AtpzfRDZOuWQPZ4rX6hiharBEuk0jewMgnEE2nC6rQkSxkLHFMAn+6VWtOvUW/Qy4ZGFa4OCLi3W69mvVszGcjnWWAc+MOr6klq9HcBgkmwsiJmtchrnxq5Y9faEb9st6i4Tc5zRNjTkF2k9kAMrE8QII+Fr1lmg58= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1621450580; h=Content-Type:Cc:Date:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:To; bh=kPdwiwE6lZBN2kAHXvK07bmx9Y+Is8JOiZE4Jiblt2A=; b=DJwPb10rLbRLyvfmwb9i2pmRpu/TNg03wmm0Fya0oMHK9J6nzQ3i8SaQFMl4KmSNzJmYkv2ZgksjxRczxxa4VUldqsyd8GTwD2SSeqHn7bFdL324loVysHoZnbRfnRuowIzH0uO5EZFHYDDVCzUZ4wcmxpT0x+tKsqT0ZtmJozw= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=anirudhrb.com; spf=pass smtp.mailfrom=mail@anirudhrb.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1621450580; s=zoho; d=anirudhrb.com; i=mail@anirudhrb.com; h=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version:Content-Type:In-Reply-To; bh=kPdwiwE6lZBN2kAHXvK07bmx9Y+Is8JOiZE4Jiblt2A=; b=mUs0JgSiDZcuWtMykbTy1/tjFFJAVUxj+XsMWmawwvuxtrPV2kiqXlih79ssbJxd BsfLC5GdLKlQoU+Rql+zqbcsRiMF9FNlXiJShZAbYb90kNnejkhWZpiF4AVwGVslDq1 pEl4aav3GgdLZ8pHCMARPajI4DZ6wVhpQRtTEoDE= Received: from anirudhrb.com (49.207.58.139 [49.207.58.139]) by mx.zohomail.com with SMTPS id 1621450578322369.60726490083687; Wed, 19 May 2021 11:56:18 -0700 (PDT) Date: Thu, 20 May 2021 00:26:12 +0530 From: Anirudh Rayabharam To: Hillf Danton Cc: Luis Chamberlain , Greg Kroah-Hartman , "Rafael J. Wysocki" , Junyong Sun , linux-kernel-mentees@lists.linuxfoundation.org, syzbot+de271708674e2093097b@syzkaller.appspotmail.com, linux-kernel@vger.kernel.org Subject: Re: [PATCH v4] firmware_loader: fix use-after-free in firmware_fallback_sysfs Message-ID: References: <20210518155921.4181-1-mail@anirudhrb.com> <20210519091047.1477-1-hdanton@sina.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20210519091047.1477-1-hdanton@sina.com> X-ZohoMailClient: External Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Wed, May 19, 2021 at 05:10:47PM +0800, Hillf Danton wrote: > On Tue, 18 May 2021 21:29:20 +0530 Anirudh Rayabharam wrote: > >This use-after-free happens when a fw_priv object has been freed but > >hasn't been removed from the pending list (pending_fw_head). The next > >time fw_load_sysfs_fallback tries to insert into the list, it ends up > >accessing the pending_list member of the previoiusly freed fw_priv. > > > >The root cause here is that all code paths that abort the fw load > >don't delete it from the pending list. For example: > > > > _request_firmware() > > -> fw_abort_batch_reqs() > > -> fw_state_aborted() > > > >To fix this, delete the fw_priv from the list in __fw_set_state() if > >the new state is DONE or ABORTED. This way, all aborts will remove > >the fw_priv from the list. Accordingly, remove calls to list_del_init > >that were being made before calling fw_state_(aborted|done)(). > > > >Also, in fw_load_sysfs_fallback, don't add the fw_priv to the list > >if it is already aborted. Instead, just jump out and return early. > > > >Fixes: bcfbd3523f3c ("firmware: fix a double abort case with fw_load_sysfs_fallback") > >Reported-by: syzbot+de271708674e2093097b@syzkaller.appspotmail.com > >Tested-by: syzbot+de271708674e2093097b@syzkaller.appspotmail.com > >Signed-off-by: Anirudh Rayabharam > >--- > > > >Changes in v4: > >Documented the reasons behind the error codes returned from > >fw_sysfs_wait_timeout() as suggested by Luis Chamberlain. > > > >Changes in v3: > >Modified the patch to incorporate suggestions by Luis Chamberlain in > >order to fix the root cause instead of applying a "band-aid" kind of > >fix. > >https://lore.kernel.org/lkml/20210403013143.GV4332@42.do-not-panic.com/ > > > >Changes in v2: > >1. Fixed 1 error and 1 warning (in the commit message) reported by > >checkpatch.pl. The error was regarding the format for referring to > >another commit "commit ("oneline")". The warning was for line > >longer than 75 chars. > > > >--- > > drivers/base/firmware_loader/fallback.c | 46 ++++++++++++++++++------- > > drivers/base/firmware_loader/firmware.h | 6 +++- > > drivers/base/firmware_loader/main.c | 2 ++ > > 3 files changed, 40 insertions(+), 14 deletions(-) > > > >diff --git a/drivers/base/firmware_loader/fallback.c b/drivers/base/firmware_loader/fallback.c > >index 91899d185e31..f244c7b89ba5 100644 > >--- a/drivers/base/firmware_loader/fallback.c > >+++ b/drivers/base/firmware_loader/fallback.c > >@@ -70,7 +70,31 @@ static inline bool fw_sysfs_loading(struct fw_priv *fw_priv) > > > > static inline int fw_sysfs_wait_timeout(struct fw_priv *fw_priv, long timeout) > > { > >- return __fw_state_wait_common(fw_priv, timeout); > >+ int ret = __fw_state_wait_common(fw_priv, timeout); > >+ > >+ /* > >+ * A signal could be sent to abort a wait. Consider Android's init > >+ * gettting a SIGCHLD, which in turn was the same process issuing the > >+ * sysfs store call for the fallback. In such cases we want to be able > >+ * to tell apart in userspace when a signal caused a failure on the > >+ * wait. In such cases we'd get -ERESTARTSYS. > >+ * > >+ * Likewise though another race can happen and abort the load earlier. > >+ * > >+ * In either case the situation is interrupted so we just inform > >+ * userspace of that and we end things right away. > >+ * > >+ * When we really time out just tell userspace it should try again, > >+ * perhaps later. > >+ */ > >+ if (ret == -ERESTARTSYS || fw_state_is_aborted(fw_priv)) > >+ ret = -EINTR; > >+ else if (ret == -ETIMEDOUT) > >+ ret = -EAGAIN; > >+ else if (fw_priv->is_paged_buf && !fw_priv->data) > >+ ret = -ENOMEM; > >+ > >+ return ret; > > } > > > > struct fw_sysfs { > >@@ -91,10 +115,9 @@ static void __fw_load_abort(struct fw_priv *fw_priv) > > * There is a small window in which user can write to 'loading' > > * between loading done and disappearance of 'loading' > > */ > >- if (fw_sysfs_done(fw_priv)) > >+ if (fw_state_is_aborted(fw_priv) || fw_sysfs_done(fw_priv)) > > return; > > > >- list_del_init(&fw_priv->pending_list); > > fw_state_aborted(fw_priv); > > } > > > >@@ -280,7 +303,6 @@ static ssize_t firmware_loading_store(struct device *dev, > > * Same logic as fw_load_abort, only the DONE bit > > * is ignored and we set ABORT only on failure. > > */ > >- list_del_init(&fw_priv->pending_list); > > if (rc) { > > fw_state_aborted(fw_priv); > > written = rc; > >@@ -513,6 +535,11 @@ static int fw_load_sysfs_fallback(struct fw_sysfs *fw_sysfs, long timeout) > > } > > > > mutex_lock(&fw_lock); > >+ if (fw_state_is_aborted(fw_priv)) { > >+ mutex_unlock(&fw_lock); > >+ retval = -EINTR; > >+ goto out; > >+ } > > list_add(&fw_priv->pending_list, &pending_fw_head); > > This looks like prepare_to_wait(). > > > mutex_unlock(&fw_lock); > > > >@@ -526,20 +553,13 @@ static int fw_load_sysfs_fallback(struct fw_sysfs *fw_sysfs, long timeout) > > } > > > > retval = fw_sysfs_wait_timeout(fw_priv, timeout); > >- if (retval < 0 && retval != -ENOENT) { > >+ if (retval < 0) { > > mutex_lock(&fw_lock); > > fw_load_abort(fw_sysfs); > __fw_load_abort(); > fw_state_aborted(); > __fw_state_set(); > > Is this your finish_wait() part? See what you add below. > > > mutex_unlock(&fw_lock); > > } > > > >- if (fw_state_is_aborted(fw_priv)) { > >- if (retval == -ERESTARTSYS) > >- retval = -EINTR; > >- else > >- retval = -EAGAIN; > >- } else if (fw_priv->is_paged_buf && !fw_priv->data) > >- retval = -ENOMEM; > >- > >+out: > > device_del(f_dev); > > err_put_dev: > > put_device(f_dev); > >diff --git a/drivers/base/firmware_loader/firmware.h b/drivers/base/firmware_loader/firmware.h > >index 63bd29fdcb9c..36bdb413c998 100644 > >--- a/drivers/base/firmware_loader/firmware.h > >+++ b/drivers/base/firmware_loader/firmware.h > >@@ -117,8 +117,12 @@ static inline void __fw_state_set(struct fw_priv *fw_priv, > > > > WRITE_ONCE(fw_st->status, status); > > > >- if (status == FW_STATUS_DONE || status == FW_STATUS_ABORTED) > >+ if (status == FW_STATUS_DONE || status == FW_STATUS_ABORTED) { > >+#ifdef CONFIG_FW_LOADER_USER_HELPER > >+ list_del_init(&fw_priv->pending_list); > >+#endif > > complete_all(&fw_st->completion); > >+ } > > Fine, apart from what you are fixing, you are adding something like > finish_wait() into the waker's backyard. Why are you calling > complete_all() on the waiter side? Sorry, I don't really get your point here. I did not add complete_all(). It was already there. Could you please elaborate? - Anirudh. > > > } > > > > static inline void fw_state_aborted(struct fw_priv *fw_priv) > >diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c > >index 4fdb8219cd08..68c549d71230 100644 > >--- a/drivers/base/firmware_loader/main.c > >+++ b/drivers/base/firmware_loader/main.c > >@@ -783,8 +783,10 @@ static void fw_abort_batch_reqs(struct firmware *fw) > > return; > > > > fw_priv = fw->priv; > >+ mutex_lock(&fw_lock); > > if (!fw_state_is_aborted(fw_priv)) > > fw_state_aborted(fw_priv); > >+ mutex_unlock(&fw_lock); > > } > > > > /* called from request_firmware() and request_firmware_work_func() */ > >-- > >2.26.2