Received: by 2002:a05:6a10:16a7:0:0:0:0 with SMTP id gp39csp239284pxb; Wed, 18 Nov 2020 03:19:58 -0800 (PST) X-Google-Smtp-Source: ABdhPJyEePV1x/UvB920wGRi39GYqmHoXVPYGcj0PUkeQLb0ON5eqekR4V4c5CnO3bhF0AmLSLpk X-Received: by 2002:a17:906:1317:: with SMTP id w23mr22842757ejb.120.1605698398379; Wed, 18 Nov 2020 03:19:58 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1605698398; cv=none; d=google.com; s=arc-20160816; b=JfHBP6HsBLmXQM5TojxwuPNQY7ozR9TyztA/q3sKWrhDM+VUSl3UgBFypJSZRG+snq uHeHwcnemaNrUl+43SqT5YWBRuxN35NzF48jjn3DLuztTY1XQvC5QH/aCWqG9G+BfQAa KwDR/qCxS83h8CYQYCpcr/QEHvRI/2CmeGAVkGV5DjtZ75NlbcMRFFgI2AxAkl0hKD5L jjVPoL6T+gwx7oV7U5vGQtjdc49xd8oboYh8DVw8vQsVZ8fuQYGdeIvfYWzwiPeTXPCs YiMv24u24atVzyilRH0t2OkiPvBgmjR/CcK6zdZv7Xz95rYO3mFY3GMalrMEHWYiIwiZ dBkA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=OaWvHg/EA6JyylB5bFXIMFDN1Jwf8aYCFNvhWBTYrvA=; b=i9gLarORUxfBz0brTWKu4WMTrAtUKkKTLxvRGnV6LEwVnTMLBLW1+51QwlraFarB4g ekJ39KutmKv6HxpTtaf+aNnHhSHp/b+SmLCDisC3B6/mLhWlkI1LeFx4RZUe8Oj2QmpQ YeumOBxABNRptNWATfo1fco5D9iMA7TBoDKwxTcjHWcZji8j0JvnFa5e25jCTUJAyjDq kGLwJzxU9/C4uXFHznaiA8mT48q0f5/PjjFSaB51ppFv7arEK2BkNJZ8HRfrvdd9Rux5 Dcr9tXOu/moO2oxuKtY9RJNAuO51kHeXs+nAhPu03aDgCIrbh4z9yvdISYocdQ4zLNKn A6CA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@privacyrequired.com header.s=stigmate header.b=eICoYrgJ; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id 63si16680304edb.454.2020.11.18.03.19.35; Wed, 18 Nov 2020 03:19:58 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@privacyrequired.com header.s=stigmate header.b=eICoYrgJ; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727684AbgKRLRJ (ORCPT + 99 others); Wed, 18 Nov 2020 06:17:09 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37536 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726696AbgKRLRI (ORCPT ); Wed, 18 Nov 2020 06:17:08 -0500 Received: from latitanza.investici.org (latitanza.investici.org [IPv6:2001:888:2000:56::19]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 49063C061A4D for ; Wed, 18 Nov 2020 03:17:08 -0800 (PST) Received: from mx3.investici.org (unknown [127.0.0.1]) by latitanza.investici.org (Postfix) with ESMTP id 4Cbg5J4CYfz8sh3; Wed, 18 Nov 2020 11:07:48 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=privacyrequired.com; s=stigmate; t=1605697668; bh=OaWvHg/EA6JyylB5bFXIMFDN1Jwf8aYCFNvhWBTYrvA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=eICoYrgJQAu73TYo9feKoHl5/z4keVo2O7m0HezKJZvx6NmUca6v8aGWJmzO6HxMR RYtoNRY1WitYrLAOo69nZi5dCQu3fDGPKodSINz+kI+zT0+YjRrjpzGXOrYJmKAr2Y u+4gGb7dsG4nVrZDJ6SdpaUuBH0DSXLL/7TKIMmw= Received: from [82.94.249.234] (mx3.investici.org [82.94.249.234]) (Authenticated sender: laniel_francis@privacyrequired.com) by localhost (Postfix) with ESMTPSA id 4Cbg5H68G0z8sgF; Wed, 18 Nov 2020 11:07:47 +0000 (UTC) From: laniel_francis@privacyrequired.com To: akpm@linux-foundation.org Cc: linux-hardening@vger.kernel.org, linux-mm@kvack.org, linux-kernel@vger.kernel.org, dja@axtens.net, keescook@chromium.org, Daniel Micay Subject: [PATCH v5 1/5] string.h: detect intra-object overflow in fortified string functions Date: Wed, 18 Nov 2020 12:07:27 +0100 Message-Id: <20201118110731.15833-2-laniel_francis@privacyrequired.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20201118110731.15833-1-laniel_francis@privacyrequired.com> References: <20201118110731.15833-1-laniel_francis@privacyrequired.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Daniel Axtens When the fortify feature was first introduced in commit 6974f0c4555e ("include/linux/string.h: add the option of fortified string.h functions"), Daniel Micay observed: * It should be possible to optionally use __builtin_object_size(x, 1) for some functions (C strings) to detect intra-object overflows (like glibc's _FORTIFY_SOURCE=2), but for now this takes the conservative approach to avoid likely compatibility issues. This is a case that often cannot be caught by KASAN. Consider: struct foo { char a[10]; char b[10]; } void test() { char *msg; struct foo foo; msg = kmalloc(16, GFP_KERNEL); strcpy(msg, "Hello world!!"); // this copy overwrites foo.b strcpy(foo.a, msg); } The questionable copy overflows foo.a and writes to foo.b as well. It cannot be detected by KASAN. Currently it is also not detected by fortify, because strcpy considers __builtin_object_size(x, 0), which considers the size of the surrounding object (here, struct foo). However, if we switch the string functions over to use __builtin_object_size(x, 1), the compiler will measure the size of the closest surrounding subobject (here, foo.a), rather than the size of the surrounding object as a whole. See https://gcc.gnu.org/onlinedocs/gcc/Object-Size-Checking.html for more info. Only do this for string functions: we cannot use it on things like memcpy, memmove, memcmp and memchr_inv due to code like this which purposefully operates on multiple structure members: (arch/x86/kernel/traps.c) /* * regs->sp points to the failing IRET frame on the * ESPFIX64 stack. Copy it to the entry stack. This fills * in gpregs->ss through gpregs->ip. * */ memmove(&gpregs->ip, (void *)regs->sp, 5*8); This change passes an allyesconfig on powerpc and x86, and an x86 kernel built with it survives running with syz-stress from syzkaller, so it seems safe so far. Cc: Daniel Micay Cc: Kees Cook Reviewed-by: Kees Cook Signed-off-by: Daniel Axtens --- include/linux/string.h | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/include/linux/string.h b/include/linux/string.h index b1f3894a0a3e..46e91d684c47 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -292,7 +292,7 @@ extern char *__underlying_strncpy(char *p, const char *q, __kernel_size_t size) __FORTIFY_INLINE char *strncpy(char *p, const char *q, __kernel_size_t size) { - size_t p_size = __builtin_object_size(p, 0); + size_t p_size = __builtin_object_size(p, 1); if (__builtin_constant_p(size) && p_size < size) __write_overflow(); if (p_size < size) @@ -302,7 +302,7 @@ __FORTIFY_INLINE char *strncpy(char *p, const char *q, __kernel_size_t size) __FORTIFY_INLINE char *strcat(char *p, const char *q) { - size_t p_size = __builtin_object_size(p, 0); + size_t p_size = __builtin_object_size(p, 1); if (p_size == (size_t)-1) return __underlying_strcat(p, q); if (strlcat(p, q, p_size) >= p_size) @@ -313,7 +313,7 @@ __FORTIFY_INLINE char *strcat(char *p, const char *q) __FORTIFY_INLINE __kernel_size_t strlen(const char *p) { __kernel_size_t ret; - size_t p_size = __builtin_object_size(p, 0); + size_t p_size = __builtin_object_size(p, 1); /* Work around gcc excess stack consumption issue */ if (p_size == (size_t)-1 || @@ -328,7 +328,7 @@ __FORTIFY_INLINE __kernel_size_t strlen(const char *p) extern __kernel_size_t __real_strnlen(const char *, __kernel_size_t) __RENAME(strnlen); __FORTIFY_INLINE __kernel_size_t strnlen(const char *p, __kernel_size_t maxlen) { - size_t p_size = __builtin_object_size(p, 0); + size_t p_size = __builtin_object_size(p, 1); __kernel_size_t ret = __real_strnlen(p, maxlen < p_size ? maxlen : p_size); if (p_size <= ret && maxlen != ret) fortify_panic(__func__); @@ -340,8 +340,8 @@ extern size_t __real_strlcpy(char *, const char *, size_t) __RENAME(strlcpy); __FORTIFY_INLINE size_t strlcpy(char *p, const char *q, size_t size) { size_t ret; - size_t p_size = __builtin_object_size(p, 0); - size_t q_size = __builtin_object_size(q, 0); + size_t p_size = __builtin_object_size(p, 1); + size_t q_size = __builtin_object_size(q, 1); if (p_size == (size_t)-1 && q_size == (size_t)-1) return __real_strlcpy(p, q, size); ret = strlen(q); @@ -361,8 +361,8 @@ __FORTIFY_INLINE size_t strlcpy(char *p, const char *q, size_t size) __FORTIFY_INLINE char *strncat(char *p, const char *q, __kernel_size_t count) { size_t p_len, copy_len; - size_t p_size = __builtin_object_size(p, 0); - size_t q_size = __builtin_object_size(q, 0); + size_t p_size = __builtin_object_size(p, 1); + size_t q_size = __builtin_object_size(q, 1); if (p_size == (size_t)-1 && q_size == (size_t)-1) return __underlying_strncat(p, q, count); p_len = strlen(p); @@ -475,11 +475,16 @@ __FORTIFY_INLINE void *kmemdup(const void *p, size_t size, gfp_t gfp) /* defined after fortified strlen and memcpy to reuse them */ __FORTIFY_INLINE char *strcpy(char *p, const char *q) { - size_t p_size = __builtin_object_size(p, 0); - size_t q_size = __builtin_object_size(q, 0); + size_t p_size = __builtin_object_size(p, 1); + size_t q_size = __builtin_object_size(q, 1); + size_t size; if (p_size == (size_t)-1 && q_size == (size_t)-1) return __underlying_strcpy(p, q); - memcpy(p, q, strlen(q) + 1); + size = strlen(q) + 1; + /* test here to use the more stringent object size */ + if (p_size < size) + fortify_panic(__func__); + memcpy(p, q, size); return p; } -- 2.20.1