Received: by 2002:a05:6a10:16a7:0:0:0:0 with SMTP id gp39csp596454pxb; Thu, 19 Nov 2020 08:59:38 -0800 (PST) X-Google-Smtp-Source: ABdhPJx8iL+EvqCcNa7eLIqm2BLtz+vGCamNBmWEnsFIQHdErPSk9LrsAIyh4I8LDCmEhz4VqwV0 X-Received: by 2002:aa7:c358:: with SMTP id j24mr31946749edr.265.1605805177880; Thu, 19 Nov 2020 08:59:37 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1605805177; cv=none; d=google.com; s=arc-20160816; b=rHJZaN0BTgHN64FD/tp4m9X6GcdaYHzCe64ehfD5uQ+daOPjolCeT3MB+3TQJJ3ydy GxxkV2yMUzbTzLzBPmF4nsa60PVVq0hLwDKJIdYFDgOAxyCfunw7vCszSVhoOQ+IJPSl Hxew5odjl8yjSJN2gBJOh+++RCObV5HcsHJKYs9+Xz28KAWumGSjq/7Z/b0vh4+VWD+n iZ5Cp1FXeDG44XQvVnhZS1edr3qMWwff8Q00KZsvrOZuCURw9xPiP//DZzQDLoyJuCSh PgJiBrZGoH3QWtPOfFoHWT6d1SIglqLyq1ONH6C8k/ARYwCc88VPHNPVYvmQSJRXW/ia 8Hlg== 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=kc0yTolnDMsGxXj80mTk7tps4hrFHDHxB6Ssr1B1EGD7BIhXd3/zdid7C0A7Xp7j8/ RS3JzRfZwGl3J4AlHr33MWgvMUNq7p851Ijw/szhyZs8qwTfLLU1/6Zlh8ABXkAU+nsQ qcAnS9A/vHY4LqB1A//VeF2enDkyGEdMCLjgXv0obDXv8fI8MdjXQA21bOg7eapfTfZK xwWQY2V/Y2Pt7DieDOTB20Am1vCwWeJez/9KBbHYmH1P8BwNSgUmkHVR06d/njzPbsZL WZX86SufxEeVfZZfgZuuvheCCKiVcoqF0JkV1TbHdZTxOStq+Acr05PrmciM7H7QvwHz TSNA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@privacyrequired.com header.s=stigmate header.b=ONheZzeK; 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 k14si150241edq.576.2020.11.19.08.59.14; Thu, 19 Nov 2020 08:59:37 -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=ONheZzeK; 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 S1728970AbgKSQ5L (ORCPT + 99 others); Thu, 19 Nov 2020 11:57:11 -0500 Received: from devianza.investici.org ([198.167.222.108]:24499 "EHLO devianza.investici.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727832AbgKSQ5K (ORCPT ); Thu, 19 Nov 2020 11:57:10 -0500 Received: from mx2.investici.org (unknown [127.0.0.1]) by devianza.investici.org (Postfix) with ESMTP id 4CcQd75qt4z6vNm; Thu, 19 Nov 2020 16:49:31 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=privacyrequired.com; s=stigmate; t=1605804571; bh=OaWvHg/EA6JyylB5bFXIMFDN1Jwf8aYCFNvhWBTYrvA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ONheZzeKEcKg6ZiL1ZtjOfL3L17wlqIpVkFkpljY1jztWKgnrZG+OUTCc217XhVYn UCmasz5nV6rEncJmN54HV5ov3sWq9M/MheiEOlbgAZulKAdfwGYIykbmQNTxXrIBO0 1KFiqXNKYTe9WhFz8MzpcI4UtgTFpZcpIMHTnh6Y= Received: from [198.167.222.108] (mx2.investici.org [198.167.222.108]) (Authenticated sender: laniel_francis@privacyrequired.com) by localhost (Postfix) with ESMTPSA id 4CcQd72YNtz6vNC; Thu, 19 Nov 2020 16:49:31 +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 v6 1/5] string.h: detect intra-object overflow in fortified string functions Date: Thu, 19 Nov 2020 17:49:11 +0100 Message-Id: <20201119164915.10618-2-laniel_francis@privacyrequired.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20201119164915.10618-1-laniel_francis@privacyrequired.com> References: <20201119164915.10618-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