From patchwork Tue Jul 31 16:10:27 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jann Horn X-Patchwork-Id: 10550981 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 C66B914E0 for ; Tue, 31 Jul 2018 16:11:51 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id B6C192AE8B for ; Tue, 31 Jul 2018 16:11:51 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id AA8712AEA0; Tue, 31 Jul 2018 16:11:51 +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 102F92AE8B for ; Tue, 31 Jul 2018 16:11:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1732364AbeGaRwq (ORCPT ); Tue, 31 Jul 2018 13:52:46 -0400 Received: from mail-qk0-f201.google.com ([209.85.220.201]:48454 "EHLO mail-qk0-f201.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728607AbeGaRwp (ORCPT ); Tue, 31 Jul 2018 13:52:45 -0400 Received: by mail-qk0-f201.google.com with SMTP id 17-v6so14468668qkz.15 for ; Tue, 31 Jul 2018 09:11:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:message-id:mime-version:subject:from:to:cc; bh=j/sV6dXvNZxplbBedVac4Z0WyJu+MmPT6ZQzfhIbe7I=; b=DG0nYZZsyf9vHLscxZ8CBdZ9OKVftHlwgzukZ0rYr51n/QoIu4mT6gbyXkWO/R1Enj 5YRqzkvnNd20crav7vWTq5k3q+qCaiTmrPDf7/7wHP7Ilvegjv72rA1TtV2WjlZXInJv wtZGAPHF/iUkBGYRukZF/pSAmHNFyTvhJvzBiwJdeTp2kBxDxWN1esLsjBGRdl0a2tPS kTqKaPYhjngpyZt9z50x7Bt7HDvaAhZRb12lqzwtvgSO/Fan3WfUFa1OOgm0oZU+A2LD 5uaZYLgoqyrh/1+TPrf0QdHxisVJ8XfTXGL/FOjuhQ9168x+M88TO+23pt/8dxGGZ+PW tWBQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:message-id:mime-version:subject:from:to:cc; bh=j/sV6dXvNZxplbBedVac4Z0WyJu+MmPT6ZQzfhIbe7I=; b=ZhQnKO/y9e/lterwJvQgeeS6JffFijVeMRUSa0Qzrd3wTr0bBA/Yhbmi97MAr3dprx wZEvMbKGTuX0ZGSFlDaH8eloMFqxgnLDcSgmnYcrGXyuUbM9gg0Y4VLgt+xGcp1c351T LB8W4lUqVBbmCVHjWk4a5yMZrP+XkBfa9J0RBMiVhaC5k6a0w/U8kdvntAJjlGJi0R8x zLSw6/zllYpHLZrPWki7UjrNZUynZRsfCFhkEBU9n7WQ4k/Yp+Hak3cAuCtKUi2PNgTd ptxsiDbPIMZbFZOHoc9it3O6K2bMK5L+NFx+lQhCQynfNfqaKEsyQUDTaNVH5VdrGcEO 9Xxg== X-Gm-Message-State: AOUpUlH06vS2GBNVI+7jmi7pDmjDjOPcPdYuRndBJP87ItyaLDK0qQnx SkVtzOSnDyPtJt0+zXOp45RzKmFQIw== X-Google-Smtp-Source: AAOMgpdw7qUYXHKgR/7fM71gh/QN6AiNMjuk/KsCdysA5FSMwAZ8Z5izeOFs2XrBZnJAgrzeIwPc1+SxXA== X-Received: by 2002:a0c:e801:: with SMTP id y1-v6mr12086529qvn.55.1533053503442; Tue, 31 Jul 2018 09:11:43 -0700 (PDT) Date: Tue, 31 Jul 2018 18:10:27 +0200 Message-Id: <20180731161025.189534-1-jannh@google.com> Mime-Version: 1.0 X-Mailer: git-send-email 2.18.0.345.g5c9ce644c3-goog Subject: [PATCH v2] 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, 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 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 --- changed in v2: - move bogus_dirent_name() out of the #ifdef where it doesn't belong (kbuild test robot) @Al: Given what Dave and Pavel said, are you okay with this? arch/alpha/kernel/osf_sys.c | 3 +++ fs/readdir.c | 33 +++++++++++++++++++++++++++++++++ include/linux/fs.h | 3 +++ 3 files changed, 39 insertions(+) diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c index c210a25dd6da..e04e51a2320d 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,8 @@ 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; + if (bogus_dirent_name(&buf->error, name, namlen, __func__)) + return -EUCLEAN; 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 d97f548e6323..fa0ac1e33230 100644 --- a/fs/readdir.c +++ b/fs/readdir.c @@ -64,6 +64,29 @@ 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 low-level IO error had + * happened while reading directory entries. + */ +bool bogus_dirent_name(int *errp, const char *name, int namlen, + const char *caller) +{ + if (namlen == 0) { + pr_err_once("%s: filesystem returned bogus empty name\n", + caller); + *errp = -EUCLEAN; + return true; + } + if (memchr(name, '/', namlen)) { + pr_err_once("%s: filesystem returned bogus name '%*pEhp' (contains slash)\n", + caller, namlen, name); + *errp = -EUCLEAN; + return true; + } + return false; +} + /* * Traditional linux readdir() handling.. * @@ -98,6 +121,8 @@ static int fillonedir(struct dir_context *ctx, const char *name, int namlen, if (buf->result) return -EINVAL; + if (bogus_dirent_name(&buf->result, name, namlen, __func__)) + return -EUCLEAN; d_ino = ino; if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) { buf->result = -EOVERFLOW; @@ -173,6 +198,8 @@ 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)); + if (bogus_dirent_name(&buf->error, name, namlen, __func__)) + return -EUCLEAN; buf->error = -EINVAL; /* only used if we fail.. */ if (reclen > buf->count) return -EINVAL; @@ -259,6 +286,8 @@ 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)); + if (bogus_dirent_name(&buf->error, name, namlen, __func__)) + return -EUCLEAN; buf->error = -EINVAL; /* only used if we fail.. */ if (reclen > buf->count) return -EINVAL; @@ -358,6 +387,8 @@ static int compat_fillonedir(struct dir_context *ctx, const char *name, if (buf->result) return -EINVAL; + if (bogus_dirent_name(&buf->result, name, namlen, __func__)) + return -EUCLEAN; d_ino = ino; if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) { buf->result = -EOVERFLOW; @@ -427,6 +458,8 @@ 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)); + if (bogus_dirent_name(&buf->error, name, namlen, __func__)) + return -EUCLEAN; 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 805bf22898cf..62ad476563f0 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1680,6 +1680,9 @@ struct dir_context { loff_t pos; }; +bool bogus_dirent_name(int *errp, const char *name, int namlen, + const char *caller); + struct block_device_operations; /* These macros are for out of kernel modules to test that