From patchwork Sun Oct 1 00:06:45 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tobin Harding X-Patchwork-Id: 9979601 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 746F260327 for ; Sun, 1 Oct 2017 00:09:17 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 6738527E63 for ; Sun, 1 Oct 2017 00:09:17 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 5BB3E27FA5; Sun, 1 Oct 2017 00:09:17 +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.3 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, RCVD_IN_DNSWL_MED 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 D18B527E63 for ; Sun, 1 Oct 2017 00:09:15 +0000 (UTC) Received: (qmail 3702 invoked by uid 550); 1 Oct 2017 00:09:08 -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 Delivered-To: moderator for kernel-hardening@lists.openwall.com Received: (qmail 32505 invoked from network); 1 Oct 2017 00:07:23 -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:x-sasl-enc; s=fm1; bh=Pw8QvS H2sXCIA86Uh3+GqtMWROLKSqEpXmkrUsOTfwk=; b=B7m9cv1Ab7CcGCE5mSAxB+ 0IN3xTu8InnkDZbFcDzQVarh9xl6BmPh07J3FLgPYudZA/CZ+kkXSGrzDXP+YzaI yEQih2ozbozagzYH41Bms1vHCPoADAp8OQDydQHKKRzRh5Jn6JiXQkLq6DEOrEjd CWMzGDlQdQKjmKDU8xWGez7ljjVLxXe83Ak3jjJi8eIPMTGHutPN4jpcaFfNXMXc EmPO3XdUndSqjoiuuvPJoZOH9SS2AvgkPjIR25dwzM488KsiBpF57Vk4BfYB0WPW rUnYEif2S0dn8uOu8tzFEbq7umpJpckkOGRrWPOAkT3tOUARUX4uFJty8EhiuttA == 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 :x-sasl-enc; s=fm1; bh=Pw8QvSH2sXCIA86Uh3+GqtMWROLKSqEpXmkrUsOTf wk=; b=Ts9P6YfuOL1ZBEzSV87khCg4lRFDEEShsJXbMzMppOpg3FCQ6dmwuDSvH IRq8kJbmvQbAvmzWxPnkQYhVGPCevFxN3J+2IcXgwKMyWA4KKIw/SUaC5Njd8due ODDvFGG8oFQjVVZ/2DaeQ+1CD1l92ADlGNQNqy5Ds0z0jgqD/M03LqiKqTCC6G+q 9W3EV21Xs6vxURSrL5eV2wmFVkcCl1UKxNRMAMbhpPyQSX9HoQf7tdeLnTHy9sOz xNzGM0aSZiAD3GpTA3X8JnCKtRb9p+NCMyOI9RYZymP2wHpsK+CQFCwpK7DCjVxk 4M8NvCiMkj6g+bB7pvdRpyC7Z/sSw== X-ME-Sender: X-Sasl-enc: 4JnARAaugZfmrs2fOJGCYtcp4gNHCsmopOWD4keIShph 1506816431 From: "Tobin C. Harding" To: Greg KH , Petr Mladek , Joe Perches , Ian Campbell , Sergey Senozhatsky Cc: "Tobin C. Harding" , kernel-hardening@lists.openwall.com, linux-kernel@vger.kernel.org, Catalin Marinas , Will Deacon , Steven Rostedt , William Roberts , Chris Fries , Dave Weinstein Date: Sun, 1 Oct 2017 11:06:45 +1100 Message-Id: <1506816410-10230-2-git-send-email-me@tobin.cc> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1506816410-10230-1-git-send-email-me@tobin.cc> References: <1506816410-10230-1-git-send-email-me@tobin.cc> Subject: [kernel-hardening] [RFC V2 1/6] lib: vsprintf: additional kernel pointer filtering options X-Virus-Scanned: ClamAV using ClamSMTP Add the kptr_restrict setting of 3 which results in both %p and %pK values being replaced by zeros. Add an additional %pP value inspired by the Grsecurity option which explicitly whitelists pointers for output. Amend scripts/checkpatch.pl to handle %pP. This patch is based on work by William Roberts Signed-off-by: Tobin C. Harding Signed-off-by: Greg Kroah-Hartman --- Documentation/printk-formats.txt | 8 +++++ Documentation/sysctl/kernel.txt | 4 +++ kernel/sysctl.c | 3 +- lib/vsprintf.c | 78 ++++++++++++++++++++++++++-------------- scripts/checkpatch.pl | 2 +- 5 files changed, 66 insertions(+), 29 deletions(-) diff --git a/Documentation/printk-formats.txt b/Documentation/printk-formats.txt index 361789d..16c9abc 100644 --- a/Documentation/printk-formats.txt +++ b/Documentation/printk-formats.txt @@ -97,6 +97,14 @@ For printing kernel pointers which should be hidden from unprivileged users. The behaviour of ``%pK`` depends on the ``kptr_restrict sysctl`` - see Documentation/sysctl/kernel.txt for more details. +:: + + %pP 0x01234567 or 0x0123456789abcdef + +For printing kernel pointers which should always be shown, even to +unprivileged users. + + Struct Resources ================ diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt index 694968c..7ee183af 100644 --- a/Documentation/sysctl/kernel.txt +++ b/Documentation/sysctl/kernel.txt @@ -394,6 +394,10 @@ values to unprivileged users is a concern. When kptr_restrict is set to (2), kernel pointers printed using %pK will be replaced with 0's regardless of privileges. +When kptr_restrict is set to (3), kernel pointers printed using +%p and %pK will be replaced with 0's regardless of privileges, +however kernel pointers printed using %pP will continue to be printed. + ============================================================== l2cr: (PPC only) diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 6648fbb..37ba637 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -129,6 +129,7 @@ static unsigned long one_ul = 1; static int one_hundred = 100; static int one_thousand = 1000; #ifdef CONFIG_PRINTK +static int three = 3; static int ten_thousand = 10000; #endif #ifdef CONFIG_PERF_EVENTS @@ -851,7 +852,7 @@ static struct ctl_table kern_table[] = { .mode = 0644, .proc_handler = proc_dointvec_minmax_sysadmin, .extra1 = &zero, - .extra2 = &two, + .extra2 = &three, }, #endif { diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 86c3385..e6eace0 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -396,6 +396,16 @@ struct printf_spec { #define FIELD_WIDTH_MAX ((1 << 23) - 1) #define PRECISION_MAX ((1 << 15) - 1) +int kptr_restrict __read_mostly; + +/* + * return non-zero if we should cleanse pointers for %p and %pK specifiers. + */ +static inline int kptr_restrict_cleanse_kernel_pointers(void) +{ + return kptr_restrict >= 3; +} + static noinline_for_stack char *number(char *buf, char *end, unsigned long long num, struct printf_spec spec) @@ -1591,8 +1601,6 @@ char *device_node_string(char *buf, char *end, struct device_node *dn, return widen_string(buf, buf - buf_start, end, spec); } -int kptr_restrict __read_mostly; - /* * Show a '%p' thing. A kernel extension is that the '%p' is followed * by an extra set of alphanumeric characters that are extended format @@ -1664,6 +1672,7 @@ int kptr_restrict __read_mostly; * Do not use this feature without some mechanism to verify the * correctness of the format string and va_list arguments. * - 'K' For a kernel pointer that should be hidden from unprivileged users + * - 'P' For a kernel pointer that should be shown to all users * - 'NF' For a netdev_features_t * - 'h[CDN]' For a variable-length buffer, it prints it as a hex string with * a certain separator (' ' by default): @@ -1703,6 +1712,8 @@ int kptr_restrict __read_mostly; * Note: The difference between 'S' and 'F' is that on ia64 and ppc64 * function pointers are really function descriptors, which contain a * pointer to the real address. + * + * Note: That for kptr_restrict set to 3, %p and %pK have the same meaning. */ static noinline_for_stack char *pointer(const char *fmt, char *buf, char *end, void *ptr, @@ -1710,7 +1721,7 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr, { const int default_width = 2 * sizeof(void *); - if (!ptr && *fmt != 'K') { + if (!ptr && *fmt != 'K' && !kptr_restrict_cleanse_kernel_pointers()) { /* * Print (null) with the same width as a pointer so it makes * tabular output look nice. @@ -1791,6 +1802,31 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr, va_end(va); return buf; } + case 'N': + return netdev_bits(buf, end, ptr, fmt); + case 'a': + return address_val(buf, end, ptr, fmt); + case 'd': + return dentry_name(buf, end, ptr, spec, fmt); + case 'C': + return clock(buf, end, ptr, spec, fmt); + case 'D': + return dentry_name(buf, end, + ((const struct file *)ptr)->f_path.dentry, + spec, fmt); +#ifdef CONFIG_BLOCK + case 'g': + return bdev_name(buf, end, ptr, spec, fmt); +#endif + + case 'G': + return flags_string(buf, end, ptr, fmt); + case 'P': + /* + * An explicitly whitelisted kernel pointer should never be + * cleansed + */ + break; case 'K': switch (kptr_restrict) { case 0: @@ -1825,39 +1861,27 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr, ptr = NULL; break; } - case 2: + case 2: /* cleanse %pK for kptr_restrict >= 2 */ default: - /* Always print 0's for %pK */ ptr = NULL; break; } - break; - - case 'N': - return netdev_bits(buf, end, ptr, fmt); - case 'a': - return address_val(buf, end, ptr, fmt); - case 'd': - return dentry_name(buf, end, ptr, spec, fmt); - case 'C': - return clock(buf, end, ptr, spec, fmt); - case 'D': - return dentry_name(buf, end, - ((const struct file *)ptr)->f_path.dentry, - spec, fmt); -#ifdef CONFIG_BLOCK - case 'g': - return bdev_name(buf, end, ptr, spec, fmt); -#endif - - case 'G': - return flags_string(buf, end, ptr, fmt); + break; /* case 'K' */ case 'O': switch (fmt[1]) { case 'F': return device_node_string(buf, end, ptr, spec, fmt + 1); } + /* fall through */ + default: + /* + * Plain pointers (%p) are always cleansed in higher restriction + * levels. + */ + if (kptr_restrict >= 3) + ptr = NULL; } + spec.flags |= SMALL; if (spec.field_width == -1) { spec.field_width = default_width; @@ -1865,7 +1889,7 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr, } spec.base = 16; - return number(buf, end, (unsigned long) ptr, spec); + return number(buf, end, (unsigned long long) ptr, spec); } /* diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index dd2c262..eabc56c 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -5762,7 +5762,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(?![\WFfSsBKRraEhMmIiUDdgVCbGNOP]).)/) { $bad_extension = $1; last; }