From patchwork Tue Oct 25 15:59:49 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andy Whitcroft X-Patchwork-Id: 121726 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from chlorine.canonical.com (chlorine.canonical.com [91.189.94.204]) by ozlabs.org (Postfix) with ESMTP id C9D601007D9 for ; Wed, 26 Oct 2011 03:00:11 +1100 (EST) Received: from localhost ([127.0.0.1] helo=chlorine.canonical.com) by chlorine.canonical.com with esmtp (Exim 4.71) (envelope-from ) id 1RIjPw-0006QK-6g; Tue, 25 Oct 2011 15:59:56 +0000 Received: from youngberry.canonical.com ([91.189.89.112]) by chlorine.canonical.com with esmtp (Exim 4.71) (envelope-from ) id 1RIjPt-0006PN-1B for kernel-team@lists.ubuntu.com; Tue, 25 Oct 2011 15:59:53 +0000 Received: from 212-139-208-147.dynamic.dsl.as9105.com ([212.139.208.147] helo=localhost.localdomain) by youngberry.canonical.com with esmtpsa (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1RIjPs-0003n2-Pr; Tue, 25 Oct 2011 15:59:53 +0000 From: Andy Whitcroft To: kernel-team@lists.ubuntu.com Subject: [hardy CVE 1/2] introduce explicit signed/unsigned 64bit divide Date: Tue, 25 Oct 2011 16:59:49 +0100 Message-Id: <1319558390-29769-2-git-send-email-apw@canonical.com> X-Mailer: git-send-email 1.7.5.4 In-Reply-To: <1319558390-29769-1-git-send-email-apw@canonical.com> References: <1319558390-29769-1-git-send-email-apw@canonical.com> Cc: Andy Whitcroft X-BeenThere: kernel-team@lists.ubuntu.com X-Mailman-Version: 2.1.13 Precedence: list List-Id: Kernel team discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: kernel-team-bounces@lists.ubuntu.com Errors-To: kernel-team-bounces@lists.ubuntu.com From: Roman Zippel The current do_div doesn't explicitly say that it's unsigned and the signed counterpart is missing, which is e.g. needed when dealing with time values. This introduces 64bit signed/unsigned divide functions which also attempts to cleanup the somewhat awkward calling API, which often requires the use of temporary variables for the dividend. To avoid the need for temporary variables everywhere for the remainder, each divide variant also provides a version which doesn't return the remainder. Each architecture can now provide optimized versions of these function, otherwise generic fallback implementations will be used. As an example I provided an alternative for the current x86 divide, which avoids the asm casts and using an union allows gcc to generate better code. It also avoids the upper divde in a few more cases, where the result is known (i.e. upper quotient is zero). Signed-off-by: Roman Zippel Cc: john stultz Cc: Thomas Gleixner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds (cherry picked from commit 2418f4f28f8467b92a6177af32d05737ebf6206c) CVE-2011-3209 BugLink: http://bugs.launchpad.net/bugs/880890 Signed-off-by: Andy Whitcroft --- include/asm-x86/div64.h | 20 +++++++++++++ include/linux/math64.h | 72 +++++++++++++++++++++++++++++++++++++++++++++++ lib/div64.c | 23 +++++++++++++- 3 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 include/linux/math64.h diff --git a/include/asm-x86/div64.h b/include/asm-x86/div64.h index e98d16e..370bc55 100644 --- a/include/asm-x86/div64.h +++ b/include/asm-x86/div64.h @@ -50,6 +50,26 @@ div_ll_X_l_rem(long long divs, long div, long *rem) } +static inline u64 div_u64_rem(u64 dividend, u32 divisor, u32 *remainder) +{ + union { + u64 v64; + u32 v32[2]; + } d = { dividend }; + u32 upper; + + upper = d.v32[1]; + d.v32[1] = 0; + if (upper >= divisor) { + d.v32[1] = upper / divisor; + upper %= divisor; + } + asm ("divl %2" : "=a" (d.v32[0]), "=d" (*remainder) : + "rm" (divisor), "0" (d.v32[0]), "1" (upper)); + return d.v64; +} +#define div_u64_rem div_u64_rem + extern uint64_t div64_64(uint64_t dividend, uint64_t divisor); #else diff --git a/include/linux/math64.h b/include/linux/math64.h new file mode 100644 index 0000000..6d17166 --- /dev/null +++ b/include/linux/math64.h @@ -0,0 +1,72 @@ +#ifndef _LINUX_MATH64_H +#define _LINUX_MATH64_H + +#include +#include + +#if BITS_PER_LONG == 64 + +/** + * div_u64_rem - unsigned 64bit divide with 32bit divisor with remainder + * + * This is commonly provided by 32bit archs to provide an optimized 64bit + * divide. + */ +static inline u64 div_u64_rem(u64 dividend, u32 divisor, u32 *remainder) +{ + *remainder = dividend % divisor; + return dividend / divisor; +} + +/** + * div_s64_rem - signed 64bit divide with 32bit divisor with remainder + */ +static inline s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder) +{ + *remainder = dividend % divisor; + return dividend / divisor; +} + +#elif BITS_PER_LONG == 32 + +#ifndef div_u64_rem +static inline u64 div_u64_rem(u64 dividend, u32 divisor, u32 *remainder) +{ + *remainder = do_div(dividend, divisor); + return dividend; +} +#endif + +#ifndef div_s64_rem +extern s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder); +#endif + +#endif /* BITS_PER_LONG */ + +/** + * div_u64 - unsigned 64bit divide with 32bit divisor + * + * This is the most common 64bit divide and should be used if possible, + * as many 32bit archs can optimize this variant better than a full 64bit + * divide. + */ +#ifndef div_u64 +static inline u64 div_u64(u64 dividend, u32 divisor) +{ + u32 remainder; + return div_u64_rem(dividend, divisor, &remainder); +} +#endif + +/** + * div_s64 - signed 64bit divide with 32bit divisor + */ +#ifndef div_s64 +static inline s64 div_s64(s64 dividend, s32 divisor) +{ + s32 remainder; + return div_s64_rem(dividend, divisor, &remainder); +} +#endif + +#endif /* _LINUX_MATH64_H */ diff --git a/lib/div64.c b/lib/div64.c index b71cf93..689bd76 100644 --- a/lib/div64.c +++ b/lib/div64.c @@ -16,9 +16,8 @@ * assembly versions such as arch/ppc/lib/div64.S and arch/sh/lib/div64.S. */ -#include #include -#include +#include /* Not needed on 64bit architectures */ #if BITS_PER_LONG == 32 @@ -58,6 +57,26 @@ uint32_t __attribute__((weak)) __div64_32(uint64_t *n, uint32_t base) EXPORT_SYMBOL(__div64_32); +#ifndef div_s64_rem +s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder) +{ + u64 quotient; + + if (dividend < 0) { + quotient = div_u64_rem(-dividend, abs(divisor), (u32 *)remainder); + *remainder = -*remainder; + if (divisor > 0) + quotient = -quotient; + } else { + quotient = div_u64_rem(dividend, abs(divisor), (u32 *)remainder); + if (divisor < 0) + quotient = -quotient; + } + return quotient; +} +EXPORT_SYMBOL(div_s64_rem); +#endif + /* 64bit divisor, dividend and result. dynamic precision */ uint64_t div64_64(uint64_t dividend, uint64_t divisor) {