From patchwork Wed Jul 14 13:47:49 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12376977 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id C7C55C07E9A for ; Wed, 14 Jul 2021 13:48:04 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A78A161374 for ; Wed, 14 Jul 2021 13:48:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232110AbhGNNuz (ORCPT ); Wed, 14 Jul 2021 09:50:55 -0400 Received: from mail.kernel.org ([198.145.29.99]:42906 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231543AbhGNNuy (ORCPT ); Wed, 14 Jul 2021 09:50:54 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 998F1613BE; Wed, 14 Jul 2021 13:48:00 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626270483; bh=vp5bXzXlnCDxY/OLVr7ZLCC70W2Er4KbPTvly6fC8eY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=S1F9m75Veld9IhxIiskErUdfRJx/tHSn/Xt2Gpjb6B2M07z0iJidmT1LER/cn8e+c Dic6lVsKzoRoP6VMOR0mfWFnQrHNock6EaTNeGAci7H0wmYSlJbL2qDpOvqOGfEwv1 3xZC9vLB7F1nDbscEEW4OdBrZu7IJRa53Vi2O7gTWxLIGCcH0hG6KOmDOHcwscgE2H NUv9AZPDZNuXv+RmLGyXgr6VoW2MxfITbckI2l2AFLHcT9Y39TO7/OJ415+xEm3QMc AETndup64IIsghIqKetcRUO0XC5pRVxmd7vKq79gxR/btu47PRenXOTJ/DK4zPIbsh AuEhD2fMcfgoQ== From: Christian Brauner To: linux-fsdevel@vger.kernel.org, Linus Torvalds , Dmitry Vyukov Cc: Christoph Hellwig , Al Viro , linux-kernel@vger.kernel.org, Christian Brauner , syzbot+283ce5a46486d6acdbaf@syzkaller.appspotmail.com, stable@kernel.org, syzkaller-bugs Subject: [PATCH 1/2] cgroup: verify that source is a string Date: Wed, 14 Jul 2021 15:47:49 +0200 Message-Id: <20210714134750.1223146-1-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: References: MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=2566; h=from:subject; bh=12+DNVCz6V74ceolxYGLmdvTW/UekPMGixUDyW+L3PU=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSS8e814IFz5c40mu3nerx+Jrnu6Q7Zqr1vx1tX63F5Wtnl+ XDs6O0pZGMS4GGTFFFkc2k3C5ZbzVGw2ytSAmcPKBDKEgYtTACbCt43hv8f5AiOm/GnLlOOUC+fevm jh0D+VMffl3BtbSs5sywxpl2Rk+LqoTuf91hkTniQZuB07rqvwrOYXa0Ufwwom6TKX174TWQA= X-Developer-Key: i=christian.brauner@ubuntu.com; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org From: Christian Brauner The following sequence can be used to trigger a UAF: int fscontext_fd = fsopen("cgroup"); int fd_null = open("/dev/null, O_RDONLY); int fsconfig(fscontext_fd, FSCONFIG_SET_FD, "source", fd_null); close_range(3, ~0U, 0); The cgroup v1 specific fs parser expects a string for the "source" parameter. However, it is perfectly legitimate to e.g. specify a file descriptor for the "source" parameter. The fs parser doesn't know what a filesystem allows there. So it's a bug to assume that "source" is always of type fs_value_is_string when it can reasonably also be fs_value_is_file. This assumption in the cgroup code causes a UAF because struct fs_parameter uses a union for the actual value. Access to that union is guarded by the param->type member. Since the cgroup paramter parser didn't check param->type but unconditionally moved param->string into fc->source a close on the fscontext_fd would trigger a UAF during put_fs_context() which frees fc->source thereby freeing the file stashed in param->file causing a UAF during a close of the fd_null. Fix this by verifying that param->type is actually a string and report an error if not. In follow up patches I'll add a new generic helper that can be used here and by other filesystems instead of this error-prone copy-pasta fix. But fixing it in here first makes backporting a it to stable a lot easier. Reported-by: syzbot+283ce5a46486d6acdbaf@syzkaller.appspotmail.com Cc: Christoph Hellwig Cc: Linus Torvalds Cc: Alexander Viro Cc: Dmitry Vyukov Cc: linux-fsdevel@vger.kernel.org Cc: Cc: syzkaller-bugs Signed-off-by: Christian Brauner --- kernel/cgroup/cgroup-v1.c | 2 ++ 1 file changed, 2 insertions(+) base-commit: e73f0f0ee7541171d89f2e2491130c7771ba58d3 diff --git a/kernel/cgroup/cgroup-v1.c b/kernel/cgroup/cgroup-v1.c index ee93b6e89587..527917c0b30b 100644 --- a/kernel/cgroup/cgroup-v1.c +++ b/kernel/cgroup/cgroup-v1.c @@ -912,6 +912,8 @@ int cgroup1_parse_param(struct fs_context *fc, struct fs_parameter *param) opt = fs_parse(fc, cgroup1_fs_parameters, param, &result); if (opt == -ENOPARAM) { if (strcmp(param->key, "source") == 0) { + if (param->type != fs_value_is_string) + return invalf(fc, "Non-string source"); if (fc->source) return invalf(fc, "Multiple sources not supported"); fc->source = param->string; From patchwork Wed Jul 14 13:47:50 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12376979 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id C542CC11F66 for ; Wed, 14 Jul 2021 13:48:07 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A8EE2613C1 for ; Wed, 14 Jul 2021 13:48:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232402AbhGNNu5 (ORCPT ); Wed, 14 Jul 2021 09:50:57 -0400 Received: from mail.kernel.org ([198.145.29.99]:42934 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232326AbhGNNu4 (ORCPT ); Wed, 14 Jul 2021 09:50:56 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 7E588613BF; Wed, 14 Jul 2021 13:48:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1626270485; bh=jV1Y9ahf/52JK4DMrb5fY352MNgzXbaxD0XC9h3PnQg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Cxgo6ogW6ZV9vsui6JPTNn6KZ/7exHMGE7vtnL9dQQoaQKusOW5doI9AcF4jBdtDh ack5BExdvkFU4jRr6TzT9atpCGSsW/btEu9RBLSP47dbjw03k+50zMoisTXUeWFMNC uj8OET2eZWQePgDJflBWWcEI3KyQ1JH+yLi9uvn0MdUc+bFIA7bpNC55x99hMiFYyI ZlbnrMb+8ZlPh8ybMhqQerzw6ZMShqiJcV2Xsn/4vaJ9CNuxECmqODi2Z0Mft6bgi7 jDclAZAepVHXaGP9wrL9+6/fdbbrpQf19ekzZChrqomBgRAxnENAaSGrHOE+9nRpDo d0LiqYXd9cbzA== From: Christian Brauner To: linux-fsdevel@vger.kernel.org, Linus Torvalds , Dmitry Vyukov Cc: Christoph Hellwig , Al Viro , linux-kernel@vger.kernel.org, Christian Brauner Subject: [PATCH 2/2] fs: add vfs_parse_fs_param_source() helper Date: Wed, 14 Jul 2021 15:47:50 +0200 Message-Id: <20210714134750.1223146-2-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210714134750.1223146-1-brauner@kernel.org> References: <20210714134750.1223146-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=5044; h=from:subject; bh=8c8204NioZlafLmPKwEmLocaMBtEqXVMoufavG6HxXE=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSS8e83YopLwVu/DBddfNSa3TsZqtFS0Xy99uWi7xuMzi33/ JvzW7yhlYRDjYpAVU2RxaDcJl1vOU7HZKFMDZg4rE8gQBi5OAZhI0gSGX0wdZvYXTla5rrthfMw/51 f5HFU+65XJJofnHO/NPfBdUYOR4UXBNOZ8jY3HNR7/msd5Qqv2gP2un4f2vfX88EjtaoLoTjYA X-Developer-Key: i=christian.brauner@ubuntu.com; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org From: Christian Brauner Add a simple helper that filesystems can use in their parameter parser to parse the "source" parameter. A few places open-coded this function and that already caused a bug in the cgroup v1 parser that we fixed. Let's make it harder to get this wrong by introducing a helper which performs all necessary checks. Link: https://syzkaller.appspot.com/bug?id=6312526aba5beae046fdae8f00399f87aab48b12 Cc: Christoph Hellwig Cc: Linus Torvalds Cc: Alexander Viro Cc: Dmitry Vyukov Cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner --- fs/fs_context.c | 54 +++++++++++++++++++++++++------------- include/linux/fs_context.h | 2 ++ kernel/cgroup/cgroup-v1.c | 14 ++++------ 3 files changed, 43 insertions(+), 27 deletions(-) diff --git a/fs/fs_context.c b/fs/fs_context.c index 2834d1afa6e8..de1985eae535 100644 --- a/fs/fs_context.c +++ b/fs/fs_context.c @@ -79,6 +79,35 @@ static int vfs_parse_sb_flag(struct fs_context *fc, const char *key) return -ENOPARAM; } +/** + * vfs_parse_fs_param_source - Handle setting "source" via parameter + * @fc: The filesystem context to modify + * @param: The parameter + * + * This is a simple helper for filesystems to verify that the "source" they + * accept is sane. + * + * Returns 0 on success, -ENOPARAM if this is not "source" parameter, and + * -EINVAL otherwise. In the event of failure, supplementary error information + * is logged. + */ +int vfs_parse_fs_param_source(struct fs_context *fc, struct fs_parameter *param) +{ + if (strcmp(param->key, "source") != 0) + return -ENOPARAM; + + if (param->type != fs_value_is_string) + return invalf(fc, "Non-string source"); + + if (fc->source) + return invalf(fc, "Multiple sources"); + + fc->source = param->string; + param->string = NULL; + return 0; +} +EXPORT_SYMBOL(vfs_parse_fs_param_source); + /** * vfs_parse_fs_param - Add a single parameter to a superblock config * @fc: The filesystem context to modify @@ -122,15 +151,9 @@ int vfs_parse_fs_param(struct fs_context *fc, struct fs_parameter *param) /* If the filesystem doesn't take any arguments, give it the * default handling of source. */ - if (strcmp(param->key, "source") == 0) { - if (param->type != fs_value_is_string) - return invalf(fc, "VFS: Non-string source"); - if (fc->source) - return invalf(fc, "VFS: Multiple sources"); - fc->source = param->string; - param->string = NULL; - return 0; - } + ret = vfs_parse_fs_param_source(fc, param); + if (ret != -ENOPARAM) + return ret; return invalf(fc, "%s: Unknown parameter '%s'", fc->fs_type->name, param->key); @@ -504,16 +527,11 @@ static int legacy_parse_param(struct fs_context *fc, struct fs_parameter *param) struct legacy_fs_context *ctx = fc->fs_private; unsigned int size = ctx->data_size; size_t len = 0; + int ret; - if (strcmp(param->key, "source") == 0) { - if (param->type != fs_value_is_string) - return invalf(fc, "VFS: Legacy: Non-string source"); - if (fc->source) - return invalf(fc, "VFS: Legacy: Multiple sources"); - fc->source = param->string; - param->string = NULL; - return 0; - } + ret = vfs_parse_fs_param_source(fc, param); + if (ret != -ENOPARAM) + return ret; if (ctx->param_type == LEGACY_FS_MONOLITHIC_PARAMS) return invalf(fc, "VFS: Legacy: Can't mix monolithic and individual options"); diff --git a/include/linux/fs_context.h b/include/linux/fs_context.h index 37e1e8f7f08d..e2bc16300c82 100644 --- a/include/linux/fs_context.h +++ b/include/linux/fs_context.h @@ -139,6 +139,8 @@ extern int vfs_parse_fs_string(struct fs_context *fc, const char *key, extern int generic_parse_monolithic(struct fs_context *fc, void *data); extern int vfs_get_tree(struct fs_context *fc); extern void put_fs_context(struct fs_context *fc); +extern int vfs_parse_fs_param_source(struct fs_context *fc, + struct fs_parameter *param); /* * sget() wrappers to be called from the ->get_tree() op. diff --git a/kernel/cgroup/cgroup-v1.c b/kernel/cgroup/cgroup-v1.c index 527917c0b30b..8d6bf56ed77a 100644 --- a/kernel/cgroup/cgroup-v1.c +++ b/kernel/cgroup/cgroup-v1.c @@ -911,15 +911,11 @@ int cgroup1_parse_param(struct fs_context *fc, struct fs_parameter *param) opt = fs_parse(fc, cgroup1_fs_parameters, param, &result); if (opt == -ENOPARAM) { - if (strcmp(param->key, "source") == 0) { - if (param->type != fs_value_is_string) - return invalf(fc, "Non-string source"); - if (fc->source) - return invalf(fc, "Multiple sources not supported"); - fc->source = param->string; - param->string = NULL; - return 0; - } + int ret; + + ret = vfs_parse_fs_param_source(fc, param); + if (ret != -ENOPARAM) + return ret; for_each_subsys(ss, i) { if (strcmp(param->key, ss->legacy_name)) continue;