From patchwork Mon Nov 27 23:40:56 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tobin Harding X-Patchwork-Id: 10078379 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id A7CFE6028E for ; Mon, 27 Nov 2017 23:41:52 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9997828F8B for ; Mon, 27 Nov 2017 23:41:52 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 8881D2908E; Mon, 27 Nov 2017 23:41:52 +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=-4.1 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_MED,T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from mother.openwall.net (mother.openwall.net [195.42.179.200]) by mail.wl.linuxfoundation.org (Postfix) with SMTP id 4EF8E28F8B for ; Mon, 27 Nov 2017 23:41:51 +0000 (UTC) Received: (qmail 7232 invoked by uid 550); 27 Nov 2017 23:41:38 -0000 Mailing-List: contact kernel-hardening-help@lists.openwall.com; run by ezmlm Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-ID: Delivered-To: mailing list kernel-hardening@lists.openwall.com Received: (qmail 6071 invoked from network); 27 Nov 2017 23:41:37 -0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=tobin.cc; h=cc :date:from:in-reply-to:message-id:references:subject:to :x-me-sender:x-me-sender:x-sasl-enc; s=fm1; bh=fKZuZUA3SjXJ/XMMX Zm21aA4A65x6vmHe6ImQWg4kdI=; b=DqIwzWZk/wZ7QXPaSxXwfGxQ+C6KEpP84 G1nVi7aanoN5URHQoLVU1oKzA/ENwraFClix1A8Q1n4DJYzaac0o5Z8n42tD8f9Y 1LRl/R/0p/70q3SFHD56s6KGvaZwQICTvfqt/eCRe1bJVjD7nvkD4mpNioD/E1nK k2ljWa4NBK3fQSsXUKhKb5/KwwH6OE+/2wZnQoxn2rQGCGciEZjuPzKc6CSoPoUh AgTrtAU3BMmnCGZQ71jZFpOa1znUCXF9Je2M3gFmwnCTGpn3kpwuXPyLvtNjI0XS p9FP37dpC105YM3JIR2l+Bi4mOrPGD/bHDvX7zxHsOHHNU4r6e8Yw== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:date:from:in-reply-to:message-id :references:subject:to:x-me-sender:x-me-sender:x-sasl-enc; s= fm1; bh=fKZuZUA3SjXJ/XMMXZm21aA4A65x6vmHe6ImQWg4kdI=; b=Cp4YqztR OZ1UWhnEjc8AdR0/S5RLXYDKQaxP8txGjFRDrUMsb3euDcMgmxcq8p/eWWyrgpiz nx4pDLbfwDW5lPFmD5bSwwYP5wxyIz8YEy+Y+IY4Yqd2KZbSem0uktWWay63VlPH W5h3Z9/xnW3TFR9O3VwArCmuKR6accpgrJX4Jl+0vdWdsDsMYhxodmGLo55cFH6X IHTMXXPmvA4/QlN7Uv1SEbY3t1sSPo98VQFoivqyTe67j++TAqjt4DzaAXrOzTuH NRW2oqLzLQ1xwftM8d+PGqsTLzV6bWM4XW6CXFmayZ5Wu3VuuATTQsIL22JDoOaV Yyui2RRbXKIVNQ== X-ME-Sender: From: "Tobin C. Harding" To: Linus Torvalds Cc: "Tobin C. Harding" , "Jason A. Donenfeld" , Theodore Ts'o , Kees Cook , Paolo Bonzini , Tycho Andersen , "Roberts, William C" , Tejun Heo , Jordan Glover , Greg KH , Petr Mladek , Joe Perches , Ian Campbell , Sergey Senozhatsky , Catalin Marinas , Will Deacon , Steven Rostedt , Chris Fries , Dave Weinstein , Daniel Micay , Djalal Harouni , =?UTF-8?q?Radim=20Kr=C4=8Dm=C3=A1=C5=99?= , linux-kernel@vger.kernel.org, kvm@vger.kernel.org, kernel-hardening@lists.openwall.com Date: Tue, 28 Nov 2017 10:40:56 +1100 Message-Id: <1511826058-2563-4-git-send-email-me@tobin.cc> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1511826058-2563-1-git-send-email-me@tobin.cc> References: <1511826058-2563-1-git-send-email-me@tobin.cc> Subject: [kernel-hardening] [PATCH 3/5] vsprintf: add specifier %px, unique identifier X-Virus-Scanned: ClamAV using ClamSMTP The typical in tree method for obtaining a unique identifier when printing kernel objects (structs) is to use the address of the struct i.e the pointer. This potentially leaks sensitive information to user space because it reveals information about the kernel layout in memory. We can get a unique identifier based on an address by hashing the address first before printing. Add printk specifier %px which hashes the address before printing. Signed-off-by: Tobin C. Harding --- lib/test_printf.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++ lib/vsprintf.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++ scripts/checkpatch.pl | 2 +- 3 files changed, 146 insertions(+), 1 deletion(-) diff --git a/lib/test_printf.c b/lib/test_printf.c index 563f10e6876a..8da57205f1ea 100644 --- a/lib/test_printf.c +++ b/lib/test_printf.c @@ -462,10 +462,84 @@ flags(void) kfree(cmp_buffer); } +#define HASHED_BUF_SIZE 64 /* leave some space so we don't oops */ + +#if BITS_PER_LONG == 64 + +#define PTR ((void *)0xffff0123456789ab) +#define PTR_STR "ffff0123456789ab" +#define ZEROS "00000000" /* hex 32 zero bits */ + +static int __init +hashed_format(void) +{ + char buf[HASHED_BUF_SIZE]; + int nchars; + + nchars = snprintf(buf, HASHED_BUF_SIZE, "%px", PTR); + + if (nchars != PTR_WIDTH || strncmp(buf, ZEROS, strlen(ZEROS)) != 0) + return -1; + + return 0; +} + +#else + +#define PTR ((void *)0x456789ab) +#define PTR_STR "456789ab" + +static int __init +hashed_format(void) +{ + /* Format is implicitly tested for 32 bit machines by hashed_hash() */ + return 0; +} + +#endif /* BITS_PER_LONG == 64 */ + +static int __init +hashed_hash(void) +{ + char buf[HASHED_BUF_SIZE]; + int nchars; + + nchars = snprintf(buf, HASHED_BUF_SIZE, "%px", PTR); + + if (nchars != PTR_WIDTH || strncmp(buf, PTR_STR, PTR_WIDTH) == 0) + return -1; + + return 0; +} + +/* + * We can't use test() to test %px because we don't know what output to expect + * after an address is hashed. + */ +static void __init +hashed(void) +{ + int err; + + err = hashed_hash(); + if (err) { + pr_warn("specifier px does not appear to be hashed\n"); + failed_tests++; + return; + } + + err = hashed_format(); + if (err) { + pr_warn("hashed output from px has unexpected format\n"); + failed_tests++; + } +} + static void __init test_pointer(void) { plain(); + hashed(); symbol_ptr(); kernel_ptr(); struct_resource(); diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 360731f81dd3..3b0de95c3498 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -33,6 +33,8 @@ #include #include #include +#include +#include #ifdef CONFIG_BLOCK #include #endif @@ -1644,6 +1646,73 @@ char *device_node_string(char *buf, char *end, struct device_node *dn, return widen_string(buf, buf - buf_start, end, spec); } +static bool have_filled_random_ptr_key __read_mostly; +static siphash_key_t ptr_key __read_mostly; + +static void fill_random_ptr_key(struct random_ready_callback *unused) +{ + get_random_bytes(&ptr_key, sizeof(ptr_key)); + /* + * have_filled_random_ptr_key==true is dependent on get_random_bytes(). + * ptr_to_id() needs to see have_filled_random_ptr_key==true + * after get_random_bytes() returns. + */ + smp_mb(); + WRITE_ONCE(have_filled_random_ptr_key, true); +} + +static struct random_ready_callback random_ready = { + .func = fill_random_ptr_key +}; + +static int __init initialize_ptr_random(void) +{ + int ret = add_random_ready_callback(&random_ready); + + if (!ret) { + return 0; + } else if (ret == -EALREADY) { + fill_random_ptr_key(&random_ready); + return 0; + } + + return ret; +} +early_initcall(initialize_ptr_random); + +/* Maps a pointer to a 32 bit unique identifier. */ +static char *ptr_to_id(char *buf, char *end, void *ptr, struct printf_spec spec) +{ + unsigned long hashval; + const int default_width = 2 * sizeof(ptr); + + if (unlikely(!have_filled_random_ptr_key)) { + spec.field_width = default_width; + /* string length must be less than default_width */ + return string(buf, end, "(ptrval)", spec); + } + +#ifdef CONFIG_64BIT + hashval = (unsigned long)siphash_1u64((u64)ptr, &ptr_key); + /* + * Mask off the first 32 bits, this makes explicit that we have + * modified the address (and 32 bits is plenty for a unique ID). + */ + hashval = hashval & 0xffffffff; +#else + hashval = (unsigned long)siphash_1u32((u32)ptr, &ptr_key); +#endif + + spec.flags |= SMALL; + if (spec.field_width == -1) { + spec.field_width = default_width; + spec.flags |= ZEROPAD; + } + spec.base = 16; + + return number(buf, end, hashval, spec); +} + /* * Show a '%p' thing. A kernel extension is that the '%p' is followed * by an extra set of alphanumeric characters that are extended format @@ -1868,6 +1937,8 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr, case 'F': return device_node_string(buf, end, ptr, spec, fmt + 1); } + case 'x': + return ptr_to_id(buf, end, ptr, spec); } spec.flags |= SMALL; if (spec.field_width == -1) { diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 95cda3ecc66b..040aa79e1d9d 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -5753,7 +5753,7 @@ sub process { for (my $count = $linenr; $count <= $lc; $count++) { my $fmt = get_quoted_string($lines[$count - 1], raw_line($count, 0)); $fmt =~ s/%%//g; - if ($fmt =~ /(\%[\*\d\.]*p(?![\WFfSsBKRraEhMmIiUDdgVCbGNO]).)/) { + if ($fmt =~ /(\%[\*\d\.]*p(?![\WFfSsBKRraEhMmIiUDdgVCbGNOx]).)/) { $bad_extension = $1; last; }