From patchwork Fri Jan 18 16:14:39 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jann Horn X-Patchwork-Id: 10770833 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 1AE8813B4 for ; Fri, 18 Jan 2019 16:15:12 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 04BE62A039 for ; Fri, 18 Jan 2019 16:15:12 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id EBEF32A144; Fri, 18 Jan 2019 16:15:11 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.5 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, USER_IN_DEF_DKIM_WL autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5B2C129F41 for ; Fri, 18 Jan 2019 16:15:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727488AbfARQPH (ORCPT ); Fri, 18 Jan 2019 11:15:07 -0500 Received: from mail-it1-f201.google.com ([209.85.166.201]:37702 "EHLO mail-it1-f201.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728074AbfARQPG (ORCPT ); Fri, 18 Jan 2019 11:15:06 -0500 Received: by mail-it1-f201.google.com with SMTP id y86so4005728ita.2 for ; Fri, 18 Jan 2019 08:15:05 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=5LVynhbNulaKwqSUfCuVd5OKAuOmA6joY7YHWKe61BE=; b=pK8ELFG73xxugBocSGDSUnJNvyIwt/ohjdBBzz0Lfb6JLLaL8FHzw/nzHvQXQp96m/ lBw4vsgTGxcinCLs7piaU+dxMdyYvqsJU78prIqb11Oi3n22vwAiANkZQHoZD5iFirxn o9dEh8zlzHsTQ8HkJuvtU8RcM9/vSPaGo+YaDLVHZ07JnT1IKBA7QioWqxGWI1TfprLs aOHwOOZV3JE6DCJghvuybtywai8beQYUqR86HH2xoEnPK31pKk4uY8a7onZs5XLHIAU8 W/LYw/QCyafekdnJ8Lq/cPg0PyMUrDJjHVr/apGUdtV939ItwCapB1N0RKTbw79hrx08 Zy6A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=5LVynhbNulaKwqSUfCuVd5OKAuOmA6joY7YHWKe61BE=; b=g4KJ4dFZiWHsxJFY3NNEI39QiHHBavrihBF5u5uqkGur8mPO4UmGW93gJ8uerwlP6c fMAF8/22mZ7oAt6q8cykC89oyPGiyywW8FZoqxDg8gD6X7YDut8Mt5LHl7XcrxKaw2Tb 51vFmt3CGceHqX06NgegHlxiQX/KxTU5rSFqSNl2phN93EqjorPII2vVFQ+4WeM71w2F kNntodiFce6+XJvBmep10QlRy75Aj8OwRXGM7G+sH2N4IXs98L3MQ+ulYEw9o1BCS/kx DhemNh2zlp4y5Ce26/QRThxim3460jfko3H/9L9yf+snQLUe3yMaIe5GcPX2Jm4HKhLR OLew== X-Gm-Message-State: AJcUukdS/FyiTou1Uf5f22/TQXm2iY0zlNWhMi8WNaN8jJyYKPtTHSnm +70oW16IuPkZy1AoYU999G9De0c7Nw== X-Google-Smtp-Source: ALg8bN6Q1AsznvuU/aVkHTxiuYPcSwkewBljenfXIaGdUzU3IdByf4DK+AhQ0a2HYCjsb/Zk9KuyMFgz6g== X-Received: by 2002:a24:5f49:: with SMTP id r70mr648398itb.22.1547828105167; Fri, 18 Jan 2019 08:15:05 -0800 (PST) Date: Fri, 18 Jan 2019 17:14:39 +0100 In-Reply-To: <20190118161440.220134-1-jannh@google.com> Message-Id: <20190118161440.220134-2-jannh@google.com> Mime-Version: 1.0 References: <20190118161440.220134-1-jannh@google.com> X-Mailer: git-send-email 2.20.1.321.g9e740568ce-goog Subject: [PATCH v4 2/3] fs: don't let getdents return bogus names From: Jann Horn To: Richard Henderson , Ivan Kokshaysky , Matt Turner , Alexander Viro , linux-fsdevel@vger.kernel.org, Arnd Bergmann , jannh@google.com Cc: "Eric W. Biederman" , "Theodore Ts'o" , Andreas Dilger , linux-alpha@vger.kernel.org, linux-kernel@vger.kernel.org, Dave Chinner , Pavel Machek , linux-arch@vger.kernel.org, linux-api@vger.kernel.org Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP When you e.g. run `find` on a directory for which getdents returns "filenames" that contain slashes, `find` passes those "filenames" back to the kernel, which then interprets them as paths. That could conceivably cause userspace to do something bad when accessing something like an untrusted USB stick, but I'm not aware of any specific example. Instead of returning bogus filenames to userspace, return -EUCLEAN. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Jann Horn --- I ordered this fix before the refactoring one so that it can easily be backported. changed in v2: - move bogus_dirent_name() out of the #ifdef (kbuild test robot) changed in v3: - change calling convention (Al Viro) - comment fix changed in v4: - use EFSCORRUPTED instead of EUCLEAN (Dave Chinner) arch/alpha/kernel/osf_sys.c | 4 ++++ fs/readdir.c | 35 +++++++++++++++++++++++++++++++++++ include/linux/fs.h | 2 ++ 3 files changed, 41 insertions(+) diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c index 792586038808..db1c2144d477 100644 --- a/arch/alpha/kernel/osf_sys.c +++ b/arch/alpha/kernel/osf_sys.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -117,6 +118,9 @@ osf_filldir(struct dir_context *ctx, const char *name, int namlen, unsigned int reclen = ALIGN(NAME_OFFSET + namlen + 1, sizeof(u32)); unsigned int d_ino; + buf->error = check_dirent_name(name, namlen); + if (unlikely(buf->error)) + return -EFSCORRUPTED; buf->error = -EINVAL; /* only used if we fail */ if (reclen > buf->count) return -EINVAL; diff --git a/fs/readdir.c b/fs/readdir.c index 2f6a4534e0df..58088510bb9c 100644 --- a/fs/readdir.c +++ b/fs/readdir.c @@ -64,6 +64,26 @@ int iterate_dir(struct file *file, struct dir_context *ctx) } EXPORT_SYMBOL(iterate_dir); +/* + * Most filesystems don't filter out bogus directory entry names, and userspace + * can get very confused by such names. Behave as if a filesystem error had + * happened while reading directory entries. + */ +int check_dirent_name(const char *name, int namlen) +{ + if (namlen == 0) { + pr_err_once("%s: filesystem returned bogus empty name\n", + __func__); + return -EFSCORRUPTED; + } + if (memchr(name, '/', namlen)) { + pr_err_once("%s: filesystem returned bogus name '%*pEhp' (contains slash)\n", + __func__, namlen, name); + return -EFSCORRUPTED; + } + return 0; +} + /* * Traditional linux readdir() handling.. * @@ -98,6 +118,9 @@ static int fillonedir(struct dir_context *ctx, const char *name, int namlen, if (buf->result) return -EINVAL; + buf->result = check_dirent_name(name, namlen); + if (unlikely(buf->result)) + return -EFSCORRUPTED; d_ino = ino; if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) { buf->result = -EOVERFLOW; @@ -173,6 +196,9 @@ static int filldir(struct dir_context *ctx, const char *name, int namlen, int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2, sizeof(long)); + buf->error = check_dirent_name(name, namlen); + if (unlikely(buf->error)) + return -EFSCORRUPTED; buf->error = -EINVAL; /* only used if we fail.. */ if (reclen > buf->count) return -EINVAL; @@ -259,6 +285,9 @@ static int filldir64(struct dir_context *ctx, const char *name, int namlen, int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1, sizeof(u64)); + buf->error = check_dirent_name(name, namlen); + if (unlikely(buf->error)) + return -EFSCORRUPTED; buf->error = -EINVAL; /* only used if we fail.. */ if (reclen > buf->count) return -EINVAL; @@ -358,6 +387,9 @@ static int compat_fillonedir(struct dir_context *ctx, const char *name, if (buf->result) return -EINVAL; + buf->result = check_dirent_name(name, namlen); + if (unlikely(buf->result)) + return -EFSCORRUPTED; d_ino = ino; if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) { buf->result = -EOVERFLOW; @@ -427,6 +459,9 @@ static int compat_filldir(struct dir_context *ctx, const char *name, int namlen, int reclen = ALIGN(offsetof(struct compat_linux_dirent, d_name) + namlen + 2, sizeof(compat_long_t)); + buf->error = check_dirent_name(name, namlen); + if (unlikely(buf->error)) + return -EFSCORRUPTED; buf->error = -EINVAL; /* only used if we fail.. */ if (reclen > buf->count) return -EINVAL; diff --git a/include/linux/fs.h b/include/linux/fs.h index 811c77743dad..e14329741e3a 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1730,6 +1730,8 @@ struct dir_context { loff_t pos; }; +int check_dirent_name(const char *name, int namlen); + struct block_device_operations; /* These macros are for out of kernel modules to test that