From patchwork Thu Dec 20 19:59:30 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tycho Andersen X-Patchwork-Id: 10739525 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 0F1B617E1 for ; Thu, 20 Dec 2018 19:59:47 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id F2EF028BA7 for ; Thu, 20 Dec 2018 19:59:46 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id E73C028BB8; Thu, 20 Dec 2018 19:59:46 +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=-7.9 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI 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 8FD7828BA7 for ; Thu, 20 Dec 2018 19:59:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731489AbeLTT7q (ORCPT ); Thu, 20 Dec 2018 14:59:46 -0500 Received: from mail-io1-f68.google.com ([209.85.166.68]:45298 "EHLO mail-io1-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725372AbeLTT7q (ORCPT ); Thu, 20 Dec 2018 14:59:46 -0500 Received: by mail-io1-f68.google.com with SMTP id p7so1696420iog.12 for ; Thu, 20 Dec 2018 11:59:45 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=tycho-ws.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=0EimkVSLHfSwmfcQEy4BKoOrgVdq12HdkoBv4xVscs0=; b=ZXsCv/D1he3mbj80n9D5c5n4Mfz28OCAPGwlTNp/szwd9o50nZ1/Wn27Ebp9i+dYku uFDy3kVGm/c9PBXi5UOSdyDtpfYP0ixSYSMsjF5Bs8HrcbNvdpFzOewAcOcY16mP7BAV 57Qd6h0rt45GaJ4srfIWeJDxa+M8atQjqThpNeH/iikUg0C2DCMxlgYreb/rqw9sApOC wM1hwYTTfuX48pCpDyIHosgEUTqcIRCsncUu9mbtpiL0f1cJTUbdAa/aMpeUk+f6ldrG NkU28tBTkOAqCpEq2tpSAwsOMuHCA7A+CnsUIr1RZw/P0aC9HOQITu/kjUML8CXLe7Fk X7Nw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=0EimkVSLHfSwmfcQEy4BKoOrgVdq12HdkoBv4xVscs0=; b=bkDjyWw8q586fVoC7O0gySiEBHzKz8ZzmeLnImSjkWrGrUNlOP2XzOXkMszjqVM85Z oYENEpHRHbzwXHiDCshvZhtK92E82qOhPbWjjAJnJGh5/E0ZURF4NqESdhsbvAlZncwD xT7nbbRRjV8sckFnmWXVmOrlKdU5hAKa5LwNWvigFebBWAG19jHvvh+zvEb+mPd9OyNw 4uBlHxsylxwqZElRHqVGSyJ2R++CIChbhL2Vlz1KA2S878XKDomDRTcTTu/7OwEilJRm rPtAfNcBx9AZTGtBDl/RjOEoe4TNfF1+klU1u9Q+tDMwjGfwCqvVdhCJz/CleF8iqYkC GVYw== X-Gm-Message-State: AA+aEWbXIxn++HQLIlpN4pMKLlE6udBGoOgHltDf9060f64KahI0h087 esKCRH2BWBY85a7+hRsPM1FcIN9xBxpPbA== X-Google-Smtp-Source: AFSGD/UjzBl3qVFnPZzo+hXGgtxG3w1oJ+Wq0+EFN7n7Gv0fdGAYIulSkCB+AG2CaOcNIwyKrBKAbg== X-Received: by 2002:a5d:8747:: with SMTP id k7mr20489826iol.279.1545335984354; Thu, 20 Dec 2018 11:59:44 -0800 (PST) Received: from cisco.lan (71-218-133-134.hlrn.qwest.net. [71.218.133.134]) by smtp.gmail.com with ESMTPSA id b140sm5068429itc.4.2018.12.20.11.59.43 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 20 Dec 2018 11:59:43 -0800 (PST) From: Tycho Andersen To: linux-sparse@vger.kernel.org, kernel-hardening@lists.openwall.com Cc: Tycho Andersen Subject: [RFC v1 3/4] add a check for copy_to_user() address spaces Date: Thu, 20 Dec 2018 12:59:30 -0700 Message-Id: <20181220195931.20331-4-tycho@tycho.ws> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20181220195931.20331-1-tycho@tycho.ws> References: <20181220195931.20331-1-tycho@tycho.ws> MIME-Version: 1.0 Sender: linux-sparse-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sparse@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Leaking kernel pointers to userspace is a bad idea, so let's try to do some analysis for it. The basic idea is that every pointer copied to userspace "should be" (but isn't necessarily) annotated with __user, and if it is not, then it's a potential infoleak. The motivation for this is stuff like [1], which is exactly a case of this. Based on a subsequent manual analysis of the uapi headers, I found [2]. Unfortunately in both of these cases, there is void * (for compat) trickery that masks them from actually turning up in this case. But hey, I wrote it and tried it out, so perhaps the code is useful for someone :) [1]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/drivers/block/floppy.c?id=65eea8edc315589d6c993cf12dbb5d0e9ef1fe4e [2]: https://lkml.org/lkml/2018/11/3/142 Signed-off-by: Tycho Andersen --- sparse.c | 93 ++++++++++++++++++++++++++++++++++++++- validation/copy_to_user.c | 31 +++++++++++++ 2 files changed, 123 insertions(+), 1 deletion(-) diff --git a/sparse.c b/sparse.c index 217a5bf..8bcbe16 100644 --- a/sparse.c +++ b/sparse.c @@ -171,13 +171,104 @@ static void check_memset(struct position pos, struct expression_list *args) check_byte_count(pos, size); } +static struct symbol *resolve_arg_type(struct position pos, struct expression *arg) +{ + struct expression *uncast; + + uncast = arg; + switch (arg->type) { + case EXPR_CAST: + case EXPR_FORCE_CAST: + case EXPR_IMPLIED_CAST: + /* + * Undo any casting done by sparse to the function's + * argument type. + */ + uncast = arg->cast_expression; + break; + case EXPR_SYMBOL: + break; + case EXPR_PREOP: + /* + * handle derefs; these are really just the type of the + * resulting expression. + */ + break; + case EXPR_BINOP: + /* TODO: resolve this pointer math if possible? */ + return NULL; + default: + warning(pos, "huh? arg not a cast or symbol? %d", arg->type); + return NULL; + } + + return uncast->ctype->ctype.base_type; +} + +static void check_ptr_in_other_as(struct position pos, struct symbol *sym, int this_as) +{ + struct ident *ident = sym->ident; + + if (sym->type == SYM_NODE) + sym = sym->ctype.base_type; + + switch (sym->type) { + case SYM_ARRAY: + case SYM_PTR: { + if (sym->ctype.as != this_as) + warning(pos, "member %s is a kernel pointer copied to userspace", show_ident(ident)); + check_ptr_in_other_as(pos, sym->ctype.base_type, this_as); + break; + } + case SYM_STRUCT: + case SYM_UNION: { + struct symbol *member; + + FOR_EACH_PTR(sym->symbol_list, member) { + check_ptr_in_other_as(pos, member, this_as); + } END_FOR_EACH_PTR(member); + break; + } + default: + /* + * scalar types are ok + * TODO: what about SYM_LABEL/PREPROCESSOR? + */ + break; + } +} + +static void check_no_kernel_pointers(struct position pos, struct expression_list *args) +{ + struct expression *src = ptr_list_nth_entry((struct ptr_list *)args, 1); + struct symbol *base = NULL; + + if (!Waddress_space) + return; + + /* get the type of src */ + base = resolve_arg_type(pos, src); + + /* + * And deref it to *src; src will *always* be a kernel pointer, and + * we're really after members of structures here, not the pointers + * themselves. So we do this deref at the top level. + */ + base = base->ctype.base_type; + + check_ptr_in_other_as(pos, base, 1); } #define check_memcpy check_memset -#define check_ctu check_memset #define check_cfu check_memset +void check_ctu(struct position pos, struct expression_list *args) +{ + check_memset(pos, args); + check_no_kernel_pointers(pos, args); +} + struct checkfn { struct ident *id; void (*check)(struct position pos, struct expression_list *args); diff --git a/validation/copy_to_user.c b/validation/copy_to_user.c new file mode 100644 index 0000000..d9cded6 --- /dev/null +++ b/validation/copy_to_user.c @@ -0,0 +1,31 @@ +#define __user __attribute__((address_space(1))) + +struct bar { + char *bar_kptr; +}; + +struct foo { + char *foo_kptr; + char __user *uptr; + struct bar bar; +}; + +static void copy_to_user(void __user *to, const void *from, unsigned long n) +{ +} + +static void bar(void) +{ + struct foo f; + void __user *p = (void __user *)0; + + copy_to_user(p, &f, sizeof(f)); +} +/* + * check-name: copy_to_user arguments + * + * check-error-start +copy_to_user.c:22:9: warning: member foo_kptr is a kernel pointer copied to userspace +copy_to_user.c:22:9: warning: member bar_kptr is a kernel pointer copied to userspace + * check-error-end + */