Received: by 2002:ab2:6816:0:b0:1f9:5764:f03e with SMTP id t22csp2292487lqo; Mon, 20 May 2024 00:40:25 -0700 (PDT) X-Forwarded-Encrypted: i=3; AJvYcCUWJ6ECLyD4WBu2Ny23U8urXX4GjiShiV3NTcogQPEADTZlEvmto+kErfPrNY9bI8r1YyndD1S87dOVFcuXsLMxXBVeqGRGAUZ7JDqPPQ== X-Google-Smtp-Source: AGHT+IG/oIg9fEOpTWlJNDZPGEUwZ87+ory0d8i+9wXH3hQOUmj/BKLrKiqONwBaq02r6qt06442 X-Received: by 2002:a05:6214:568b:b0:6aa:ec88:6536 with SMTP id 6a1803df08f44-6aaec886604mr18440426d6.40.1716190825061; Mon, 20 May 2024 00:40:25 -0700 (PDT) ARC-Seal: i=2; a=rsa-sha256; t=1716190825; cv=pass; d=google.com; s=arc-20160816; b=zFVRAx8/ZpsIo54bIsOWmZaa0FmPGV5zEshl74q/aAGVogR5+7oCtMR9W8AlIuJH4K uEPhH19TTow/Erhd/BlcghPw8WE+58D248OGlIm4Zqu0sMZb5FiI/ZfPotnf3evFMv5H zdHHEANbAkXDf9Q7yWHQ4QPSQugeVcCx/blLBf4XHWf5/2NQjSGG9wNvhs+hwVPiO5OD aqYR9G/fpx1Inc/FsLqEpe/L59BT3MH+JZ/1zYxFQlNFLtyP+aou1PU9FrT6fB1gM/Ki bxRDcTNYhMU32JnkwPEggIanidFAE0XtQzAn1uXcqUGiPdt/EiEbjaEP00dRAa+VvAKa OREQ== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:in-reply-to:from:content-language :references:cc:to:subject:user-agent:mime-version:list-unsubscribe :list-subscribe:list-id:precedence:date:message-id:dkim-signature; bh=IlmzNrUbsrPyph3pr6xCrO3cu14PxLbK25E2+fDJGUE=; fh=VvJBipBEabZLyCpPRM9XygDwqTVFQZHqClQWlp0FMM8=; b=I81qmFkr1sf9f+OE0xO1edmZ0pzun8jwJC8JMSZryDqKAxqlx6ojXGD7fWf/dc4e0L qwV3x/0/peiGsC9am3gUmIswtrOxSvtJOQwjhFAtJaPnbc13Ylhn1nhP0mvTm16HxLAm Cy8GIDaNa4/sYdBNfDaXKjBpv8P0fk5MePc24DxtbgdPDvcr/pXODGudl3VffegKoyhR ojJSqc6qb2mtOFQxbDhMrVPcJHSyDrbJVLQ12b8BHQi8BACOHUErqLYKgHSLB5VlILTU hcTvKkqNWQVS2pGlneJxW1byiA5yZoTZZmVub7xvO02KYYa99CHfIyInYu3KRYR4ydE/ btrg==; dara=google.com ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@linux.alibaba.com header.s=default header.b=T0Jvr2mg; arc=pass (i=1 spf=pass spfdomain=linux.alibaba.com dkim=pass dkdomain=linux.alibaba.com dmarc=pass fromdomain=linux.alibaba.com); spf=pass (google.com: domain of linux-kernel+bounces-183468-linux.lists.archive=gmail.com@vger.kernel.org designates 2604:1380:45d1:ec00::1 as permitted sender) smtp.mailfrom="linux-kernel+bounces-183468-linux.lists.archive=gmail.com@vger.kernel.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linux.alibaba.com Return-Path: Received: from ny.mirrors.kernel.org (ny.mirrors.kernel.org. [2604:1380:45d1:ec00::1]) by mx.google.com with ESMTPS id 6a1803df08f44-6a15f2b6bf3si249368626d6.371.2024.05.20.00.40.24 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 20 May 2024 00:40:25 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel+bounces-183468-linux.lists.archive=gmail.com@vger.kernel.org designates 2604:1380:45d1:ec00::1 as permitted sender) client-ip=2604:1380:45d1:ec00::1; Authentication-Results: mx.google.com; dkim=pass header.i=@linux.alibaba.com header.s=default header.b=T0Jvr2mg; arc=pass (i=1 spf=pass spfdomain=linux.alibaba.com dkim=pass dkdomain=linux.alibaba.com dmarc=pass fromdomain=linux.alibaba.com); spf=pass (google.com: domain of linux-kernel+bounces-183468-linux.lists.archive=gmail.com@vger.kernel.org designates 2604:1380:45d1:ec00::1 as permitted sender) smtp.mailfrom="linux-kernel+bounces-183468-linux.lists.archive=gmail.com@vger.kernel.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linux.alibaba.com Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ny.mirrors.kernel.org (Postfix) with ESMTPS id B741D1C212A2 for ; Mon, 20 May 2024 07:40:24 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id BD35612B8B; Mon, 20 May 2024 07:40:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.alibaba.com header.i=@linux.alibaba.com header.b="T0Jvr2mg" Received: from out30-118.freemail.mail.aliyun.com (out30-118.freemail.mail.aliyun.com [115.124.30.118]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 72BBB1B978; Mon, 20 May 2024 07:40:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=115.124.30.118 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716190816; cv=none; b=TEuFELjmzcy07+d8hFIitgWKq8VsQy8AjUAKe4pCJdm8KoU0/OCUlXbUuAOwkI/U4ff2RLwDa4rDrw8fxmOK8Z0MhzuQ+mLzL3ZMWuJG+HiSo9WzDQXTdlF5NzH0PR6LoeOOAz+LyWeUNc0sP4qnWnzHUp9wXhazEcN6LklJ4nI= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716190816; c=relaxed/simple; bh=l00/+uOMDkhRE/QdP2A2ykbsURjs/s57h6reqFA2rek=; h=Message-ID:Date:MIME-Version:Subject:To:Cc:References:From: In-Reply-To:Content-Type; b=V27L1nB1oeT/E/4oHgsTjHW0ANYHq7c9Hql37F0NQDrO6/UATRm2T4ANOAj+/tjPD6wIr1zZqr6LqyG7XkiGpyxT5khO6Jy/gACHJaM7Z/YKU42IBoSmiwZ9c+0MZBF9SQVp8uJQ8Qkmcd4yij2s7XY0DdLJV4Dy0QJpyTbKmzw= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.alibaba.com; spf=pass smtp.mailfrom=linux.alibaba.com; dkim=pass (1024-bit key) header.d=linux.alibaba.com header.i=@linux.alibaba.com header.b=T0Jvr2mg; arc=none smtp.client-ip=115.124.30.118 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.alibaba.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.alibaba.com DKIM-Signature:v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.alibaba.com; s=default; t=1716190811; h=Message-ID:Date:MIME-Version:Subject:To:From:Content-Type; bh=IlmzNrUbsrPyph3pr6xCrO3cu14PxLbK25E2+fDJGUE=; b=T0Jvr2mgAz8W+W34INrVYIkc1EaLrfhtK+5xgbTQ91kCiZSNUlOABhiHypRoB+dcdKrI1LzMt0hOMXmNl/e3OIi8ndmAjAacYZlYgShTzvUbgasPtmQFa9z57/WvXCOFNJ0hRHGh2NVsPBPcO9dGdCgkdWev+t7aCR23g63A9WQ= X-Alimail-AntiSpam:AC=PASS;BC=-1|-1;BR=01201311R131e4;CH=green;DM=||false|;DS=||;FP=0|-1|-1|-1|0|-1|-1|-1;HT=maildocker-contentspam033037067110;MF=jefflexu@linux.alibaba.com;NM=1;PH=DS;RN=14;SR=0;TI=SMTPD_---0W6nhN.g_1716189872; Received: from 30.221.148.185(mailfrom:jefflexu@linux.alibaba.com fp:SMTPD_---0W6nhN.g_1716189872) by smtp.aliyun-inc.com; Mon, 20 May 2024 15:24:34 +0800 Message-ID: <35561c99-c978-4cf6-82e9-d1308c82a7ff@linux.alibaba.com> Date: Mon, 20 May 2024 15:24:31 +0800 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PATCH v2 03/12] cachefiles: fix slab-use-after-free in cachefiles_ondemand_get_fd() To: libaokun@huaweicloud.com, netfs@lists.linux.dev, dhowells@redhat.com, jlayton@kernel.org Cc: hsiangkao@linux.alibaba.com, zhujia.zj@bytedance.com, linux-erofs@lists.ozlabs.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, yangerkun@huawei.com, houtao1@huawei.com, yukuai3@huawei.com, wozizhi@huawei.com, Baokun Li References: <20240515084601.3240503-1-libaokun@huaweicloud.com> <20240515084601.3240503-4-libaokun@huaweicloud.com> Content-Language: en-US From: Jingbo Xu In-Reply-To: <20240515084601.3240503-4-libaokun@huaweicloud.com> Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit On 5/15/24 4:45 PM, libaokun@huaweicloud.com wrote: > From: Baokun Li > > We got the following issue in a fuzz test of randomly issuing the restore > command: > > ================================================================== > BUG: KASAN: slab-use-after-free in cachefiles_ondemand_daemon_read+0x609/0xab0 > Write of size 4 at addr ffff888109164a80 by task ondemand-04-dae/4962 > > CPU: 11 PID: 4962 Comm: ondemand-04-dae Not tainted 6.8.0-rc7-dirty #542 > Call Trace: > kasan_report+0x94/0xc0 > cachefiles_ondemand_daemon_read+0x609/0xab0 > vfs_read+0x169/0xb50 > ksys_read+0xf5/0x1e0 > > Allocated by task 626: > __kmalloc+0x1df/0x4b0 > cachefiles_ondemand_send_req+0x24d/0x690 > cachefiles_create_tmpfile+0x249/0xb30 > cachefiles_create_file+0x6f/0x140 > cachefiles_look_up_object+0x29c/0xa60 > cachefiles_lookup_cookie+0x37d/0xca0 > fscache_cookie_state_machine+0x43c/0x1230 > [...] > > Freed by task 626: > kfree+0xf1/0x2c0 > cachefiles_ondemand_send_req+0x568/0x690 > cachefiles_create_tmpfile+0x249/0xb30 > cachefiles_create_file+0x6f/0x140 > cachefiles_look_up_object+0x29c/0xa60 > cachefiles_lookup_cookie+0x37d/0xca0 > fscache_cookie_state_machine+0x43c/0x1230 > [...] > ================================================================== > > Following is the process that triggers the issue: > > mount | daemon_thread1 | daemon_thread2 > ------------------------------------------------------------ > cachefiles_ondemand_init_object > cachefiles_ondemand_send_req > REQ_A = kzalloc(sizeof(*req) + data_len) > wait_for_completion(&REQ_A->done) > > cachefiles_daemon_read > cachefiles_ondemand_daemon_read > REQ_A = cachefiles_ondemand_select_req > cachefiles_ondemand_get_fd > copy_to_user(_buffer, msg, n) > process_open_req(REQ_A) > ------ restore ------ > cachefiles_ondemand_restore > xas_for_each(&xas, req, ULONG_MAX) > xas_set_mark(&xas, CACHEFILES_REQ_NEW); > > cachefiles_daemon_read > cachefiles_ondemand_daemon_read > REQ_A = cachefiles_ondemand_select_req > > write(devfd, ("copen %u,%llu", msg->msg_id, size)); > cachefiles_ondemand_copen > xa_erase(&cache->reqs, id) > complete(&REQ_A->done) > kfree(REQ_A) > cachefiles_ondemand_get_fd(REQ_A) > fd = get_unused_fd_flags > file = anon_inode_getfile > fd_install(fd, file) > load = (void *)REQ_A->msg.data; > load->fd = fd; > // load UAF !!! > > This issue is caused by issuing a restore command when the daemon is still > alive, which results in a request being processed multiple times thus > triggering a UAF. So to avoid this problem, add an additional reference > count to cachefiles_req, which is held while waiting and reading, and then > released when the waiting and reading is over. > > Note that since there is only one reference count for waiting, we need to > avoid the same request being completed multiple times, so we can only > complete the request if it is successfully removed from the xarray. Sorry the above description makes me confused. As the same request may be got by different daemon threads multiple times, the introduced refcount mechanism can't protect it from being completed multiple times (which is expected). The refcount only protects it from being freed multiple times. > > Fixes: e73fa11a356c ("cachefiles: add restore command to recover inflight ondemand read requests") > Suggested-by: Hou Tao > Signed-off-by: Baokun Li > Reviewed-by: Jia Zhu > --- > fs/cachefiles/internal.h | 1 + > fs/cachefiles/ondemand.c | 44 ++++++++++++++++++++++------------------ > 2 files changed, 25 insertions(+), 20 deletions(-) > > diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h > index d33169f0018b..7745b8abc3aa 100644 > --- a/fs/cachefiles/internal.h > +++ b/fs/cachefiles/internal.h > @@ -138,6 +138,7 @@ static inline bool cachefiles_in_ondemand_mode(struct cachefiles_cache *cache) > struct cachefiles_req { > struct cachefiles_object *object; > struct completion done; > + refcount_t ref; > int error; > struct cachefiles_msg msg; > }; > diff --git a/fs/cachefiles/ondemand.c b/fs/cachefiles/ondemand.c > index fd49728d8bae..56d12fe4bf73 100644 > --- a/fs/cachefiles/ondemand.c > +++ b/fs/cachefiles/ondemand.c > @@ -4,6 +4,12 @@ > #include > #include "internal.h" > > +static inline void cachefiles_req_put(struct cachefiles_req *req) > +{ > + if (refcount_dec_and_test(&req->ref)) > + kfree(req); > +} > + > static int cachefiles_ondemand_fd_release(struct inode *inode, > struct file *file) > { > @@ -299,7 +305,6 @@ ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache, > { > struct cachefiles_req *req; > struct cachefiles_msg *msg; > - unsigned long id = 0; > size_t n; > int ret = 0; > XA_STATE(xas, &cache->reqs, cache->req_id_next); > @@ -330,41 +335,39 @@ ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache, > > xas_clear_mark(&xas, CACHEFILES_REQ_NEW); > cache->req_id_next = xas.xa_index + 1; > + refcount_inc(&req->ref); > xa_unlock(&cache->reqs); > > - id = xas.xa_index; > - > if (msg->opcode == CACHEFILES_OP_OPEN) { > ret = cachefiles_ondemand_get_fd(req); > if (ret) { > cachefiles_ondemand_set_object_close(req->object); > - goto error; > + goto out; > } > } > > - msg->msg_id = id; > + msg->msg_id = xas.xa_index; > msg->object_id = req->object->ondemand->ondemand_id; > > if (copy_to_user(_buffer, msg, n) != 0) { > ret = -EFAULT; > if (msg->opcode == CACHEFILES_OP_OPEN) > close_fd(((struct cachefiles_open *)msg->data)->fd); > - goto error; > } > - > - /* CLOSE request has no reply */ > - if (msg->opcode == CACHEFILES_OP_CLOSE) { > - xa_erase(&cache->reqs, id); > - complete(&req->done); > +out: > + /* Remove error request and CLOSE request has no reply */ > + if (ret || msg->opcode == CACHEFILES_OP_CLOSE) { > + xas_reset(&xas); > + xas_lock(&xas); > + if (xas_load(&xas) == req) { Just out of curiosity... How could xas_load(&xas) doesn't equal to req? > + req->error = ret; > + complete(&req->done); > + xas_store(&xas, NULL); > + } > + xas_unlock(&xas); > } > - > - return n; > - > -error: > - xa_erase(&cache->reqs, id); > - req->error = ret; > - complete(&req->done); > - return ret; > + cachefiles_req_put(req); > + return ret ? ret : n; > } This is actually a combination of a fix and a cleanup which combines the logic of removing error request and the CLOSE requests into one place. Also it relies on the cleanup made in patch 2 ("cachefiles: remove err_put_fd tag in cachefiles_ondemand_daemon_read()"), making it difficult to be atomatically back ported to the stable (as patch 2 is not marked as "Fixes"). Thus could we make the fix first, and then make the cleanup. > > typedef int (*init_req_fn)(struct cachefiles_req *req, void *private); > @@ -394,6 +397,7 @@ static int cachefiles_ondemand_send_req(struct cachefiles_object *object, > goto out; > } > > + refcount_set(&req->ref, 1); > req->object = object; > init_completion(&req->done); > req->msg.opcode = opcode; > @@ -455,7 +459,7 @@ static int cachefiles_ondemand_send_req(struct cachefiles_object *object, > wake_up_all(&cache->daemon_pollwq); > wait_for_completion(&req->done); > ret = req->error; > - kfree(req); > + cachefiles_req_put(req); > return ret; > out: > /* Reset the object to close state in error handling path. Don't we need to also convert "kfree(req)" to cachefiles_req_put(req) for the error path of cachefiles_ondemand_send_req()? ``` out: /* Reset the object to close state in error handling path. * If error occurs after creating the anonymous fd, * cachefiles_ondemand_fd_release() will set object to close. */ if (opcode == CACHEFILES_OP_OPEN) cachefiles_ondemand_set_object_close(object); kfree(req); return ret; ``` -- Thanks, Jingbo