From patchwork Mon Jan 14 18:23:17 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jann Horn X-Patchwork-Id: 10763289 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 620BA91E for ; Mon, 14 Jan 2019 18:23:36 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5250C29AC9 for ; Mon, 14 Jan 2019 18:23:36 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 443F729B0E; Mon, 14 Jan 2019 18:23:36 +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 1E15D29AC9 for ; Mon, 14 Jan 2019 18:23:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726804AbfANSXa (ORCPT ); Mon, 14 Jan 2019 13:23:30 -0500 Received: from mail-qt1-f202.google.com ([209.85.160.202]:47583 "EHLO mail-qt1-f202.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726788AbfANSXa (ORCPT ); Mon, 14 Jan 2019 13:23:30 -0500 Received: by mail-qt1-f202.google.com with SMTP id f2so25473044qtg.14 for ; Mon, 14 Jan 2019 10:23:29 -0800 (PST) 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=SHK+IOeculRAQi6J0T4o1mQ7HU+iz1CfKWD3z3R3LeM=; b=F3U6k3GvdlNm75jnk4Sre+J9c+CObIcbI6OkUqWpUWUpUR6jYIOM+172p6b7lnw/np yrirrVquEVmkFRHdxwWxAFq2eFMNP54lMTWgTb43QLqdbJASK1b6HXNmfuoNfYxIaAgF eFVGv50GW2o42ZFqE4EdF1sKLJJG/S/ji6n3PHSa4ItMtb4mZBihmv92yTc6AwJwRbZ0 9JwYuNB0tDElZgvAWjnIxYX11DtgQk4HwD/+dVvvJba0As8noRYihENfllMFVxZsjvbu bb+BYYlrQwNqSl5/pqziekxGxIAY2YB6kpFILeR2sd29Hvw0xeWSSEKnnPQ0GasSfhgU 03QQ== 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=SHK+IOeculRAQi6J0T4o1mQ7HU+iz1CfKWD3z3R3LeM=; b=lUTORYHqw+wHQkyYj5auxZWuZ1T465hLWNJ0mlChqQhtN2DGPjdIqraRQIQ+mfk5M2 jn+71U4B4b/INhbtBOMDGDyWD5DOoCoV737/cFMKjkzHXTh2CZltm1JEv6hFfnLr0YTX DEsGKUmQZgH4o3/eCO7irhQ7fABxteMEJJSQQOY61Edn8vwVWrXpdF+kVVn2dEMgGDcG 3/Of85wneqAFSUSR6M4r8lcc/taNPFLs+rTAm7KGNDHBBCvGlJttRgt0Nk+3WNi1S3C4 /E3om/ZyO29eGmHVuW/vjrvrCF7lKsuH4iGif2oxy8YnDACvo07kJ4IsmfU7Z7eUIuzg ABJQ== X-Gm-Message-State: AJcUukfBjIxSVSnXGN0dRXjwMaEDbIlqvWu7lxRcUGkE3UyiW/JdkSbk QEX5MvZeB6ohYEldSC8g/mWgdY2QyA== X-Google-Smtp-Source: ALg8bN5rhvK62AjBhArzjuBFaBhYsC2duco/Jv+2/6K36/l0OMymdPazul90Q0PQkldc4GkmX1UJ4t+iEQ== X-Received: by 2002:a0c:a8cd:: with SMTP id h13mr8893841qvc.54.1547490208796; Mon, 14 Jan 2019 10:23:28 -0800 (PST) Date: Mon, 14 Jan 2019 19:23:17 +0100 Message-Id: <20190114182318.110443-1-jannh@google.com> Mime-Version: 1.0 X-Mailer: git-send-email 2.20.1.97.g81188d93c3-goog Subject: [PATCH v3 1/2] 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 I ordered this fix before the refactoring one so that it can easily be backported. --- Bringing that half-year-old patch back to life... changed in v2: - move bogus_dirent_name() out of the #ifdef (kbuild test robot) changed in v3: - change calling convention (Al Viro) - comment fix 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..e60f8e72591b 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 -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 2f6a4534e0df..102b0c86a97f 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 -EUCLEAN; + } + if (memchr(name, '/', namlen)) { + pr_err_once("%s: filesystem returned bogus name '%*pEhp' (contains slash)\n", + __func__, namlen, name); + return -EUCLEAN; + } + 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 -EUCLEAN; 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 -EUCLEAN; 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 -EUCLEAN; 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 -EUCLEAN; 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 -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 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