From patchwork Sat Feb 17 04:48:24 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kees Cook X-Patchwork-Id: 202568 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a05:693c:2685:b0:108:e6aa:91d0 with SMTP id mn5csp149810dyc; Fri, 16 Feb 2024 20:54:47 -0800 (PST) X-Forwarded-Encrypted: i=3; AJvYcCV4vBX9NIUSBKnwt5s9y+kVL4ysOSO3z+n0Ewpb3aFv/uXCJswsxmx/Is6wSx+d9Aepx7MPVd1OyPPgBLSsgrIp1MGJbQ== X-Google-Smtp-Source: AGHT+IFa3c4jV+V6gVh/BoyaoxzWn3F5dkk8pI8T5s9c6552VWVR3hhyxtKlJ2/+23XeHWl23a23 X-Received: by 2002:a17:906:5f84:b0:a3c:c346:6894 with SMTP id a4-20020a1709065f8400b00a3cc3466894mr5023919eju.31.1708145686956; Fri, 16 Feb 2024 20:54:46 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1708145686; cv=pass; d=google.com; s=arc-20160816; b=Q4eX+rbbCgP8+oO9WZfU7W1WOcwe1HzSPQtPga5p1B07ajGzEdELyMr/6WgLVQ9Wuy CBEBW2a4REN6VDuc8Kw0m5munMT+H1jqoDMMoFV3jVtho+V6opPFoJuJ1OjNTIzq+KNb V4ZXILV1mgp+dgxV6kXCuoQXVTOgEF1f4ZikHj58Vi46NzCJrwaZ7biD6HEfwTkFRYfQ SWZLG1Q2CBi8al0W3V61DJzdtxOTFWM7Sc06u4QmY3lOGFWQXTTV7P3W6oosDEAZ9kr2 WLfnjIGRcGTHfNbuX6xUjy+rytkqSSYcDsclfo8uiasVYGDp2sykwfq5K5VTqlysZJpG hqEg== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:list-unsubscribe :list-subscribe:list-id:precedence:references:in-reply-to:message-id :date:subject:cc:to:from:dkim-signature; bh=KQ5gV9CXUyFtitKBx8YkJF+WHLIxVIqL5lNg3JpCqzI=; fh=RGjMRLmQANii1BTINzjeAwf16aZYsQDqdbpG85ZFhMM=; b=NlrAOnuVBSL+GmwtsXoJ99yoSNj3KHAenCbB+99jcURnJXOxWAHglQxD9ELA0bpGRH sEbgsq4JEeufMCtcgsMtyKL7HJrEjGMRiO0hvimzGQQEDXxq3U13YgIEIHG/VSZTE8oi yNkmcIaUefIVtxPs+M8UQfiygHPSBtis14xqKUvlENohYsSgADMLV8NscWh0ArR2JHBm LdxkUHPkABRxS4ao92bK7QE8h0DX+YGQWBHoQPao4f2hfOkNmP5iVaqfpKGRqm9QOeAf wldSHmtlwUhuyN1PjpQyXJSpDuz+KbhoLSgoYP0ZTfHNmmGr2x5RZvP3jX/XuRDvcp2d OZYQ==; dara=google.com ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@chromium.org header.s=google header.b=IjTYoksW; arc=pass (i=1 spf=pass spfdomain=chromium.org dkim=pass dkdomain=chromium.org dmarc=pass fromdomain=chromium.org); spf=pass (google.com: domain of linux-kernel+bounces-69686-ouuuleilei=gmail.com@vger.kernel.org designates 147.75.80.249 as permitted sender) smtp.mailfrom="linux-kernel+bounces-69686-ouuuleilei=gmail.com@vger.kernel.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=chromium.org Received: from am.mirrors.kernel.org (am.mirrors.kernel.org. [147.75.80.249]) by mx.google.com with ESMTPS id r10-20020a1709067fca00b00a3df1ee0c65si556770ejs.286.2024.02.16.20.54.46 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 16 Feb 2024 20:54:46 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel+bounces-69686-ouuuleilei=gmail.com@vger.kernel.org designates 147.75.80.249 as permitted sender) client-ip=147.75.80.249; Authentication-Results: mx.google.com; dkim=pass header.i=@chromium.org header.s=google header.b=IjTYoksW; arc=pass (i=1 spf=pass spfdomain=chromium.org dkim=pass dkdomain=chromium.org dmarc=pass fromdomain=chromium.org); spf=pass (google.com: domain of linux-kernel+bounces-69686-ouuuleilei=gmail.com@vger.kernel.org designates 147.75.80.249 as permitted sender) smtp.mailfrom="linux-kernel+bounces-69686-ouuuleilei=gmail.com@vger.kernel.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=chromium.org Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by am.mirrors.kernel.org (Postfix) with ESMTPS id 61EA11F21D6E for ; Sat, 17 Feb 2024 04:54:46 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id A4AB01D55E; Sat, 17 Feb 2024 04:53:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b="IjTYoksW" Received: from mail-pl1-f174.google.com (mail-pl1-f174.google.com [209.85.214.174]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BD1021C69D for ; Sat, 17 Feb 2024 04:53:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.174 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1708145621; cv=none; b=BIIlBprayIFCLSIN8TuQjqQebLyVviXtV93t97qe+6ljUrmbwlpux7PY/ZPhyAy/tz6YWYXOZTx35dvxKbzQil/AjyIvDAhujWfXUOfPz4AeAfl4s0X8iuzBN6smcCH2yhMRLw9RGMPzlhte45jWKcBtyyY1H0elgrD5Hj/vbvo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1708145621; c=relaxed/simple; bh=YoMI2eAU7bea6ne283y1TtKCLsonFYrY3DXQAfqNLjo=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=HwAXCpQ7B0Go8J/QdYoYTWGFN8iPAdfau+DNMwupxR+d/rdBqTy6NFi5Bj7J7e+BkN2ECkvc9Nl/86WFwv8Nx4t9z+a5sAGEjk4f2SNOSlhbIrti9QPwfeU17tXzTDOlWyQTgfYV+WWi+XdEw2smfHjHi0hp7LFcTkX+YB+hj8c= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=chromium.org; spf=pass smtp.mailfrom=chromium.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b=IjTYoksW; arc=none smtp.client-ip=209.85.214.174 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=chromium.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=chromium.org Received: by mail-pl1-f174.google.com with SMTP id d9443c01a7336-1d944e8f367so25941995ad.0 for ; Fri, 16 Feb 2024 20:53:39 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1708145619; x=1708750419; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=KQ5gV9CXUyFtitKBx8YkJF+WHLIxVIqL5lNg3JpCqzI=; b=IjTYoksWNjGW4FpT1FbnV0/u4yvyzbND0NBlMgWn0j2ucjmWdkOujmPi7eu+SXgK0D DF7n1KKZOs99xl4kZtIgRP2/Swx7jCYU4rinNaPhbJeznm4mA+YZIRrQQ4PAQdyRsy6U Tl9lMNXP1SiqxfxY9gxXbv583oJHYE6+HdTls= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1708145619; x=1708750419; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=KQ5gV9CXUyFtitKBx8YkJF+WHLIxVIqL5lNg3JpCqzI=; b=LDm/sGkGVkqhnY89Zk4EtFxanbgBVnLcjwGiy5SKSY+hKSz+2F6rWQUzgerVdf8WE/ prHOWfczjcyIOfWZEzp+VvtRGNyaoBH99Odj7B/pgIR4zEetN381YVQSqebkEtK4EuAF fyIiTm14J2CBahC+grBBUB+TOYuyUVpX+wGHq4DjyIncT/VbslXo7G4eb50vWTpSQLBk aT+woVJylnYqEQ5DHAy136nZhmOBjfzq6mlBn7DHB+4Qbon0soDWctjvoAj9oAg5U5AV cOmEWWQPHMpI6TsQRdyAJ8lMA5feeg4Jm55UVYbviHTv4l0RpsiN8vudWrs0pUhuMo3B 7DSw== X-Forwarded-Encrypted: i=1; AJvYcCWRk8y+zQ4to01FzWHZ+A0sRNZDUaqWaH7/WT/DmIXLjOKja11gZW+SdmXAPd9t6Oay2XviUQDrMHl7RQr8X9diHmMYHnj6ykkWwE8H X-Gm-Message-State: AOJu0Yym07ETqqoDklVLUPpmbm9CjhHqTRm7A9px3vcQPh2t1S2brxIx CEiU+QKMTaEacJf0jKI8kbODbQMdja+sqKC8qou+SBcbgo9lGUdef+MHguBWdw== X-Received: by 2002:a17:902:dad2:b0:1da:1d91:7131 with SMTP id q18-20020a170902dad200b001da1d917131mr8574310plx.57.1708145618863; Fri, 16 Feb 2024 20:53:38 -0800 (PST) Received: from www.outflux.net ([198.0.35.241]) by smtp.gmail.com with ESMTPSA id s3-20020a170902c64300b001d93ba1120dsm625287pls.200.2024.02.16.20.53.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 16 Feb 2024 20:53:36 -0800 (PST) From: Kees Cook To: linux-hardening@vger.kernel.org Cc: Kees Cook , Alexander Lobakin , Andy Shevchenko , Cezary Rojewski , Puyou Lu , Mark Brown , Brendan Higgins , David Gow , Nick Desaulniers , linux-kernel@vger.kernel.org, kunit-dev@googlegroups.com Subject: [PATCH v3 1/5] fortify: Split reporting and avoid passing string pointer Date: Fri, 16 Feb 2024 20:48:24 -0800 Message-Id: <20240217045335.1526675-1-keescook@chromium.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240217043535.make.664-kees@kernel.org> References: <20240217043535.make.664-kees@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=12013; i=keescook@chromium.org; h=from:subject; bh=YoMI2eAU7bea6ne283y1TtKCLsonFYrY3DXQAfqNLjo=; b=owEBbQKS/ZANAwAKAYly9N/cbcAmAcsmYgBl0Dqc1uepxbmeD9CO9ZPH176i41FVAe9bBqFpA ZKOI9Cg7oKJAjMEAAEKAB0WIQSlw/aPIp3WD3I+bhOJcvTf3G3AJgUCZdA6nAAKCRCJcvTf3G3A Jr3AD/9XaSs0pbudKqxbrv8zV31XF1AVf1DPO/73+eJuGkjqNKLlqoagd8eBj+vNzUEhvt5HsCj x9tykiOGBGlBORBoMDLeuxhB/dcVIoUtOXaufi/ff5fn4lb1A+jfbkHGHFM4tpCoUDjxE5qdpJa ETavEnAb1Xr7H90OkxvJy5b/b/t5+OmaAulRK+3xf3lWNNk4QlRJqGDjn7Z6rC+UOty51iBu76o YaFHyKwc0AOTb3dAedKPD7qmv9REtVIQhgebOPlmwi/5hX1f57rPYcTYAaHJQykIlhPH1TEvs4r dIyCuvgVbN0MRc5d3vAtjkXGT59hpkXOmJUwp0Vqi/Pa1bjvKXlOSL1w4OW/iRpyUM4PDmjCfg8 0dCZMbsVMTS4Cm749NE1vdQ3fzsaybO5o1y7/ZFUpn6iYaGGwADbuxRY5Qgtzwb6ctSy5bxuF2i UsGaDvv8jkUEpZgKuwpvAXwmM5uIVp6OD+trbLyBfHGy6Js7uAeSgT8Gc5FI6IWgTWRsaAJWdmt OXgpvGzYyzE0Gwks1IUUpPayDh5n2AOsDpuxX63SuP21mMF52tU4/jKNLby5PSULpkoDrwkqAJI Efy5AwFNXu4z2yb2t8uuit+XCXMXM3SFsDLIzZWOochiK0yJgSYY/NLCoYAu+qCEue2ujuhVxM5 lXsFMxtgtqaiqJw== X-Developer-Key: i=keescook@chromium.org; a=openpgp; fpr=A5C3F68F229DD60F723E6E138972F4DFDC6DC026 X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1791120571619570495 X-GMAIL-MSGID: 1791120571619570495 In preparation for KUnit testing and further improvements in fortify failure reporting, split out the report and encode the function and access failure (read or write overflow) into a single u8 argument. This mainly ends up saving a tiny bit of space in the data segment. For a defconfig with FORTIFY_SOURCE enabled: $ size gcc/vmlinux.before gcc/vmlinux.after text data bss dec hex filename 26132309 9760658 2195460 38088427 2452eeb gcc/vmlinux.before 26132386 9748382 2195460 38076228 244ff44 gcc/vmlinux.after Reviewed-by: Alexander Lobakin Signed-off-by: Kees Cook --- Cc: linux-hardening@vger.kernel.org Cc: Andy Shevchenko Cc: Cezary Rojewski Cc: Puyou Lu Cc: Mark Brown --- arch/arm/boot/compressed/misc.c | 2 +- arch/x86/boot/compressed/misc.c | 2 +- include/linux/fortify-string.h | 81 ++++++++++++++++++++++++--------- lib/string_helpers.c | 23 ++++++++-- tools/objtool/noreturns.h | 2 +- 5 files changed, 83 insertions(+), 27 deletions(-) diff --git a/arch/arm/boot/compressed/misc.c b/arch/arm/boot/compressed/misc.c index 6b4baa6a9a50..d93e2e466f6a 100644 --- a/arch/arm/boot/compressed/misc.c +++ b/arch/arm/boot/compressed/misc.c @@ -154,7 +154,7 @@ decompress_kernel(unsigned long output_start, unsigned long free_mem_ptr_p, putstr(" done, booting the kernel.\n"); } -void fortify_panic(const char *name) +void __fortify_panic(const u8 reason) { error("detected buffer overflow"); } diff --git a/arch/x86/boot/compressed/misc.c b/arch/x86/boot/compressed/misc.c index b99e08e6815b..c9971b9dbb09 100644 --- a/arch/x86/boot/compressed/misc.c +++ b/arch/x86/boot/compressed/misc.c @@ -496,7 +496,7 @@ asmlinkage __visible void *extract_kernel(void *rmode, unsigned char *output) return output + entry_offset; } -void fortify_panic(const char *name) +void __fortify_panic(const u8 reason) { error("detected buffer overflow"); } diff --git a/include/linux/fortify-string.h b/include/linux/fortify-string.h index 06b3aaa63724..4f6767dcd933 100644 --- a/include/linux/fortify-string.h +++ b/include/linux/fortify-string.h @@ -2,6 +2,7 @@ #ifndef _LINUX_FORTIFY_STRING_H_ #define _LINUX_FORTIFY_STRING_H_ +#include #include #include #include @@ -9,7 +10,44 @@ #define __FORTIFY_INLINE extern __always_inline __gnu_inline __overloadable #define __RENAME(x) __asm__(#x) -void fortify_panic(const char *name) __noreturn __cold; +#define FORTIFY_REASON_DIR(r) FIELD_GET(BIT(0), r) +#define FORTIFY_REASON_FUNC(r) FIELD_GET(GENMASK(7, 1), r) +#define FORTIFY_REASON(func, write) (FIELD_PREP(BIT(0), write) | \ + FIELD_PREP(GENMASK(7, 1), func)) + +#define fortify_panic(func, write) \ + __fortify_panic(FORTIFY_REASON(func, write)) + +#define FORTIFY_READ 0 +#define FORTIFY_WRITE 1 + +#define EACH_FORTIFY_FUNC(macro) \ + macro(strncpy), \ + macro(strnlen), \ + macro(strlen), \ + macro(strscpy), \ + macro(strlcat), \ + macro(strcat), \ + macro(strncat), \ + macro(memset), \ + macro(memcpy), \ + macro(memmove), \ + macro(memscan), \ + macro(memcmp), \ + macro(memchr), \ + macro(memchr_inv), \ + macro(kmemdup), \ + macro(strcpy), \ + macro(UNKNOWN), + +#define MAKE_FORTIFY_FUNC(func) FORTIFY_FUNC_##func + +enum fortify_func { + EACH_FORTIFY_FUNC(MAKE_FORTIFY_FUNC) +}; + +void __fortify_report(const u8 reason); +void __fortify_panic(const u8 reason) __cold __noreturn; void __read_overflow(void) __compiletime_error("detected read beyond size of object (1st parameter)"); void __read_overflow2(void) __compiletime_error("detected read beyond size of object (2nd parameter)"); void __read_overflow2_field(size_t avail, size_t wanted) __compiletime_warning("detected read beyond size of field (2nd parameter); maybe use struct_group()?"); @@ -143,7 +181,7 @@ char *strncpy(char * const POS p, const char *q, __kernel_size_t size) if (__compiletime_lessthan(p_size, size)) __write_overflow(); if (p_size < size) - fortify_panic(__func__); + fortify_panic(FORTIFY_FUNC_strncpy, FORTIFY_WRITE); return __underlying_strncpy(p, q, size); } @@ -174,7 +212,7 @@ __FORTIFY_INLINE __kernel_size_t strnlen(const char * const POS p, __kernel_size /* Do not check characters beyond the end of p. */ ret = __real_strnlen(p, maxlen < p_size ? maxlen : p_size); if (p_size <= ret && maxlen != ret) - fortify_panic(__func__); + fortify_panic(FORTIFY_FUNC_strnlen, FORTIFY_READ); return ret; } @@ -210,7 +248,7 @@ __kernel_size_t __fortify_strlen(const char * const POS p) return __underlying_strlen(p); ret = strnlen(p, p_size); if (p_size <= ret) - fortify_panic(__func__); + fortify_panic(FORTIFY_FUNC_strlen, FORTIFY_READ); return ret; } @@ -261,7 +299,7 @@ __FORTIFY_INLINE ssize_t sized_strscpy(char * const POS p, const char * const PO * p_size. */ if (len > p_size) - fortify_panic(__func__); + fortify_panic(FORTIFY_FUNC_strscpy, FORTIFY_WRITE); /* * We can now safely call vanilla strscpy because we are protected from: @@ -319,7 +357,7 @@ size_t strlcat(char * const POS p, const char * const POS q, size_t avail) /* Give up if string is already overflowed. */ if (p_size <= p_len) - fortify_panic(__func__); + fortify_panic(FORTIFY_FUNC_strlcat, FORTIFY_READ); if (actual >= avail) { copy_len = avail - p_len - 1; @@ -328,7 +366,7 @@ size_t strlcat(char * const POS p, const char * const POS q, size_t avail) /* Give up if copy will overflow. */ if (p_size <= actual) - fortify_panic(__func__); + fortify_panic(FORTIFY_FUNC_strlcat, FORTIFY_WRITE); __underlying_memcpy(p + p_len, q, copy_len); p[actual] = '\0'; @@ -357,7 +395,7 @@ char *strcat(char * const POS p, const char *q) const size_t p_size = __member_size(p); if (strlcat(p, q, p_size) >= p_size) - fortify_panic(__func__); + fortify_panic(FORTIFY_FUNC_strcat, FORTIFY_WRITE); return p; } @@ -393,7 +431,7 @@ char *strncat(char * const POS p, const char * const POS q, __kernel_size_t coun p_len = strlen(p); copy_len = strnlen(q, count); if (p_size < p_len + copy_len + 1) - fortify_panic(__func__); + fortify_panic(FORTIFY_FUNC_strncat, FORTIFY_WRITE); __underlying_memcpy(p + p_len, q, copy_len); p[p_len + copy_len] = '\0'; return p; @@ -434,7 +472,7 @@ __FORTIFY_INLINE void fortify_memset_chk(__kernel_size_t size, * lengths are unknown.) */ if (p_size != SIZE_MAX && p_size < size) - fortify_panic("memset"); + fortify_panic(FORTIFY_FUNC_memset, FORTIFY_WRITE); } #define __fortify_memset_chk(p, c, size, p_size, p_size_field) ({ \ @@ -488,7 +526,7 @@ __FORTIFY_INLINE bool fortify_memcpy_chk(__kernel_size_t size, const size_t q_size, const size_t p_size_field, const size_t q_size_field, - const char *func) + const u8 func) { if (__builtin_constant_p(size)) { /* @@ -532,9 +570,10 @@ __FORTIFY_INLINE bool fortify_memcpy_chk(__kernel_size_t size, * (The SIZE_MAX test is to optimize away checks where the buffer * lengths are unknown.) */ - if ((p_size != SIZE_MAX && p_size < size) || - (q_size != SIZE_MAX && q_size < size)) - fortify_panic(func); + if (p_size != SIZE_MAX && p_size < size) + fortify_panic(func, FORTIFY_WRITE); + else if (q_size != SIZE_MAX && q_size < size) + fortify_panic(func, FORTIFY_READ); /* * Warn when writing beyond destination field size. @@ -567,7 +606,7 @@ __FORTIFY_INLINE bool fortify_memcpy_chk(__kernel_size_t size, const size_t __q_size_field = (q_size_field); \ WARN_ONCE(fortify_memcpy_chk(__fortify_size, __p_size, \ __q_size, __p_size_field, \ - __q_size_field, #op), \ + __q_size_field, FORTIFY_FUNC_ ##op), \ #op ": detected field-spanning write (size %zu) of single %s (size %zu)\n", \ __fortify_size, \ "field \"" #p "\" at " FILE_LINE, \ @@ -634,7 +673,7 @@ __FORTIFY_INLINE void *memscan(void * const POS0 p, int c, __kernel_size_t size) if (__compiletime_lessthan(p_size, size)) __read_overflow(); if (p_size < size) - fortify_panic(__func__); + fortify_panic(FORTIFY_FUNC_memscan, FORTIFY_READ); return __real_memscan(p, c, size); } @@ -651,7 +690,7 @@ int memcmp(const void * const POS0 p, const void * const POS0 q, __kernel_size_t __read_overflow2(); } if (p_size < size || q_size < size) - fortify_panic(__func__); + fortify_panic(FORTIFY_FUNC_memcmp, FORTIFY_READ); return __underlying_memcmp(p, q, size); } @@ -663,7 +702,7 @@ void *memchr(const void * const POS0 p, int c, __kernel_size_t size) if (__compiletime_lessthan(p_size, size)) __read_overflow(); if (p_size < size) - fortify_panic(__func__); + fortify_panic(FORTIFY_FUNC_memchr, FORTIFY_READ); return __underlying_memchr(p, c, size); } @@ -675,7 +714,7 @@ __FORTIFY_INLINE void *memchr_inv(const void * const POS0 p, int c, size_t size) if (__compiletime_lessthan(p_size, size)) __read_overflow(); if (p_size < size) - fortify_panic(__func__); + fortify_panic(FORTIFY_FUNC_memchr_inv, FORTIFY_READ); return __real_memchr_inv(p, c, size); } @@ -688,7 +727,7 @@ __FORTIFY_INLINE void *kmemdup(const void * const POS0 p, size_t size, gfp_t gfp if (__compiletime_lessthan(p_size, size)) __read_overflow(); if (p_size < size) - fortify_panic(__func__); + fortify_panic(FORTIFY_FUNC_kmemdup, FORTIFY_READ); return __real_kmemdup(p, size, gfp); } @@ -725,7 +764,7 @@ char *strcpy(char * const POS p, const char * const POS q) __write_overflow(); /* Run-time check for dynamic size overflow. */ if (p_size < size) - fortify_panic(__func__); + fortify_panic(FORTIFY_FUNC_strcpy, FORTIFY_WRITE); __underlying_memcpy(p, q, size); return p; } diff --git a/lib/string_helpers.c b/lib/string_helpers.c index 606c3099013f..9291dc74ae01 100644 --- a/lib/string_helpers.c +++ b/lib/string_helpers.c @@ -1008,10 +1008,27 @@ EXPORT_SYMBOL(__read_overflow2_field); void __write_overflow_field(size_t avail, size_t wanted) { } EXPORT_SYMBOL(__write_overflow_field); -void fortify_panic(const char *name) +static const char * const fortify_func_name[] = { +#define MAKE_FORTIFY_FUNC_NAME(func) [MAKE_FORTIFY_FUNC(func)] = #func + EACH_FORTIFY_FUNC(MAKE_FORTIFY_FUNC_NAME) +#undef MAKE_FORTIFY_FUNC_NAME +}; + +void __fortify_report(const u8 reason) +{ + const u8 func = FORTIFY_REASON_FUNC(reason); + const bool write = FORTIFY_REASON_DIR(reason); + const char *name; + + name = fortify_func_name[umin(func, FORTIFY_FUNC_UNKNOWN)]; + WARN(1, "%s: detected buffer %s overflow\n", name, str_read_write(!write)); +} +EXPORT_SYMBOL(__fortify_report); + +void __fortify_panic(const u8 reason) { - pr_emerg("detected buffer overflow in %s\n", name); + __fortify_report(reason); BUG(); } -EXPORT_SYMBOL(fortify_panic); +EXPORT_SYMBOL(__fortify_panic); #endif /* CONFIG_FORTIFY_SOURCE */ diff --git a/tools/objtool/noreturns.h b/tools/objtool/noreturns.h index 1685d7ea6a9f..3a301696f005 100644 --- a/tools/objtool/noreturns.h +++ b/tools/objtool/noreturns.h @@ -6,6 +6,7 @@ * * Yes, this is unfortunate. A better solution is in the works. */ +NORETURN(__fortify_panic) NORETURN(__kunit_abort) NORETURN(__module_put_and_kthread_exit) NORETURN(__reiserfs_panic) @@ -22,7 +23,6 @@ NORETURN(do_exit) NORETURN(do_group_exit) NORETURN(do_task_dead) NORETURN(ex_handler_msr_mce) -NORETURN(fortify_panic) NORETURN(hlt_play_dead) NORETURN(hv_ghcb_terminate) NORETURN(kthread_complete_and_exit) From patchwork Sat Feb 17 04:48:25 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kees Cook X-Patchwork-Id: 202567 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a05:693c:2685:b0:108:e6aa:91d0 with SMTP id mn5csp149678dyc; Fri, 16 Feb 2024 20:54:08 -0800 (PST) X-Forwarded-Encrypted: i=3; AJvYcCVb+W5tzfp9CmR+e6iunfXm4WVdD41K+fXfstcLOhQUWU8tkofqPxsPMbrpeme6E9A9Y3cF1Tk1Bohcf/y6T+yU5qbo9w== X-Google-Smtp-Source: AGHT+IE1wpvg50mJ4UQJAAyhFqq0SC9TMtugizFhP6gSy6gQwsR2ggl6Nx1GhffE6OlIz4Uiydm1 X-Received: by 2002:a17:906:c2d4:b0:a3c:8784:2731 with SMTP id ch20-20020a170906c2d400b00a3c87842731mr5785269ejb.31.1708145648150; Fri, 16 Feb 2024 20:54:08 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1708145648; cv=pass; d=google.com; s=arc-20160816; b=Ug7O+7VSDjFN40BgFrIneBTXgdSDRna2v3O6hrKXLh1bn2Rb4Y5hwMBuv1YDGqU6m7 KudVWp5gnK6tyX7ffk4fpjlK35kqDCPKijbI3P7XaxpVPtcL1j8QQtODmElQAl04H7CR w3TKNctAI7mjVfzXA6vV9+4RH1DvaFsXXT840pwG1Xt4Uj2M99r6tmMxW6dGnAAYjxcj 7MMM0KUM2ZoB+PplmRdFfut/GDi0w+3aCoSrkkJDdHWRMXi8bVvKQePpMPpXlzQKJK5e KCUXu7+4jF6bbaJYPi+Wk560oIZccLxBx3DpTcB7ohS93sOhEFID2jxHOcl9xAvMGx+f eZxA== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:list-unsubscribe :list-subscribe:list-id:precedence:references:in-reply-to:message-id :date:subject:cc:to:from:dkim-signature; bh=7qdAfjTcONjZuFbVJIFLeD6GXhLkNJkyoK1rJKpx18g=; fh=XmuWYzTKbePbeHBLfp2ftXtPXA3jp0TNkhxHDjC/hr0=; b=gHlnsHrAx1wxBlxuxgV171y9MP+HzOou8ZR2J0g5HqqwGHuzJ9JIxyv4Dw1tu6wvEa haI6VvABKGxL9Xc21UR0FDGZfdjTzloNuMVQ0yBnOu3FulapBnmOu8GVqTCVG53WpcMd bh8HcILIp+1J39P1038xSdg6hRHwwL36iRlfic26V9ME8Z7QnS3Awtfqj611hYDTThrO sUJzQMcmuhhbvrLmQJQ3GBljRkZZiVMhuWZVdLEtZEdyeNUsf9omtVEZckaGw2kOsxyC CH6hZpVoBCmYGPW4Lq9yNSBnrnW1BRcgR5tUHy2nc8waXVSc2VRjJkcWe7rWpxhd6moQ XFOg==; dara=google.com ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@chromium.org header.s=google header.b=AsKyh1i7; arc=pass (i=1 spf=pass spfdomain=chromium.org dkim=pass dkdomain=chromium.org dmarc=pass fromdomain=chromium.org); spf=pass (google.com: domain of linux-kernel+bounces-69683-ouuuleilei=gmail.com@vger.kernel.org designates 147.75.80.249 as permitted sender) smtp.mailfrom="linux-kernel+bounces-69683-ouuuleilei=gmail.com@vger.kernel.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=chromium.org Received: from am.mirrors.kernel.org (am.mirrors.kernel.org. [147.75.80.249]) by mx.google.com with ESMTPS id pj11-20020a170906d78b00b00a3d4c1f3fd3si528093ejb.521.2024.02.16.20.54.07 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 16 Feb 2024 20:54:08 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel+bounces-69683-ouuuleilei=gmail.com@vger.kernel.org designates 147.75.80.249 as permitted sender) client-ip=147.75.80.249; Authentication-Results: mx.google.com; dkim=pass header.i=@chromium.org header.s=google header.b=AsKyh1i7; arc=pass (i=1 spf=pass spfdomain=chromium.org dkim=pass dkdomain=chromium.org dmarc=pass fromdomain=chromium.org); spf=pass (google.com: domain of linux-kernel+bounces-69683-ouuuleilei=gmail.com@vger.kernel.org designates 147.75.80.249 as permitted sender) smtp.mailfrom="linux-kernel+bounces-69683-ouuuleilei=gmail.com@vger.kernel.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=chromium.org Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by am.mirrors.kernel.org (Postfix) with ESMTPS id 8DF481F21E2C for ; Sat, 17 Feb 2024 04:54:07 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 937761CD1D; Sat, 17 Feb 2024 04:53:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b="AsKyh1i7" Received: from mail-pl1-f176.google.com (mail-pl1-f176.google.com [209.85.214.176]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F25DF1C292 for ; Sat, 17 Feb 2024 04:53:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.176 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1708145619; cv=none; b=EHmGqgwjvLq+waBCGf5vZM7UYDUETGEOtjA6Cb5fey+fbIkbGdmA55vYTL5UMLjKNngiu4tx872EvRQA83wM5uI2c70lA7u82Vd/p+kbBDjLHrL8XMVyVfikzBLS5bvNujtwwZTi/CfwPadh4VttQIfjU+qZtNGWpq4zTy8KCaE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1708145619; c=relaxed/simple; bh=kuoh45Evnx5JyHfck+cgZ7vm+rNwO3DEfwaBM/CzXtM=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=cFXRKU0xBqMfwEhArIe11NLEozt6LRKIaDHx2VLlk2Q5koTwQpblxb4DrwwCSGTTDfxIPll8FPFWUP282AwtJACGcvHbplFJk1dN4xEHNqmH7ZKLKkSOpvOe9/PL6AVtjK2Y545+ZmGFBntRKinUUfTz95TvIl9z05eZThJHR54= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=chromium.org; spf=pass smtp.mailfrom=chromium.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b=AsKyh1i7; arc=none smtp.client-ip=209.85.214.176 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=chromium.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=chromium.org Received: by mail-pl1-f176.google.com with SMTP id d9443c01a7336-1d94b222a3aso26934955ad.2 for ; Fri, 16 Feb 2024 20:53:37 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1708145617; x=1708750417; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=7qdAfjTcONjZuFbVJIFLeD6GXhLkNJkyoK1rJKpx18g=; b=AsKyh1i7gSiAco917JNW/VbpJGqDbRvPapj4xwCbAVEXUgv6Kx5YoLEKMgnpw5bjUZ jV4ziR78LhoSc2RsaRabVjHVK155ztqZz8uFLrAqoIpEnjNN+jwT+ywFBQDmZzCc6MDS wxq9lPFjEGNh8pKxLbSq+SZNrMXh/3aefHB/Y= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1708145617; x=1708750417; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=7qdAfjTcONjZuFbVJIFLeD6GXhLkNJkyoK1rJKpx18g=; b=kF1N8Gzuii0Ppskq66EImmqgNyxWXndfmOCG9LJoLXxz5aA+awWZIQ0rNgkf4BgfnH KLi9tB09u2HdQ9GA301tPBNcxShcUoQ37fzPY8CmvzANlHwW9eun86Mj+lB2Gw+E7Gs8 YGZDMggSGkfWGBZkRd8weiUt/uzBmf7KzwJ7PMoPjY+gRUAJyNYSYpG4O5NJLyDJ7Jbm Vz9J4xhVNNjd9pC+Re8M1IZzs2D5G3Aquq4ef7puYG1z/J3Y02f+XoUcLrz58XPfgwRM 0N2dMyWYS+pc9SyFA9J/NeWqSMJVBuhv8dTUaAVSHgRyeNVGAcGjV0D1og++NYmZB+WI vQ0w== X-Forwarded-Encrypted: i=1; AJvYcCXwydd++XM2hUc8j1Rdz83+0IKU7/tzumrpSiY66iTgBJ+SSpn81zTurpaozb8qpLhJB3kgaAi2QCqs5h3PQFhTmUwqFbI/jNf1apQd X-Gm-Message-State: AOJu0YzsZDZhMpoShsq9CXF1cNhdccFD1013p/6NSG5VMCUm+WnR312F WkzgWzi7dxOs4A2a8JXGWUdIV/BHGVp46ZYuzqTIkIVhGSNBkvRxjfB66KwunQ== X-Received: by 2002:a17:902:d2c2:b0:1db:2d82:e803 with SMTP id n2-20020a170902d2c200b001db2d82e803mr8044720plc.20.1708145617246; Fri, 16 Feb 2024 20:53:37 -0800 (PST) Received: from www.outflux.net ([198.0.35.241]) by smtp.gmail.com with ESMTPSA id w12-20020a170902d3cc00b001db398d13ecsm621742plb.258.2024.02.16.20.53.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 16 Feb 2024 20:53:36 -0800 (PST) From: Kees Cook To: linux-hardening@vger.kernel.org Cc: Kees Cook , Alexander Lobakin , Andy Shevchenko , Cezary Rojewski , Puyou Lu , Mark Brown , Brendan Higgins , David Gow , Nick Desaulniers , linux-kernel@vger.kernel.org, kunit-dev@googlegroups.com Subject: [PATCH v3 2/5] fortify: Allow KUnit test to build without FORTIFY Date: Fri, 16 Feb 2024 20:48:25 -0800 Message-Id: <20240217045335.1526675-2-keescook@chromium.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240217043535.make.664-kees@kernel.org> References: <20240217043535.make.664-kees@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=2099; i=keescook@chromium.org; h=from:subject; bh=kuoh45Evnx5JyHfck+cgZ7vm+rNwO3DEfwaBM/CzXtM=; b=owEBbQKS/ZANAwAKAYly9N/cbcAmAcsmYgBl0DqcqxsshKhN+XVitP/aPp0wIqPepQH/G0HTU LaFc4bmVtyJAjMEAAEKAB0WIQSlw/aPIp3WD3I+bhOJcvTf3G3AJgUCZdA6nAAKCRCJcvTf3G3A JsyxD/9NuT75fNho8Z/IYQI3bIaDR5+SVoHtJn460dJU895HRVECqQDQK0FH10OwLUPmtsWRNLO e3AY4jCBlmVXEUvk312YO4mVGGKOwIrKn4qRGfuwrYIg6di0KWySPoUJlJmKuEeC0crqblr2VCm 9KdKwIM8rpCcWtVDnfpUdTHtEJDp4t8GB8jwnp3gJQbOgexoO1iHwV6eOQy6pJvdEFaPzv+pcqx 5b9SD2CIjBFPyY1unxleXUEbKZC7kOkSzcCyEQvAK7UgB7cH+7EhKyaXlPyVrou72mOJUhDHKv0 bR/kWpMi+iQ2zUIfTpl8jZIQpcxJZ8b3WsTN7mAG5xtpTnVjdzSbfazkWiTpY1AjGy2nUpuOqSD jhc8Tw8shjxmcmN6hgY9NfC5PQXVrg1frUMWiNR9fTlaSRIYSp9hlixNzCmHGFCFYKhEuWxYoXv gPFCLy8MEGfoUv28zY/TcahYUe338xs7yYeK5Ew7IVFHYtiX7psKu+eOT6oz8C6vQOZ/RmRYMVP K6DOuJygGnjnC1XgOib+KqdhTQCJ0KznSI24Z9g/U2hPwJrn8fdDa3LmXFsTE4+riDIDI3RbkmT CNmNkScyxcDWDUVFXft/0/JEVwtdhsW/xoLpBw8DTPLFlqaFywHieC3G4G808zoLZnhnI8lTLHN 6/YCwtjCopS6SAw== X-Developer-Key: i=keescook@chromium.org; a=openpgp; fpr=A5C3F68F229DD60F723E6E138972F4DFDC6DC026 X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1791120531173825055 X-GMAIL-MSGID: 1791120531173825055 In order for CI systems to notice all the skipped tests related to CONFIG_FORTIFY_SOURCE, allow the FORTIFY_SOURCE KUnit tests to build with or without CONFIG_FORTIFY_SOURCE. Signed-off-by: Kees Cook --- Cc: linux-hardening@vger.kernel.org --- lib/Kconfig.debug | 2 +- lib/fortify_kunit.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 975a07f9f1cc..4e2febe3b568 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -2748,7 +2748,7 @@ config STACKINIT_KUNIT_TEST config FORTIFY_KUNIT_TEST tristate "Test fortified str*() and mem*() function internals at runtime" if !KUNIT_ALL_TESTS - depends on KUNIT && FORTIFY_SOURCE + depends on KUNIT default KUNIT_ALL_TESTS help Builds unit tests for checking internals of FORTIFY_SOURCE as used diff --git a/lib/fortify_kunit.c b/lib/fortify_kunit.c index 2e4fedc81621..7a88b5dd3d27 100644 --- a/lib/fortify_kunit.c +++ b/lib/fortify_kunit.c @@ -22,6 +22,11 @@ #include #include +/* Handle being built without CONFIG_FORTIFY_SOURCE */ +#ifndef __compiletime_strlen +# define __compiletime_strlen __builtin_strlen +#endif + static const char array_of_10[] = "this is 10"; static const char *ptr_of_11 = "this is 11!"; static char array_unknown[] = "compiler thinks I might change"; @@ -308,6 +313,14 @@ DEFINE_ALLOC_SIZE_TEST_PAIR(kvmalloc) } while (0) DEFINE_ALLOC_SIZE_TEST_PAIR(devm_kmalloc) +static int fortify_test_init(struct kunit *test) +{ + if (!IS_ENABLED(CONFIG_FORTIFY_SOURCE)) + kunit_skip(test, "Not built with CONFIG_FORTIFY_SOURCE=y"); + + return 0; +} + static struct kunit_case fortify_test_cases[] = { KUNIT_CASE(known_sizes_test), KUNIT_CASE(control_flow_split_test), @@ -324,6 +337,7 @@ static struct kunit_case fortify_test_cases[] = { static struct kunit_suite fortify_test_suite = { .name = "fortify", + .init = fortify_test_init, .test_cases = fortify_test_cases, }; From patchwork Sat Feb 17 04:48:26 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kees Cook X-Patchwork-Id: 202571 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a05:693c:2685:b0:108:e6aa:91d0 with SMTP id mn5csp149949dyc; Fri, 16 Feb 2024 20:55:19 -0800 (PST) X-Forwarded-Encrypted: i=3; AJvYcCVwDtXAEyWABWL/CN5imW/OI2EGi6ud/ZPCEAWmCiA/q3cSxg6EkDqSjWxIXzhLNLISN6LtgB7p1U5eFVqw7OxQo6vlYw== X-Google-Smtp-Source: AGHT+IF27z/R4tDzFEWUFlWfb3GGOMvMTRDzp1pfHNH9wf0QSIDfm7+7xCfVa6svnKCkloLjT1s2 X-Received: by 2002:a05:6808:309d:b0:3c1:395b:9bf6 with SMTP id bl29-20020a056808309d00b003c1395b9bf6mr7695167oib.41.1708145718866; Fri, 16 Feb 2024 20:55:18 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1708145718; cv=pass; d=google.com; s=arc-20160816; b=tDJTwpj2GRCaY5Nuej6G5VIxOL6kHMxYnHs5oP7BA2tOwneTdVb7uTp4QH0Zs05Mzd HCZ5zdcIdiS2ckgbuqU2fxBZ1Xkil1PMp33VsEJv7zhZ9w6YEnrF0URSpQ5n4Cb/nCeb ni8gyKgKft9RIBNKqAFY8Z/Tiwll8qjUN86hNDbkRBePyYEFu4DVOfTpCllNiJveaNmn n5pWexW8x24fhmUyNVTnyXlOchVq2FU+nDxGI0+FQ8pTd8bzNajDLE6qossvbNaUn0YH YPtaLc5WDEB3YGiFofyGQ8k8P2p2BUTHi+T+ChoLDPy3PfR3Yz78xbwbmcZ1xDNDpmg3 mVTA== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:list-unsubscribe :list-subscribe:list-id:precedence:references:in-reply-to:message-id :date:subject:cc:to:from:dkim-signature; bh=CqD9QSOaWitNrV/YtqVACNtULpzHLxmWAN5VYT7BaOg=; fh=YpaZERDDBe8599T+V6v5enguiR0gw8slyCXL32VUW7w=; b=kt1aUDNbh39Fkl9D9c/DPSunwWcY+Hg9VPtQUFMRwtq8hmSjaSredEz/lK1WAkZ9sK 0QDeE3oXmzTrTGFwaCRAqFBgwHv90MBCZsTcTDGEmEg3WHjzEKXF1gV3VY6f3B1BsN0r za5TgSO0p8F0cJud6uIhDigUq/JBvV9QBA8Ipv7L4CBdWTm+KehaPW9GhzAcjlzwWTq4 arQk5vS7GmbxvplS5qMqWB2hUaN4R5MyPVvoBWlM+WGAukVwj/uqvk6iZtbuyFkGhUXq /1H2CClTJUkeMcTEhGkFtbU45l9drnZ/DGlRtEWH01CS0GNcMAyM2m5Ahtg8JQw4xLts QmRg==; dara=google.com ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@chromium.org header.s=google header.b=FG2W5ld3; arc=pass (i=1 spf=pass spfdomain=chromium.org dkim=pass dkdomain=chromium.org dmarc=pass fromdomain=chromium.org); spf=pass (google.com: domain of linux-kernel+bounces-69685-ouuuleilei=gmail.com@vger.kernel.org designates 147.75.48.161 as permitted sender) smtp.mailfrom="linux-kernel+bounces-69685-ouuuleilei=gmail.com@vger.kernel.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=chromium.org Received: from sy.mirrors.kernel.org (sy.mirrors.kernel.org. [147.75.48.161]) by mx.google.com with ESMTPS id e38-20020a635026000000b005dc90546100si944424pgb.295.2024.02.16.20.55.18 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 16 Feb 2024 20:55:18 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel+bounces-69685-ouuuleilei=gmail.com@vger.kernel.org designates 147.75.48.161 as permitted sender) client-ip=147.75.48.161; Authentication-Results: mx.google.com; dkim=pass header.i=@chromium.org header.s=google header.b=FG2W5ld3; arc=pass (i=1 spf=pass spfdomain=chromium.org dkim=pass dkdomain=chromium.org dmarc=pass fromdomain=chromium.org); spf=pass (google.com: domain of linux-kernel+bounces-69685-ouuuleilei=gmail.com@vger.kernel.org designates 147.75.48.161 as permitted sender) smtp.mailfrom="linux-kernel+bounces-69685-ouuuleilei=gmail.com@vger.kernel.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=chromium.org Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by sy.mirrors.kernel.org (Postfix) with ESMTPS id 0A7C0B22414 for ; Sat, 17 Feb 2024 04:54:42 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 134411D54B; Sat, 17 Feb 2024 04:53:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b="FG2W5ld3" Received: from mail-oi1-f172.google.com (mail-oi1-f172.google.com [209.85.167.172]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6E9371C2B3 for ; Sat, 17 Feb 2024 04:53:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.167.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1708145621; cv=none; b=CZTnCmw3DbspQ6me7EZ7r2MbAiUoceQoWCXjkGdc4YQsF2iItkavCYWCQqTa5FPdzEsQQaDG9ZJtyxl4rGpLQqs3SStTfR5DWf2P++nc3s36zkfnTErHLaBBSsUezeD8F3rs4MboHminM5I1cf8MCK7lJ/mmmennwq+e3CkMy1Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1708145621; c=relaxed/simple; bh=hokFyy8qPsOZQK0QxxkCCaEHdxSLYeLL82uvMMA2NB4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=HwYWdhk6ugE9zuHWeHOKIXSLgMowmaSbyyc+OZ9Ifm0apIOAGfWANuiKSliQF/KQIIDs4ZFoWnDKs6g3CpqlxYUysqoEOntnkjXejymL6D0KocDDamaQuzsQA5U5N51diiTfiZbPJ++oGtHu2ztc58jvAyESa5S9rj2zqoGQECI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=chromium.org; spf=pass smtp.mailfrom=chromium.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b=FG2W5ld3; arc=none smtp.client-ip=209.85.167.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=chromium.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=chromium.org Received: by mail-oi1-f172.google.com with SMTP id 5614622812f47-3c02adddb8eso2269836b6e.0 for ; Fri, 16 Feb 2024 20:53:39 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1708145618; x=1708750418; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=CqD9QSOaWitNrV/YtqVACNtULpzHLxmWAN5VYT7BaOg=; b=FG2W5ld3TvsqpUlq58rOIyh9ekRMIEiwkb6iMDnzB1P8lFFbHFdRONpYFyVYldgYm4 MQ7M+cLoD8vtmNFgoIMgnQ0kxUf7atzJgKO0PVV9KMPWfJcpqi0oR7tzrbjWrRW8wk4g cHdilNq5nMKevdHOBkPkL0JsttyMxaGJc13B0= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1708145618; x=1708750418; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=CqD9QSOaWitNrV/YtqVACNtULpzHLxmWAN5VYT7BaOg=; b=MHgZOQBAEQN+SjFmctddaJS1zWMPpG8gF/bmm18oeieJUxkyqz4ZRN3RcM24yglWB8 uP8q/qMrQOuVTqnNBPuQA8caDVDlmXkVpuPn8CVE66b+PhFGweWC5w8O5LSU+0ABwode gfQxi0sbOXh2TUpNAyEm6ZBr9IaBUtF7JqA0HaD2kSStBvb3rizR34Irp9KoK7qE85Mx ze30l5Y1xDxEoEvg1bUW/BsMoP/WBoVLdaJ83N1ony9W9QEDtVFenG3new+F3Gbgfj7B nlEJOCwIQu8j3JYmGom7NSBDQBbTABMR+HAgl3uEgaaCohrrhCSuAUnq+B6aH6XiLPxZ LKCQ== X-Forwarded-Encrypted: i=1; AJvYcCW/023SHswW4mvBKowmf6rVSydt6Yi2JdcG/69tPWs8f86SFdwaQzPMt2hGdQ97EdG+HfvFdb/4n982ILU+wG6Q79ey1OnexYAPtvrK X-Gm-Message-State: AOJu0YzKpxYnzOIibjmoykBAqSpupM2DvWF1S+rHygQrI2Ts4CEju9CJ +aAZFCrIZtIrYByUSeqajX+56SkMFJHiLb25Cd/TJcrz1/pBqTUulONfTbB75A== X-Received: by 2002:a05:6808:2cd:b0:3bf:d1ea:23bc with SMTP id a13-20020a05680802cd00b003bfd1ea23bcmr7007093oid.49.1708145618428; Fri, 16 Feb 2024 20:53:38 -0800 (PST) Received: from www.outflux.net ([198.0.35.241]) by smtp.gmail.com with ESMTPSA id fh14-20020a056a00390e00b006e08c103dddsm756206pfb.192.2024.02.16.20.53.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 16 Feb 2024 20:53:36 -0800 (PST) From: Kees Cook To: linux-hardening@vger.kernel.org Cc: Kees Cook , Andy Shevchenko , Brendan Higgins , David Gow , kunit-dev@googlegroups.com, Alexander Lobakin , Cezary Rojewski , Puyou Lu , Mark Brown , Brendan Higgins , Nick Desaulniers , linux-kernel@vger.kernel.org Subject: [PATCH v3 3/5] fortify: Provide KUnit counters for failure testing Date: Fri, 16 Feb 2024 20:48:26 -0800 Message-Id: <20240217045335.1526675-3-keescook@chromium.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240217043535.make.664-kees@kernel.org> References: <20240217043535.make.664-kees@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=10228; i=keescook@chromium.org; h=from:subject; bh=hokFyy8qPsOZQK0QxxkCCaEHdxSLYeLL82uvMMA2NB4=; b=owEBbQKS/ZANAwAKAYly9N/cbcAmAcsmYgBl0DqcQaQiJnoleWY/J6HeSpUirntlCXzKCTaUg YFFbsxYzRSJAjMEAAEKAB0WIQSlw/aPIp3WD3I+bhOJcvTf3G3AJgUCZdA6nAAKCRCJcvTf3G3A JkaGD/0XK03QS4Ju4T33E26ZgsIiEgVjdZlDIYTxV55sr1wcFy8otOKbTu+K0sKJZ0WxnOOAKdQ 7Nl1pZacWsADJZ98A8gp5O8qDT42vvkXwGmLNsJe+1p4FNyBLlDU2iYuo7o9RNu7QhZRzNGnFNG c+UBduxPwil71P59eigc5JsxW10g2JWCvcAIMgcVwxcr3FPEUagdyvIKFUte02xVwsHNH5bVOAI FfmldAasLq/m0yVqOP1XF7z/N5sd18qU8UFenMKrFqpNetQammLDnV5h5ZogJwCTTDMTRzSXOaX 7ZJrCB0t5r7aeCN8hOV1FGH2m/YCXtizW0TDRNzn0OYE8fXH/fwk5TK62Uc0R7XmXXXAUZUilYb oxMkNRce+xVZbEQxtSUjJJJT3OOaKbm7snNn1pw8tzc5hhudDPOsWl0gg74/Kp+aoGOUJ3R5otj m7yLJdHmvky6Uy3+rglN3Wd6OcesRDY/eHHjUngIUtWEjKSp4+6vmwTKJ58g7+zIaH4R8rIm45w X0P7UtrpFh0SJO+xERUA+XYVWYWT98TAVzPEJsIiY9RWfMhEmLBJ3QMNeapZGuPsGoRf/i07SAZ xCd1Gnqvwl9OFwPfCIGPvBffCIWiNcs32s70lFLF/Ym7WGDK7lO95lWPQu+vwiQMROYM9joFueK +f7LydPrDdge8Aw== X-Developer-Key: i=keescook@chromium.org; a=openpgp; fpr=A5C3F68F229DD60F723E6E138972F4DFDC6DC026 X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1791120605672298261 X-GMAIL-MSGID: 1791120605672298261 The standard C string APIs were not designed to have a failure mode; they were expected to always succeed without memory safety issues. Normally, CONFIG_FORTIFY_SOURCE will use fortify_panic() to stop processing, as truncating a read or write may provide an even worse system state. However, this creates a problem for testing under things like KUnit, which needs a way to survive failures. When building with CONFIG_KUNIT, provide a failure path for all users of fortify_panic, and track whether the failure was a read overflow or a write overflow, for KUnit tests to examine. Inspired by similar logic in the slab tests. Signed-off-by: Kees Cook --- Cc: linux-hardening@vger.kernel.org Cc: Andy Shevchenko Cc: Brendan Higgins Cc: David Gow Cc: kunit-dev@googlegroups.com --- include/linux/fortify-string.h | 43 ++++++++++++++++++---------------- lib/fortify_kunit.c | 41 ++++++++++++++++++++++++++++++++ lib/string_helpers.c | 2 ++ 3 files changed, 66 insertions(+), 20 deletions(-) diff --git a/include/linux/fortify-string.h b/include/linux/fortify-string.h index 4f6767dcd933..fbfb90479b8f 100644 --- a/include/linux/fortify-string.h +++ b/include/linux/fortify-string.h @@ -15,8 +15,10 @@ #define FORTIFY_REASON(func, write) (FIELD_PREP(BIT(0), write) | \ FIELD_PREP(GENMASK(7, 1), func)) -#define fortify_panic(func, write) \ - __fortify_panic(FORTIFY_REASON(func, write)) +#ifndef fortify_panic +# define fortify_panic(func, write, retfail) \ + __fortify_panic(FORTIFY_REASON(func, write)) +#endif #define FORTIFY_READ 0 #define FORTIFY_WRITE 1 @@ -181,7 +183,7 @@ char *strncpy(char * const POS p, const char *q, __kernel_size_t size) if (__compiletime_lessthan(p_size, size)) __write_overflow(); if (p_size < size) - fortify_panic(FORTIFY_FUNC_strncpy, FORTIFY_WRITE); + fortify_panic(FORTIFY_FUNC_strncpy, FORTIFY_WRITE, p); return __underlying_strncpy(p, q, size); } @@ -212,7 +214,7 @@ __FORTIFY_INLINE __kernel_size_t strnlen(const char * const POS p, __kernel_size /* Do not check characters beyond the end of p. */ ret = __real_strnlen(p, maxlen < p_size ? maxlen : p_size); if (p_size <= ret && maxlen != ret) - fortify_panic(FORTIFY_FUNC_strnlen, FORTIFY_READ); + fortify_panic(FORTIFY_FUNC_strnlen, FORTIFY_READ, ret); return ret; } @@ -248,7 +250,7 @@ __kernel_size_t __fortify_strlen(const char * const POS p) return __underlying_strlen(p); ret = strnlen(p, p_size); if (p_size <= ret) - fortify_panic(FORTIFY_FUNC_strlen, FORTIFY_READ); + fortify_panic(FORTIFY_FUNC_strlen, FORTIFY_READ, ret); return ret; } @@ -299,7 +301,7 @@ __FORTIFY_INLINE ssize_t sized_strscpy(char * const POS p, const char * const PO * p_size. */ if (len > p_size) - fortify_panic(FORTIFY_FUNC_strscpy, FORTIFY_WRITE); + fortify_panic(FORTIFY_FUNC_strscpy, FORTIFY_WRITE, -E2BIG); /* * We can now safely call vanilla strscpy because we are protected from: @@ -357,7 +359,7 @@ size_t strlcat(char * const POS p, const char * const POS q, size_t avail) /* Give up if string is already overflowed. */ if (p_size <= p_len) - fortify_panic(FORTIFY_FUNC_strlcat, FORTIFY_READ); + fortify_panic(FORTIFY_FUNC_strlcat, FORTIFY_READ, wanted); if (actual >= avail) { copy_len = avail - p_len - 1; @@ -366,7 +368,7 @@ size_t strlcat(char * const POS p, const char * const POS q, size_t avail) /* Give up if copy will overflow. */ if (p_size <= actual) - fortify_panic(FORTIFY_FUNC_strlcat, FORTIFY_WRITE); + fortify_panic(FORTIFY_FUNC_strlcat, FORTIFY_WRITE, wanted); __underlying_memcpy(p + p_len, q, copy_len); p[actual] = '\0'; @@ -395,7 +397,7 @@ char *strcat(char * const POS p, const char *q) const size_t p_size = __member_size(p); if (strlcat(p, q, p_size) >= p_size) - fortify_panic(FORTIFY_FUNC_strcat, FORTIFY_WRITE); + fortify_panic(FORTIFY_FUNC_strcat, FORTIFY_WRITE, p); return p; } @@ -431,13 +433,13 @@ char *strncat(char * const POS p, const char * const POS q, __kernel_size_t coun p_len = strlen(p); copy_len = strnlen(q, count); if (p_size < p_len + copy_len + 1) - fortify_panic(FORTIFY_FUNC_strncat, FORTIFY_WRITE); + fortify_panic(FORTIFY_FUNC_strncat, FORTIFY_WRITE, p); __underlying_memcpy(p + p_len, q, copy_len); p[p_len + copy_len] = '\0'; return p; } -__FORTIFY_INLINE void fortify_memset_chk(__kernel_size_t size, +__FORTIFY_INLINE bool fortify_memset_chk(__kernel_size_t size, const size_t p_size, const size_t p_size_field) { @@ -472,7 +474,8 @@ __FORTIFY_INLINE void fortify_memset_chk(__kernel_size_t size, * lengths are unknown.) */ if (p_size != SIZE_MAX && p_size < size) - fortify_panic(FORTIFY_FUNC_memset, FORTIFY_WRITE); + fortify_panic(FORTIFY_FUNC_memset, FORTIFY_WRITE, true); + return false; } #define __fortify_memset_chk(p, c, size, p_size, p_size_field) ({ \ @@ -571,9 +574,9 @@ __FORTIFY_INLINE bool fortify_memcpy_chk(__kernel_size_t size, * lengths are unknown.) */ if (p_size != SIZE_MAX && p_size < size) - fortify_panic(func, FORTIFY_WRITE); + fortify_panic(func, FORTIFY_WRITE, true); else if (q_size != SIZE_MAX && q_size < size) - fortify_panic(func, FORTIFY_READ); + fortify_panic(func, FORTIFY_READ, true); /* * Warn when writing beyond destination field size. @@ -673,7 +676,7 @@ __FORTIFY_INLINE void *memscan(void * const POS0 p, int c, __kernel_size_t size) if (__compiletime_lessthan(p_size, size)) __read_overflow(); if (p_size < size) - fortify_panic(FORTIFY_FUNC_memscan, FORTIFY_READ); + fortify_panic(FORTIFY_FUNC_memscan, FORTIFY_READ, NULL); return __real_memscan(p, c, size); } @@ -690,7 +693,7 @@ int memcmp(const void * const POS0 p, const void * const POS0 q, __kernel_size_t __read_overflow2(); } if (p_size < size || q_size < size) - fortify_panic(FORTIFY_FUNC_memcmp, FORTIFY_READ); + fortify_panic(FORTIFY_FUNC_memcmp, FORTIFY_READ, INT_MIN); return __underlying_memcmp(p, q, size); } @@ -702,7 +705,7 @@ void *memchr(const void * const POS0 p, int c, __kernel_size_t size) if (__compiletime_lessthan(p_size, size)) __read_overflow(); if (p_size < size) - fortify_panic(FORTIFY_FUNC_memchr, FORTIFY_READ); + fortify_panic(FORTIFY_FUNC_memchr, FORTIFY_READ, NULL); return __underlying_memchr(p, c, size); } @@ -714,7 +717,7 @@ __FORTIFY_INLINE void *memchr_inv(const void * const POS0 p, int c, size_t size) if (__compiletime_lessthan(p_size, size)) __read_overflow(); if (p_size < size) - fortify_panic(FORTIFY_FUNC_memchr_inv, FORTIFY_READ); + fortify_panic(FORTIFY_FUNC_memchr_inv, FORTIFY_READ, NULL); return __real_memchr_inv(p, c, size); } @@ -727,7 +730,7 @@ __FORTIFY_INLINE void *kmemdup(const void * const POS0 p, size_t size, gfp_t gfp if (__compiletime_lessthan(p_size, size)) __read_overflow(); if (p_size < size) - fortify_panic(FORTIFY_FUNC_kmemdup, FORTIFY_READ); + fortify_panic(FORTIFY_FUNC_kmemdup, FORTIFY_READ, NULL); return __real_kmemdup(p, size, gfp); } @@ -764,7 +767,7 @@ char *strcpy(char * const POS p, const char * const POS q) __write_overflow(); /* Run-time check for dynamic size overflow. */ if (p_size < size) - fortify_panic(FORTIFY_FUNC_strcpy, FORTIFY_WRITE); + fortify_panic(FORTIFY_FUNC_strcpy, FORTIFY_WRITE, p); __underlying_memcpy(p, q, size); return p; } diff --git a/lib/fortify_kunit.c b/lib/fortify_kunit.c index 7a88b5dd3d27..4ba7d02fdd78 100644 --- a/lib/fortify_kunit.c +++ b/lib/fortify_kunit.c @@ -15,8 +15,17 @@ */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +/* Redefine fortify_panic() to track failures. */ +void fortify_add_kunit_error(int write); +#define fortify_panic(func, write, retfail) do { \ + __fortify_report(FORTIFY_REASON(func, write)); \ + fortify_add_kunit_error(write); \ + return (retfail); \ +} while (0) + #include #include +#include #include #include #include @@ -27,10 +36,34 @@ # define __compiletime_strlen __builtin_strlen #endif +static struct kunit_resource read_resource; +static struct kunit_resource write_resource; +static int fortify_read_overflows; +static int fortify_write_overflows; + static const char array_of_10[] = "this is 10"; static const char *ptr_of_11 = "this is 11!"; static char array_unknown[] = "compiler thinks I might change"; +void fortify_add_kunit_error(int write) +{ + struct kunit_resource *resource; + struct kunit *current_test; + + current_test = kunit_get_current_test(); + if (!current_test) + return; + + resource = kunit_find_named_resource(current_test, + write ? "fortify_write_overflows" + : "fortify_read_overflows"); + if (!resource) + return; + + (*(int *)resource->data)++; + kunit_put_resource(resource); +} + static void known_sizes_test(struct kunit *test) { KUNIT_EXPECT_EQ(test, __compiletime_strlen("88888888"), 8); @@ -318,6 +351,14 @@ static int fortify_test_init(struct kunit *test) if (!IS_ENABLED(CONFIG_FORTIFY_SOURCE)) kunit_skip(test, "Not built with CONFIG_FORTIFY_SOURCE=y"); + fortify_read_overflows = 0; + kunit_add_named_resource(test, NULL, NULL, &read_resource, + "fortify_read_overflows", + &fortify_read_overflows); + fortify_write_overflows = 0; + kunit_add_named_resource(test, NULL, NULL, &write_resource, + "fortify_write_overflows", + &fortify_write_overflows); return 0; } diff --git a/lib/string_helpers.c b/lib/string_helpers.c index 9291dc74ae01..5e53d42e32bb 100644 --- a/lib/string_helpers.c +++ b/lib/string_helpers.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include /** * string_get_size - get the size in the specified units From patchwork Sat Feb 17 04:48:27 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kees Cook X-Patchwork-Id: 202570 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a05:693c:2685:b0:108:e6aa:91d0 with SMTP id mn5csp149891dyc; Fri, 16 Feb 2024 20:55:05 -0800 (PST) X-Forwarded-Encrypted: i=3; AJvYcCW+WGtC/jxdvZvJq6z3Pz+TUAfOaxox2Hg+96YptKbDV5T7qDXcLAZgL2jGKBVqAGCR/IGMmMHC1KuAdRmMB6+Ys485NA== X-Google-Smtp-Source: AGHT+IEyZQK2KqAOgrX14LMwkSQZgtzeo+um9IlF1eVzusUUBDIir4ezHpfRNB00rY4nXPiYVqFT X-Received: by 2002:aa7:ca56:0:b0:564:396:52e2 with SMTP id j22-20020aa7ca56000000b00564039652e2mr1825528edt.38.1708145705106; Fri, 16 Feb 2024 20:55:05 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1708145705; cv=pass; d=google.com; s=arc-20160816; b=QIXS0hupb1GMwuKHAQR7zo34Cz6y5v9SDS9x0IaUCuKCn0CXEQ3Tq/rJF3r8kro3fv fBOi3IrhG0IZgzEe8wGN01L168dlV2cG/luTYRJCT3lTkeq5Iv1caudlzvF68kryAnuw T837nwbk6GMKjzHTMlxJ8qyCPH+hJDEanddqScczAdxUgEOLT1tfc5/u2VEkurMUDyFp ABIEKfts+monGZ5vkoQUB2UCEm9GLOhzYkpmobiZAr6HTEcumoxodQZHWhfYj2eiFabc KXR5TFqVrdWcamqrj8v6geNb4SXSW/ulg/mQqh+jKAlIEM3rUkcdi0Vuc3vwd4MSA6Gg fK4w== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:list-unsubscribe :list-subscribe:list-id:precedence:references:in-reply-to:message-id :date:subject:cc:to:from:dkim-signature; bh=c8fMfE+BGQPs5FLgU3mkDOSE9IZHFwdxz/Ne820XXmM=; fh=ci3MlFIg5wwJGAS7dNuQfb5EY1Hib+P4OgNlNgYGtb4=; b=U8MygGV2Wdtx47DOTFYbR0opSJ68c31yfahJjJfshkA4GfeJIhza0Xua2q1XqTjyOw bNcEdhPjeiMrdPObcBEWHMmkSHf/7ZLs8VWzkSwVhhMcrtJlxNBfwbd02uXEnFWbUvRu LwGRMAjpYRItEHl2UbjoddDFrPrc1/XHLDy6iv01tI6CNU83JLNQvp/TKY8XRHJbdMFq VXGHyPISD5zS6YgOxt/DhhQQXq4SufbNzhDswl0AHo6OY5Y0IsENhgc4P0hcBnkzcYRG lCr40pUx/Wrvw9pvHUPTvMIMau8PPnWgmNwhxkVK5NJLziGlyVx8/6vZubroLWtcMZos DINQ==; dara=google.com ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@chromium.org header.s=google header.b="I/grpy16"; arc=pass (i=1 spf=pass spfdomain=chromium.org dkim=pass dkdomain=chromium.org dmarc=pass fromdomain=chromium.org); spf=pass (google.com: domain of linux-kernel+bounces-69687-ouuuleilei=gmail.com@vger.kernel.org designates 147.75.80.249 as permitted sender) smtp.mailfrom="linux-kernel+bounces-69687-ouuuleilei=gmail.com@vger.kernel.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=chromium.org Received: from am.mirrors.kernel.org (am.mirrors.kernel.org. [147.75.80.249]) by mx.google.com with ESMTPS id g8-20020aa7d1c8000000b00563ef6e3994si573112edp.5.2024.02.16.20.55.04 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 16 Feb 2024 20:55:05 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel+bounces-69687-ouuuleilei=gmail.com@vger.kernel.org designates 147.75.80.249 as permitted sender) client-ip=147.75.80.249; Authentication-Results: mx.google.com; dkim=pass header.i=@chromium.org header.s=google header.b="I/grpy16"; arc=pass (i=1 spf=pass spfdomain=chromium.org dkim=pass dkdomain=chromium.org dmarc=pass fromdomain=chromium.org); spf=pass (google.com: domain of linux-kernel+bounces-69687-ouuuleilei=gmail.com@vger.kernel.org designates 147.75.80.249 as permitted sender) smtp.mailfrom="linux-kernel+bounces-69687-ouuuleilei=gmail.com@vger.kernel.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=chromium.org Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by am.mirrors.kernel.org (Postfix) with ESMTPS id 601FD1F22394 for ; Sat, 17 Feb 2024 04:55:04 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id D77511DA24; Sat, 17 Feb 2024 04:53:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b="I/grpy16" Received: from mail-io1-f47.google.com (mail-io1-f47.google.com [209.85.166.47]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id ECD2E1CA9B for ; Sat, 17 Feb 2024 04:53:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.166.47 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1708145623; cv=none; b=U9wiBRdFSPzNVDGu/NPROkh7XU9PmHL7iC0XYuEXL+Q/zkq4gj1voP39kiy/ZW63isvCPhlHNG+PuH8biUW6X31iEZTC9J5pDgV/WfapPM6DRIHEE/DQ3zjNQUvwY3aZWn09Sg6MpWfie4CO+vrgwuRORQ0zQqFhSeYPUkum78w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1708145623; c=relaxed/simple; bh=n2yHX3VFuSqd5a58ECahq9aQe9ZQtPrH7dJ9V/EutHI=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=o2fuQn533M2IuzLJf5iEm9c58FeEA9Rp7UfiIgudJT8/QJDDHEgleKp7D3+HuY/au6ld610+VwMuOSjEKx3cfJKFOdVMRl1YlIF0wen3FqQSSuTsKr+3Q7uVg61pXLS5PD0ooOyo/NUFkKfsxCcsY5ktOQx1Cl57/c7oDf+ioRQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=chromium.org; spf=pass smtp.mailfrom=chromium.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b=I/grpy16; arc=none smtp.client-ip=209.85.166.47 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=chromium.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=chromium.org Received: by mail-io1-f47.google.com with SMTP id ca18e2360f4ac-7c457b8ef7cso94629639f.2 for ; Fri, 16 Feb 2024 20:53:40 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1708145620; x=1708750420; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=c8fMfE+BGQPs5FLgU3mkDOSE9IZHFwdxz/Ne820XXmM=; b=I/grpy16LJZQtu1siFzazt3x4uF8lvSJt6o8SZa+Ydkv0r/vOeSelmro1Y2kUmOqN2 s7HJFlahEvLhcJDKU/XGWCGSNlT7HbBbAzTQZ6g8FBf17Ew6axR0jxgnM0cW/49biEhq dbTvgxCcEZM/XFC9S4qCJE+1bMBcv9cqtueqM= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1708145620; x=1708750420; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=c8fMfE+BGQPs5FLgU3mkDOSE9IZHFwdxz/Ne820XXmM=; b=aIYJ93txuRBsl6CycFzM76ZKkeleNRfvWhCMLhfnvG0VnDMxVgOu2uBhKO1EhaIggZ C3pi48HSxeEFKqJXMSmXORTXa2+y9VaGDLQbnPzjUSJaEQ6QElTPXAshKsMmAZg4/OkM SqI90yHs7ok6XC9jQIcwV9l1sjT0FWXV+V35TSRdYPoAZT2khZqglSeTOJOo8H+H1L+2 xvPtsYylT4faGwFT/AgnEX7omy1yNwViR38jRRHWGss3wPRb0FG4IugB8WPiShIFKr9b vzJiV7CWVR0HX0ege0I0E8CF2WJ8ICXO+A3ooetWsy6jk/xk/XS0ayjkN/dcoPjoxdDw //9w== X-Forwarded-Encrypted: i=1; AJvYcCU4dsfI21N1ARqpqMAgoToUmDmOfMMxxW6oZgbHUW+SbXGie2+rAfgz0pgpDHN7lA3Km+WRCfJx4r1wPBrr9ULjtJCtkBtrLzfx1tqE X-Gm-Message-State: AOJu0Yy6Snm6h4r0bZR6VBU5MiFQrGVVT8RpdTOZHHgm8Ugbmq3uq1yz veySQpXU998AAItI4QwkN7URvZ4CPD7FhvexTlMD22lTnSrorHjmPm4cP5N/cw== X-Received: by 2002:a05:6e02:690:b0:365:12e:4ef1 with SMTP id o16-20020a056e02069000b00365012e4ef1mr5858702ils.5.1708145620002; Fri, 16 Feb 2024 20:53:40 -0800 (PST) Received: from www.outflux.net ([198.0.35.241]) by smtp.gmail.com with ESMTPSA id p18-20020a170902c71200b001d9af77893esm634181plp.58.2024.02.16.20.53.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 16 Feb 2024 20:53:36 -0800 (PST) From: Kees Cook To: linux-hardening@vger.kernel.org Cc: Kees Cook , Brendan Higgins , David Gow , kunit-dev@googlegroups.com, Alexander Lobakin , Andy Shevchenko , Cezary Rojewski , Puyou Lu , Mark Brown , Nick Desaulniers , linux-kernel@vger.kernel.org Subject: [PATCH v3 4/5] fortify: Add KUnit tests for runtime overflows Date: Fri, 16 Feb 2024 20:48:27 -0800 Message-Id: <20240217045335.1526675-4-keescook@chromium.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240217043535.make.664-kees@kernel.org> References: <20240217043535.make.664-kees@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=26692; i=keescook@chromium.org; h=from:subject; bh=n2yHX3VFuSqd5a58ECahq9aQe9ZQtPrH7dJ9V/EutHI=; b=owEBbQKS/ZANAwAKAYly9N/cbcAmAcsmYgBl0DqcQlF1eBHKyO8mBCYFrGEs5iP7hA+zlY5eC 0qQa/Z0BOSJAjMEAAEKAB0WIQSlw/aPIp3WD3I+bhOJcvTf3G3AJgUCZdA6nAAKCRCJcvTf3G3A Jq24EACDywTBm/wdQrUjt5zHqxfG7oFi0zRC+57NC3oiddVIzY0QKZlwooIHe/ZuHoTvSdPHBP1 UxU2L8jB33B8EXf8ODljClojC1/7hGyXFIfE0WpHH4o51rHPwuxeVGTtIl79UShxNZysKG6b/Ep Y7C3/ZsjFLx3FbIiSUmwoYsUGBw7Vgf/qsT2OYqc0N2B7kB72kzlknqbgdG4xvoZIBJ4/VrVDc6 rLB0H4NekcQpxxwwVvDFy8+R9z7bOIUXwIavNwAYctDeq9DjeBgM6RrbkJvN2uDhTKC9i65aqUy y4JKHhfZKgJZpV3UW4B3mW0e+YScDLleamYibB7uUpwfZvThRKdFJtj6SDMkai8PrknlTvPbfM0 /NafXU6hBLmrvklJH0j50NMPkcHt+uNhKG8iwLRCZOTU244nCiX/8FqsnPTM+wsJXOD/yB7FpyF GVlKid3BYZsL83S+AlHpfKnTUjUdwccMG+QQBLmtN+qkj//nL3Bd6GGbuML8UPFCSt43nyFR+Sw +fq6yINitr2KUEXm5WoJFVnUv5+9vMH4eDqdP355rNaq7eY12WcOVGvM4GN/jeDCiTfE7B1celV jzNjHTd4NbrdSwiRxhdcak2gDJNL+wPwF+rZrzgDkeaRDlcoCQouxm8vgqy/LJQ6SUexceNjWB5 kRfZl0O4ZUxTckA== X-Developer-Key: i=keescook@chromium.org; a=openpgp; fpr=A5C3F68F229DD60F723E6E138972F4DFDC6DC026 X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1791120534135609821 X-GMAIL-MSGID: 1791120591069962808 With fortify overflows able to be redirected, we can use KUnit to exercise the overflow conditions. Add tests for every API covered by CONFIG_FORTIFY_SOURCE, except for memset() and memcpy(), which are special-cased for now. Note that this makes the LKDTM FORTIFY_STR* tests obsolete, but those can be removed separately. Signed-off-by: Kees Cook --- Cc: linux-hardening@vger.kernel.org Cc: Brendan Higgins Cc: David Gow Cc: kunit-dev@googlegroups.com --- lib/Makefile | 1 + lib/fortify_kunit.c | 607 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 606 insertions(+), 2 deletions(-) diff --git a/lib/Makefile b/lib/Makefile index bc36a5c167db..9d0e2af8100a 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -402,6 +402,7 @@ obj-$(CONFIG_OVERFLOW_KUNIT_TEST) += overflow_kunit.o CFLAGS_stackinit_kunit.o += $(call cc-disable-warning, switch-unreachable) obj-$(CONFIG_STACKINIT_KUNIT_TEST) += stackinit_kunit.o CFLAGS_fortify_kunit.o += $(call cc-disable-warning, unsequenced) +CFLAGS_fortify_kunit.o += $(call cc-disable-warning, stringop-overread) CFLAGS_fortify_kunit.o += $(DISABLE_STRUCTLEAK_PLUGIN) obj-$(CONFIG_FORTIFY_KUNIT_TEST) += fortify_kunit.o obj-$(CONFIG_STRCAT_KUNIT_TEST) += strcat_kunit.o diff --git a/lib/fortify_kunit.c b/lib/fortify_kunit.c index 4ba7d02fdd78..f0accebeca02 100644 --- a/lib/fortify_kunit.c +++ b/lib/fortify_kunit.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Runtime test cases for CONFIG_FORTIFY_SOURCE that aren't expected to - * Oops the kernel on success. (For those, see drivers/misc/lkdtm/fortify.c) + * Runtime test cases for CONFIG_FORTIFY_SOURCE. For testing memcpy(), + * see FORTIFY_MEM_* tests in LKDTM (drivers/misc/lkdtm/fortify.c). * * For corner cases with UBSAN, try testing with: * @@ -346,6 +346,594 @@ DEFINE_ALLOC_SIZE_TEST_PAIR(kvmalloc) } while (0) DEFINE_ALLOC_SIZE_TEST_PAIR(devm_kmalloc) +/* + * We can't have an array at the end of a structure or else + * builds without -fstrict-flex-arrays=3 will report them as + * being an unknown length. Additionally, add bytes before + * and after the string to catch over/underflows if tests + * fail. + */ +struct fortify_padding { + unsigned long bytes_before; + char buf[32]; + unsigned long bytes_after; +}; +/* Force compiler into not being able to resolve size at compile-time. */ +static volatile int unconst; + +static void strlen_test(struct kunit *test) +{ + struct fortify_padding pad = { }; + int i, end = sizeof(pad.buf) - 1; + + /* Fill 31 bytes with valid characters. */ + for (i = 0; i < sizeof(pad.buf) - 1; i++) + pad.buf[i] = i + '0'; + /* Trailing bytes are still %NUL. */ + KUNIT_EXPECT_EQ(test, pad.buf[end], '\0'); + KUNIT_EXPECT_EQ(test, pad.bytes_after, 0); + + /* String is terminated, so strlen() is valid. */ + KUNIT_EXPECT_EQ(test, strlen(pad.buf), end); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); + + /* Make string unterminated, and recount. */ + pad.buf[end] = 'A'; + end = sizeof(pad.buf); + KUNIT_EXPECT_EQ(test, strlen(pad.buf), end); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 1); +} + +static void strnlen_test(struct kunit *test) +{ + struct fortify_padding pad = { }; + int i, end = sizeof(pad.buf) - 1; + + /* Fill 31 bytes with valid characters. */ + for (i = 0; i < sizeof(pad.buf) - 1; i++) + pad.buf[i] = i + '0'; + /* Trailing bytes are still %NUL. */ + KUNIT_EXPECT_EQ(test, pad.buf[end], '\0'); + KUNIT_EXPECT_EQ(test, pad.bytes_after, 0); + + /* String is terminated, so strnlen() is valid. */ + KUNIT_EXPECT_EQ(test, strnlen(pad.buf, sizeof(pad.buf)), end); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); + /* A truncated strnlen() will be safe, too. */ + KUNIT_EXPECT_EQ(test, strnlen(pad.buf, sizeof(pad.buf) / 2), + sizeof(pad.buf) / 2); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); + + /* Make string unterminated, and recount. */ + pad.buf[end] = 'A'; + end = sizeof(pad.buf); + /* Reading beyond with strncpy() will fail. */ + KUNIT_EXPECT_EQ(test, strnlen(pad.buf, end + 1), end); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 1); + KUNIT_EXPECT_EQ(test, strnlen(pad.buf, end + 2), end); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 2); + + /* Early-truncated is safe still, though. */ + KUNIT_EXPECT_EQ(test, strnlen(pad.buf, end), end); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 2); + + end = sizeof(pad.buf) / 2; + KUNIT_EXPECT_EQ(test, strnlen(pad.buf, end), end); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 2); +} + +static void strcpy_test(struct kunit *test) +{ + struct fortify_padding pad = { }; + char src[sizeof(pad.buf) + 1] = { }; + int i; + + /* Fill 31 bytes with valid characters. */ + for (i = 0; i < sizeof(src) - 2; i++) + src[i] = i + '0'; + + /* Destination is %NUL-filled to start with. */ + KUNIT_EXPECT_EQ(test, pad.bytes_before, 0); + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0'); + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 3], '\0'); + KUNIT_EXPECT_EQ(test, pad.bytes_after, 0); + + /* Legitimate strcpy() 1 less than of max size. */ + KUNIT_ASSERT_TRUE(test, strcpy(pad.buf, src) + == pad.buf); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); + KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0); + /* Only last byte should be %NUL */ + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0'); + + src[sizeof(src) - 2] = 'A'; + /* But now we trip the overflow checking. */ + KUNIT_ASSERT_TRUE(test, strcpy(pad.buf, src) + == pad.buf); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); + KUNIT_EXPECT_EQ(test, fortify_write_overflows, 1); + /* Trailing %NUL -- thanks to FORTIFY. */ + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + /* And we will not have gone beyond. */ + KUNIT_EXPECT_EQ(test, pad.bytes_after, 0); + + src[sizeof(src) - 1] = 'A'; + /* And for sure now, two bytes past. */ + KUNIT_ASSERT_TRUE(test, strcpy(pad.buf, src) + == pad.buf); + /* + * Which trips both the strlen() on the unterminated src, + * and the resulting copy attempt. + */ + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 1); + KUNIT_EXPECT_EQ(test, fortify_write_overflows, 2); + /* Trailing %NUL -- thanks to FORTIFY. */ + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + /* And we will not have gone beyond. */ + KUNIT_EXPECT_EQ(test, pad.bytes_after, 0); +} + +static void strncpy_test(struct kunit *test) +{ + struct fortify_padding pad = { }; + char src[] = "Copy me fully into a small buffer and I will overflow!"; + + /* Destination is %NUL-filled to start with. */ + KUNIT_EXPECT_EQ(test, pad.bytes_before, 0); + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0'); + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 3], '\0'); + KUNIT_EXPECT_EQ(test, pad.bytes_after, 0); + + /* Legitimate strncpy() 1 less than of max size. */ + KUNIT_ASSERT_TRUE(test, strncpy(pad.buf, src, + sizeof(pad.buf) + unconst - 1) + == pad.buf); + KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0); + /* Only last byte should be %NUL */ + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0'); + + /* Legitimate (though unterminated) max-size strncpy. */ + KUNIT_ASSERT_TRUE(test, strncpy(pad.buf, src, + sizeof(pad.buf) + unconst) + == pad.buf); + KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0); + /* No trailing %NUL -- thanks strncpy API. */ + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 1], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + /* But we will not have gone beyond. */ + KUNIT_EXPECT_EQ(test, pad.bytes_after, 0); + + /* Now verify that FORTIFY is working... */ + KUNIT_ASSERT_TRUE(test, strncpy(pad.buf, src, + sizeof(pad.buf) + unconst + 1) + == pad.buf); + /* Should catch the overflow. */ + KUNIT_EXPECT_EQ(test, fortify_write_overflows, 1); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 1], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + /* And we will not have gone beyond. */ + KUNIT_EXPECT_EQ(test, pad.bytes_after, 0); + + /* And further... */ + KUNIT_ASSERT_TRUE(test, strncpy(pad.buf, src, + sizeof(pad.buf) + unconst + 2) + == pad.buf); + /* Should catch the overflow. */ + KUNIT_EXPECT_EQ(test, fortify_write_overflows, 2); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 1], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + /* And we will not have gone beyond. */ + KUNIT_EXPECT_EQ(test, pad.bytes_after, 0); +} + +static void strscpy_test(struct kunit *test) +{ + struct fortify_padding pad = { }; + char src[] = "Copy me fully into a small buffer and I will overflow!"; + + /* Destination is %NUL-filled to start with. */ + KUNIT_EXPECT_EQ(test, pad.bytes_before, 0); + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0'); + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 3], '\0'); + KUNIT_EXPECT_EQ(test, pad.bytes_after, 0); + + /* Legitimate strscpy() 1 less than of max size. */ + KUNIT_ASSERT_EQ(test, strscpy(pad.buf, src, + sizeof(pad.buf) + unconst - 1), + -E2BIG); + KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0); + /* Keeping space for %NUL, last two bytes should be %NUL */ + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0'); + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0'); + + /* Legitimate max-size strscpy. */ + KUNIT_ASSERT_EQ(test, strscpy(pad.buf, src, + sizeof(pad.buf) + unconst), + -E2BIG); + KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0); + /* A trailing %NUL will exist. */ + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + + /* Now verify that FORTIFY is working... */ + KUNIT_ASSERT_EQ(test, strscpy(pad.buf, src, + sizeof(pad.buf) + unconst + 1), + -E2BIG); + /* Should catch the overflow. */ + KUNIT_EXPECT_EQ(test, fortify_write_overflows, 1); + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + /* And we will not have gone beyond. */ + KUNIT_EXPECT_EQ(test, pad.bytes_after, 0); + + /* And much further... */ + KUNIT_ASSERT_EQ(test, strscpy(pad.buf, src, + sizeof(src) * 2 + unconst), + -E2BIG); + /* Should catch the overflow. */ + KUNIT_EXPECT_EQ(test, fortify_write_overflows, 2); + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + /* And we will not have gone beyond. */ + KUNIT_EXPECT_EQ(test, pad.bytes_after, 0); +} + +static void strcat_test(struct kunit *test) +{ + struct fortify_padding pad = { }; + char src[sizeof(pad.buf) / 2] = { }; + char one[] = "A"; + char two[] = "BC"; + int i; + + /* Fill 15 bytes with valid characters. */ + for (i = 0; i < sizeof(src) - 1; i++) + src[i] = i + 'A'; + + /* Destination is %NUL-filled to start with. */ + KUNIT_EXPECT_EQ(test, pad.bytes_before, 0); + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0'); + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 3], '\0'); + KUNIT_EXPECT_EQ(test, pad.bytes_after, 0); + + /* Legitimate strcat() using less than half max size. */ + KUNIT_ASSERT_TRUE(test, strcat(pad.buf, src) == pad.buf); + KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0); + /* Legitimate strcat() now 2 bytes shy of end. */ + KUNIT_ASSERT_TRUE(test, strcat(pad.buf, src) == pad.buf); + KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0); + /* Last two bytes should be %NUL */ + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0'); + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0'); + + /* Add one more character to the end. */ + KUNIT_ASSERT_TRUE(test, strcat(pad.buf, one) == pad.buf); + KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0); + /* Last byte should be %NUL */ + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0'); + + /* And this one char will overflow. */ + KUNIT_ASSERT_TRUE(test, strcat(pad.buf, one) == pad.buf); + KUNIT_EXPECT_EQ(test, fortify_write_overflows, 1); + /* Last byte should be %NUL thanks to FORTIFY. */ + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0'); + KUNIT_EXPECT_EQ(test, pad.bytes_after, 0); + + /* And adding two will overflow more. */ + KUNIT_ASSERT_TRUE(test, strcat(pad.buf, two) == pad.buf); + KUNIT_EXPECT_EQ(test, fortify_write_overflows, 2); + /* Last byte should be %NUL thanks to FORTIFY. */ + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0'); + KUNIT_EXPECT_EQ(test, pad.bytes_after, 0); +} + +static void strncat_test(struct kunit *test) +{ + struct fortify_padding pad = { }; + char src[sizeof(pad.buf)] = { }; + int i, partial; + + /* Fill 31 bytes with valid characters. */ + partial = sizeof(src) / 2 - 1; + for (i = 0; i < partial; i++) + src[i] = i + 'A'; + + /* Destination is %NUL-filled to start with. */ + KUNIT_EXPECT_EQ(test, pad.bytes_before, 0); + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0'); + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 3], '\0'); + KUNIT_EXPECT_EQ(test, pad.bytes_after, 0); + + /* Legitimate strncat() using less than half max size. */ + KUNIT_ASSERT_TRUE(test, strncat(pad.buf, src, partial) == pad.buf); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); + KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0); + /* Legitimate strncat() now 2 bytes shy of end. */ + KUNIT_ASSERT_TRUE(test, strncat(pad.buf, src, partial) == pad.buf); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); + KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0); + /* Last two bytes should be %NUL */ + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0'); + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0'); + + /* Add one more character to the end. */ + KUNIT_ASSERT_TRUE(test, strncat(pad.buf, src, 1) == pad.buf); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); + KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0); + /* Last byte should be %NUL */ + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0'); + + /* And this one char will overflow. */ + KUNIT_ASSERT_TRUE(test, strncat(pad.buf, src, 1) == pad.buf); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); + KUNIT_EXPECT_EQ(test, fortify_write_overflows, 1); + /* Last byte should be %NUL thanks to FORTIFY. */ + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0'); + KUNIT_EXPECT_EQ(test, pad.bytes_after, 0); + + /* And adding two will overflow more. */ + KUNIT_ASSERT_TRUE(test, strncat(pad.buf, src, 2) == pad.buf); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); + KUNIT_EXPECT_EQ(test, fortify_write_overflows, 2); + /* Last byte should be %NUL thanks to FORTIFY. */ + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0'); + KUNIT_EXPECT_EQ(test, pad.bytes_after, 0); + + /* Force an unterminated destination, and overflow. */ + pad.buf[sizeof(pad.buf) - 1] = 'A'; + KUNIT_ASSERT_TRUE(test, strncat(pad.buf, src, 1) == pad.buf); + /* This will have tripped both strlen() and strcat(). */ + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 1); + KUNIT_EXPECT_EQ(test, fortify_write_overflows, 3); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 1], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0'); + /* But we should not go beyond the end. */ + KUNIT_EXPECT_EQ(test, pad.bytes_after, 0); +} + +static void strlcat_test(struct kunit *test) +{ + struct fortify_padding pad = { }; + char src[sizeof(pad.buf)] = { }; + int i, partial; + int len = sizeof(pad.buf) + unconst; + + /* Fill 15 bytes with valid characters. */ + partial = sizeof(src) / 2 - 1; + for (i = 0; i < partial; i++) + src[i] = i + 'A'; + + /* Destination is %NUL-filled to start with. */ + KUNIT_EXPECT_EQ(test, pad.bytes_before, 0); + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0'); + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 3], '\0'); + KUNIT_EXPECT_EQ(test, pad.bytes_after, 0); + + /* Legitimate strlcat() using less than half max size. */ + KUNIT_ASSERT_EQ(test, strlcat(pad.buf, src, len), partial); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); + KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0); + /* Legitimate strlcat() now 2 bytes shy of end. */ + KUNIT_ASSERT_EQ(test, strlcat(pad.buf, src, len), partial * 2); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); + KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0); + /* Last two bytes should be %NUL */ + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0'); + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0'); + + /* Add one more character to the end. */ + KUNIT_ASSERT_EQ(test, strlcat(pad.buf, "Q", len), partial * 2 + 1); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); + KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0); + /* Last byte should be %NUL */ + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0'); + + /* And this one char will overflow. */ + KUNIT_ASSERT_EQ(test, strlcat(pad.buf, "V", len * 2), len); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); + KUNIT_EXPECT_EQ(test, fortify_write_overflows, 1); + /* Last byte should be %NUL thanks to FORTIFY. */ + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0'); + KUNIT_EXPECT_EQ(test, pad.bytes_after, 0); + + /* And adding two will overflow more. */ + KUNIT_ASSERT_EQ(test, strlcat(pad.buf, "QQ", len * 2), len + 1); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); + KUNIT_EXPECT_EQ(test, fortify_write_overflows, 2); + /* Last byte should be %NUL thanks to FORTIFY. */ + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0'); + KUNIT_EXPECT_EQ(test, pad.bytes_after, 0); + + /* Force an unterminated destination, and overflow. */ + pad.buf[sizeof(pad.buf) - 1] = 'A'; + KUNIT_ASSERT_EQ(test, strlcat(pad.buf, "TT", len * 2), len + 2); + /* This will have tripped both strlen() and strlcat(). */ + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 2); + KUNIT_EXPECT_EQ(test, fortify_write_overflows, 2); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 1], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0'); + KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0'); + /* But we should not go beyond the end. */ + KUNIT_EXPECT_EQ(test, pad.bytes_after, 0); + + /* Force an unterminated source, and overflow. */ + memset(src, 'B', sizeof(src)); + pad.buf[sizeof(pad.buf) - 1] = '\0'; + KUNIT_ASSERT_EQ(test, strlcat(pad.buf, src, len * 3), len - 1 + sizeof(src)); + /* This will have tripped both strlen() and strlcat(). */ + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 3); + KUNIT_EXPECT_EQ(test, fortify_write_overflows, 3); + KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0'); + /* But we should not go beyond the end. */ + KUNIT_EXPECT_EQ(test, pad.bytes_after, 0); +} + +static void memscan_test(struct kunit *test) +{ + char haystack[] = "Where oh where is my memory range?"; + char *mem = haystack + strlen("Where oh where is "); + char needle = 'm'; + size_t len = sizeof(haystack) + unconst; + + KUNIT_ASSERT_PTR_EQ(test, memscan(haystack, needle, len), + mem); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); + /* Catch too-large range. */ + KUNIT_ASSERT_PTR_EQ(test, memscan(haystack, needle, len + 1), + NULL); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 1); + KUNIT_ASSERT_PTR_EQ(test, memscan(haystack, needle, len * 2), + NULL); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 2); +} + +static void memchr_test(struct kunit *test) +{ + char haystack[] = "Where oh where is my memory range?"; + char *mem = haystack + strlen("Where oh where is "); + char needle = 'm'; + size_t len = sizeof(haystack) + unconst; + + KUNIT_ASSERT_PTR_EQ(test, memchr(haystack, needle, len), + mem); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); + /* Catch too-large range. */ + KUNIT_ASSERT_PTR_EQ(test, memchr(haystack, needle, len + 1), + NULL); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 1); + KUNIT_ASSERT_PTR_EQ(test, memchr(haystack, needle, len * 2), + NULL); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 2); +} + +static void memchr_inv_test(struct kunit *test) +{ + char haystack[] = "Where oh where is my memory range?"; + char *mem = haystack + 1; + char needle = 'W'; + size_t len = sizeof(haystack) + unconst; + + /* Normal search is okay. */ + KUNIT_ASSERT_PTR_EQ(test, memchr_inv(haystack, needle, len), + mem); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); + /* Catch too-large range. */ + KUNIT_ASSERT_PTR_EQ(test, memchr_inv(haystack, needle, len + 1), + NULL); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 1); + KUNIT_ASSERT_PTR_EQ(test, memchr_inv(haystack, needle, len * 2), + NULL); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 2); +} + +static void memcmp_test(struct kunit *test) +{ + char one[] = "My mind is going ..."; + char two[] = "My mind is going ... I can feel it."; + size_t one_len = sizeof(one) + unconst - 1; + size_t two_len = sizeof(two) + unconst - 1; + + /* We match the first string (ignoring the %NUL). */ + KUNIT_ASSERT_EQ(test, memcmp(one, two, one_len), 0); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); + /* Still in bounds, but no longer matching. */ + KUNIT_ASSERT_EQ(test, memcmp(one, two, one_len + 1), -32); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); + + /* Catch too-large ranges. */ + KUNIT_ASSERT_EQ(test, memcmp(one, two, one_len + 2), INT_MIN); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 1); + + KUNIT_ASSERT_EQ(test, memcmp(two, one, two_len + 2), INT_MIN); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 2); +} + +static void kmemdup_test(struct kunit *test) +{ + char src[] = "I got Doom running on it!"; + char *copy; + size_t len = sizeof(src) + unconst; + + /* Copy is within bounds. */ + copy = kmemdup(src, len, GFP_KERNEL); + KUNIT_EXPECT_NOT_NULL(test, copy); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); + kfree(copy); + + /* Without %NUL. */ + copy = kmemdup(src, len - 1, GFP_KERNEL); + KUNIT_EXPECT_NOT_NULL(test, copy); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); + kfree(copy); + + /* Tiny bounds. */ + copy = kmemdup(src, 1, GFP_KERNEL); + KUNIT_EXPECT_NOT_NULL(test, copy); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); + kfree(copy); + + /* Out of bounds by 1 byte. */ + copy = kmemdup(src, len + 1, GFP_KERNEL); + KUNIT_EXPECT_NULL(test, copy); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 1); + kfree(copy); + + /* Way out of bounds. */ + copy = kmemdup(src, len * 2, GFP_KERNEL); + KUNIT_EXPECT_NULL(test, copy); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 2); + kfree(copy); + + /* Starting offset causing out of bounds. */ + copy = kmemdup(src + 1, len, GFP_KERNEL); + KUNIT_EXPECT_NULL(test, copy); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 3); + kfree(copy); +} + static int fortify_test_init(struct kunit *test) { if (!IS_ENABLED(CONFIG_FORTIFY_SOURCE)) @@ -373,6 +961,21 @@ static struct kunit_case fortify_test_cases[] = { KUNIT_CASE(alloc_size_kvmalloc_dynamic_test), KUNIT_CASE(alloc_size_devm_kmalloc_const_test), KUNIT_CASE(alloc_size_devm_kmalloc_dynamic_test), + KUNIT_CASE(strlen_test), + KUNIT_CASE(strnlen_test), + KUNIT_CASE(strcpy_test), + KUNIT_CASE(strncpy_test), + KUNIT_CASE(strscpy_test), + KUNIT_CASE(strcat_test), + KUNIT_CASE(strncat_test), + KUNIT_CASE(strlcat_test), + /* skip memset: performs bounds checking on whole structs */ + /* skip memcpy: still using warn-and-overwrite instead of hard-fail */ + KUNIT_CASE(memscan_test), + KUNIT_CASE(memchr_test), + KUNIT_CASE(memchr_inv_test), + KUNIT_CASE(memcmp_test), + KUNIT_CASE(kmemdup_test), {} }; From patchwork Sat Feb 17 04:48:28 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kees Cook X-Patchwork-Id: 202569 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a05:693c:2685:b0:108:e6aa:91d0 with SMTP id mn5csp149865dyc; Fri, 16 Feb 2024 20:54:56 -0800 (PST) X-Forwarded-Encrypted: i=3; AJvYcCUhG+jXFFxWa8MxLdG2Lp6rU4u/4iW1BCOvCUSkxmj6mcR2cLh2zQ7hdSTTu8vM/DEcBbs4TErijogWPw8yzFPEucTtHQ== X-Google-Smtp-Source: AGHT+IFPbBIa1CyL76OB3fYMMZS7msln3BlHjzrPHEs46jhalYpSOU666/PtHbgCFLolhQHGTNSr X-Received: by 2002:a05:620a:14b1:b0:787:3684:38a1 with SMTP id x17-20020a05620a14b100b00787368438a1mr6558820qkj.73.1708145696289; Fri, 16 Feb 2024 20:54:56 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1708145696; cv=pass; d=google.com; s=arc-20160816; b=oz4UtWolWp8AOh3nVZM0663OpkJL7ItIUiv/vcDkUmeRTT5Yvu30COGbIb4Pwa1lkf uS/212kt7XX+2vBIEk8JJcc9IHZK/1QRCrG5XMTt2noJXIWLkH5yXk/xN14BQf69B+aZ UHSL2ZcO0yd1YEzuoqdHpcweHJIihH5XYUrFiHOWR1p+rCbO79baLmGXNWwp9T5hnZ/S az+sLd4inArKGgBkQrjHie7VYOMpgDltcJ8d0U69qGzbUBI1d/Vlv2YaWu1Os4OWNDxb xZzNX12FUNa0Z9p8i3OdEvn9kaJhSqLL6/e9GNTRIGUwh3pL6HFFxo80tijFMMAS/OoG 5zgA== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:list-unsubscribe :list-subscribe:list-id:precedence:references:in-reply-to:message-id :date:subject:cc:to:from:dkim-signature; bh=ywnWEQ8XGtSxKO6WdLPFCCZ1X6Rmd0BoX1prDPDFNU0=; fh=bNlLxjzkcNBX3+eKd2oMgTsyj1M0Bci6di7BSUh+l90=; b=Du37cUuCHxc5TRkaniuIAmpkaUiXvy+94vxC/VXLUbJ00/6WUrYTx7Xgh+jGNR48+Y Xo9eFv5r3JEjTTJd8UIpl6itdedxqjOsFQn/Im/RGxoZLD9rP+0lMbA3LIGyzm4x3Rst yu/8tT4IcJee0UDWF18slx0yQsp5NiwAZmgwTtNWq3cPc1yTqXm1I4thMIM0EPoHkn21 7KpRgMqanYuMX5fZ1WrylmXGRiJUxHTVs4uNOnQ2SV2qCmj6MtSDuDADZtoUGiHodL53 osgQCf3L5i5p2hb7VV8On02QZJfed0dmSFSW1HC2BgAuvMZ/5GzZUkyMTRAeQgl//nlo saKg==; dara=google.com ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@chromium.org header.s=google header.b=PW2M3D5F; arc=pass (i=1 spf=pass spfdomain=chromium.org dkim=pass dkdomain=chromium.org dmarc=pass fromdomain=chromium.org); spf=pass (google.com: domain of linux-kernel+bounces-69688-ouuuleilei=gmail.com@vger.kernel.org designates 2604:1380:45d1:ec00::1 as permitted sender) smtp.mailfrom="linux-kernel+bounces-69688-ouuuleilei=gmail.com@vger.kernel.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=chromium.org Received: from ny.mirrors.kernel.org (ny.mirrors.kernel.org. [2604:1380:45d1:ec00::1]) by mx.google.com with ESMTPS id m8-20020a05620a24c800b0078735aa7074si1657417qkn.533.2024.02.16.20.54.56 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 16 Feb 2024 20:54:56 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel+bounces-69688-ouuuleilei=gmail.com@vger.kernel.org designates 2604:1380:45d1:ec00::1 as permitted sender) client-ip=2604:1380:45d1:ec00::1; Authentication-Results: mx.google.com; dkim=pass header.i=@chromium.org header.s=google header.b=PW2M3D5F; arc=pass (i=1 spf=pass spfdomain=chromium.org dkim=pass dkdomain=chromium.org dmarc=pass fromdomain=chromium.org); spf=pass (google.com: domain of linux-kernel+bounces-69688-ouuuleilei=gmail.com@vger.kernel.org designates 2604:1380:45d1:ec00::1 as permitted sender) smtp.mailfrom="linux-kernel+bounces-69688-ouuuleilei=gmail.com@vger.kernel.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=chromium.org Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ny.mirrors.kernel.org (Postfix) with ESMTPS id 0A2F01C21217 for ; Sat, 17 Feb 2024 04:54:56 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 5F7461C6A5; Sat, 17 Feb 2024 04:53:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b="PW2M3D5F" Received: from mail-pj1-f41.google.com (mail-pj1-f41.google.com [209.85.216.41]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 874F61CAAD for ; Sat, 17 Feb 2024 04:53:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.41 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1708145623; cv=none; b=MLHkoKA7DPU0csDWnLsnAPaoSDVMoESpwJ4TphHhp6MWD1frMOf3Q1fRnzMSBlb8knhebdrncLgsyVvP+cnqmQNRldm7pQ/Vi9/cL7dfAYTVkA8lljRuxXB0WKZDtUuaa7RAtwmb9q2MoIpj+YMz095oCdIob6Pr+A0sRNXs7RU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1708145623; c=relaxed/simple; bh=o9bi0PQaph7nLtjH/UpCrP47Rjaglgom5drD61yQXYI=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=uXaBQZjYrqBWFODGfm1U8indtBpAqNOol8Gr0nR48pqed7kvdjT9+ZcRW4df0yRfI3DsYmwSPzeU+MHs8X7ngOHPWg1zAalJYFHsD2PsyEXL4UUZZ6UDn1elym790iJVTDozxBHLhsLIijCINcFWrVzMdDBwqi6BNEyjWIyNndg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=chromium.org; spf=pass smtp.mailfrom=chromium.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b=PW2M3D5F; arc=none smtp.client-ip=209.85.216.41 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=chromium.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=chromium.org Received: by mail-pj1-f41.google.com with SMTP id 98e67ed59e1d1-296a02b7104so2069425a91.2 for ; Fri, 16 Feb 2024 20:53:41 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1708145621; x=1708750421; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=ywnWEQ8XGtSxKO6WdLPFCCZ1X6Rmd0BoX1prDPDFNU0=; b=PW2M3D5F3LZqRaR4xKb4A3ZU8T+BAypqiU+1wzyWU7NPiP7oap6HxuRVhjhh505Lbe KQmqp+y3rMwAXV4QeSG9bPawwLCH8tPimqdsbWTDppiFRpMtG+lui74C2atTVlYx+Cug 2AMDX958xFtua5d6pp+C+sAXcht3ta/AGZFso= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1708145621; x=1708750421; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=ywnWEQ8XGtSxKO6WdLPFCCZ1X6Rmd0BoX1prDPDFNU0=; b=DHxKKiMCes+8SAan7pR4Xa4uuxqqdgPe3sKUSQ2f/FQCQO45ufjZn79vzAnHwsL88I IDcJpslsCoL5yZON91eBg04A4WTFGvJU3FO81SXExQ7bkjiGwIA9ZtJFVT9msPXkg+RR WlwqMF3u1ePLInc5r+9KgzDnb8B3Koab249x7+0rQfRErY6ffHD0ITUr5l1wTaVqVVss MS21q0mNAmG7XBCQQiT+UYTplUXmsznF8k7ewfYIExDltwg0facWr2829cCKZ8ZnQFJm 9ImuR7eYZYY5f/VC5OAG8tBNt9SfgagXqQCX+uyb+mGhnTUsrWie/tUZfwedVRVbpGRZ Egcw== X-Forwarded-Encrypted: i=1; AJvYcCV6Ms+3gZKQp8j5rB2IzRwqAdhImwfDrjeJUeFW0829fVfSRqdixOTOCgbmofeLvEIIE9o5GubS7pptMzo3ZEtVeLfFlGWw2PGaq7OP X-Gm-Message-State: AOJu0YyYbdN3DU86YMgXswdAVjTVm+eUPCK/W9YM0/mpp3zbzlRKOcmE 3o5nGnb/kFwH7taGnJlB29TrwXhTvg/3T/EphREaSMeS8PSR4zEsuui2jk+KNg== X-Received: by 2002:a17:902:b944:b0:1d9:893f:cd06 with SMTP id h4-20020a170902b94400b001d9893fcd06mr6105079pls.60.1708145620829; Fri, 16 Feb 2024 20:53:40 -0800 (PST) Received: from www.outflux.net ([198.0.35.241]) by smtp.gmail.com with ESMTPSA id c5-20020a170902c1c500b001db92e7b80fsm626092plc.155.2024.02.16.20.53.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 16 Feb 2024 20:53:39 -0800 (PST) From: Kees Cook To: linux-hardening@vger.kernel.org Cc: Kees Cook , Alexander Lobakin , Andy Shevchenko , Cezary Rojewski , Puyou Lu , Mark Brown , Brendan Higgins , David Gow , Nick Desaulniers , linux-kernel@vger.kernel.org, kunit-dev@googlegroups.com Subject: [PATCH v3 5/5] fortify: Improve buffer overflow reporting Date: Fri, 16 Feb 2024 20:48:28 -0800 Message-Id: <20240217045335.1526675-5-keescook@chromium.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240217043535.make.664-kees@kernel.org> References: <20240217043535.make.664-kees@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=11459; i=keescook@chromium.org; h=from:subject; bh=o9bi0PQaph7nLtjH/UpCrP47Rjaglgom5drD61yQXYI=; b=owEBbQKS/ZANAwAKAYly9N/cbcAmAcsmYgBl0DqcWSD5g8bWqw5Np2W+346suamMHJeWtUZ8p TdB8pK3rSGJAjMEAAEKAB0WIQSlw/aPIp3WD3I+bhOJcvTf3G3AJgUCZdA6nAAKCRCJcvTf3G3A JgE4D/9m0K1cswkGbu1+lS3yfX+30qR+82N7kQbHh4UkbEoYbKLfZM90+18eewHgou7+48MEg23 qjJbbTuKatJQjEZntkXXchY6oI2NR5IMHnL7Rg8/QCGEOBr3iSn0pzsbN95kCX5+9zR9byw4qPe mtCYhckX38QdRAxxQZGUFpdEGjF7VcQ9YQ8BXPHzNFHfxr6gFeb3HQ9Zq1tc2HN2+bd3YIpHdkg kr+4AjBo5pssrXXhSoUehYYWrUgfCuQ8cd3jLPLrjXxiD0L7A1GUO0ppP1lWxUJAt5ISEscBxWL Bpzhh+rlyW/74e9JVDH1DMO8aI6Kc/4t2149NAH1e+pidkGxbt11FpfV9yrDjn4SajAmIp5unVT KXLboyvtY5ED8Hcy2t4coe3OUgX3gZpR5OZFKJgMt0DWLB/jgMB5TK39Jp4zABnIThXLCg3ibew WhgEw9p2Fm2bBgxFoU0/NQo4rudSBCqYZnJQ8ATfmuuyPMwYUnyRnOF7KPQNun4gZxOfgSgQdYX ZLJB1BEO3d+jCN1X9aWMHb32nNU61rdcmHfNK9/cMlUJw1H+aTnPYz+kWv3MbDHwRCCO8RNTpsB s0k8aYFA6JCA5SVlRC+Rt5T51tmV42na5BcHOF2nVgAQyKMbyJQfm46z6wt3scIUlYhVTUaMAmy 5yU7tIvgBucRKCA== X-Developer-Key: i=keescook@chromium.org; a=openpgp; fpr=A5C3F68F229DD60F723E6E138972F4DFDC6DC026 X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1791120581655511415 X-GMAIL-MSGID: 1791120581655511415 Improve the reporting of buffer overflows under CONFIG_FORTIFY_SOURCE to help accelerate debugging efforts. The calculations are all just sitting in registers anyway, so pass them along to the function to be reported. For example, before: detected buffer overflow in memcpy and after: memcpy: detected buffer overflow: 4096 byte read of buffer size 1 Link: https://lore.kernel.org/r/20230407192717.636137-10-keescook@chromium.org Signed-off-by: Kees Cook --- arch/arm/boot/compressed/misc.c | 2 +- arch/x86/boot/compressed/misc.c | 2 +- include/linux/fortify-string.h | 56 ++++++++++++++++++--------------- lib/fortify_kunit.c | 4 +-- lib/string_helpers.c | 9 +++--- 5 files changed, 39 insertions(+), 34 deletions(-) diff --git a/arch/arm/boot/compressed/misc.c b/arch/arm/boot/compressed/misc.c index d93e2e466f6a..6c41b270560e 100644 --- a/arch/arm/boot/compressed/misc.c +++ b/arch/arm/boot/compressed/misc.c @@ -154,7 +154,7 @@ decompress_kernel(unsigned long output_start, unsigned long free_mem_ptr_p, putstr(" done, booting the kernel.\n"); } -void __fortify_panic(const u8 reason) +void __fortify_panic(const u8 reason, size_t avail, size_t size) { error("detected buffer overflow"); } diff --git a/arch/x86/boot/compressed/misc.c b/arch/x86/boot/compressed/misc.c index c9971b9dbb09..1844da203da9 100644 --- a/arch/x86/boot/compressed/misc.c +++ b/arch/x86/boot/compressed/misc.c @@ -496,7 +496,7 @@ asmlinkage __visible void *extract_kernel(void *rmode, unsigned char *output) return output + entry_offset; } -void __fortify_panic(const u8 reason) +void __fortify_panic(const u8 reason, size_t avail, size_t size) { error("detected buffer overflow"); } diff --git a/include/linux/fortify-string.h b/include/linux/fortify-string.h index fbfb90479b8f..6aeebe0a6777 100644 --- a/include/linux/fortify-string.h +++ b/include/linux/fortify-string.h @@ -16,8 +16,8 @@ FIELD_PREP(GENMASK(7, 1), func)) #ifndef fortify_panic -# define fortify_panic(func, write, retfail) \ - __fortify_panic(FORTIFY_REASON(func, write)) +# define fortify_panic(func, write, avail, size, retfail) \ + __fortify_panic(FORTIFY_REASON(func, write), avail, size) #endif #define FORTIFY_READ 0 @@ -48,8 +48,8 @@ enum fortify_func { EACH_FORTIFY_FUNC(MAKE_FORTIFY_FUNC) }; -void __fortify_report(const u8 reason); -void __fortify_panic(const u8 reason) __cold __noreturn; +void __fortify_report(const u8 reason, const size_t avail, const size_t size); +void __fortify_panic(const u8 reason, const size_t avail, const size_t size) __cold __noreturn; void __read_overflow(void) __compiletime_error("detected read beyond size of object (1st parameter)"); void __read_overflow2(void) __compiletime_error("detected read beyond size of object (2nd parameter)"); void __read_overflow2_field(size_t avail, size_t wanted) __compiletime_warning("detected read beyond size of field (2nd parameter); maybe use struct_group()?"); @@ -183,7 +183,7 @@ char *strncpy(char * const POS p, const char *q, __kernel_size_t size) if (__compiletime_lessthan(p_size, size)) __write_overflow(); if (p_size < size) - fortify_panic(FORTIFY_FUNC_strncpy, FORTIFY_WRITE, p); + fortify_panic(FORTIFY_FUNC_strncpy, FORTIFY_WRITE, p_size, size, p); return __underlying_strncpy(p, q, size); } @@ -214,7 +214,7 @@ __FORTIFY_INLINE __kernel_size_t strnlen(const char * const POS p, __kernel_size /* Do not check characters beyond the end of p. */ ret = __real_strnlen(p, maxlen < p_size ? maxlen : p_size); if (p_size <= ret && maxlen != ret) - fortify_panic(FORTIFY_FUNC_strnlen, FORTIFY_READ, ret); + fortify_panic(FORTIFY_FUNC_strnlen, FORTIFY_READ, p_size, ret + 1, ret); return ret; } @@ -250,7 +250,7 @@ __kernel_size_t __fortify_strlen(const char * const POS p) return __underlying_strlen(p); ret = strnlen(p, p_size); if (p_size <= ret) - fortify_panic(FORTIFY_FUNC_strlen, FORTIFY_READ, ret); + fortify_panic(FORTIFY_FUNC_strlen, FORTIFY_READ, p_size, ret + 1, ret); return ret; } @@ -300,8 +300,8 @@ __FORTIFY_INLINE ssize_t sized_strscpy(char * const POS p, const char * const PO * Generate a runtime write overflow error if len is greater than * p_size. */ - if (len > p_size) - fortify_panic(FORTIFY_FUNC_strscpy, FORTIFY_WRITE, -E2BIG); + if (p_size < len) + fortify_panic(FORTIFY_FUNC_strscpy, FORTIFY_WRITE, p_size, len, -E2BIG); /* * We can now safely call vanilla strscpy because we are protected from: @@ -359,7 +359,7 @@ size_t strlcat(char * const POS p, const char * const POS q, size_t avail) /* Give up if string is already overflowed. */ if (p_size <= p_len) - fortify_panic(FORTIFY_FUNC_strlcat, FORTIFY_READ, wanted); + fortify_panic(FORTIFY_FUNC_strlcat, FORTIFY_READ, p_size, p_len + 1, wanted); if (actual >= avail) { copy_len = avail - p_len - 1; @@ -368,7 +368,7 @@ size_t strlcat(char * const POS p, const char * const POS q, size_t avail) /* Give up if copy will overflow. */ if (p_size <= actual) - fortify_panic(FORTIFY_FUNC_strlcat, FORTIFY_WRITE, wanted); + fortify_panic(FORTIFY_FUNC_strlcat, FORTIFY_WRITE, p_size, actual + 1, wanted); __underlying_memcpy(p + p_len, q, copy_len); p[actual] = '\0'; @@ -395,9 +395,10 @@ __FORTIFY_INLINE __diagnose_as(__builtin_strcat, 1, 2) char *strcat(char * const POS p, const char *q) { const size_t p_size = __member_size(p); + const size_t wanted = strlcat(p, q, p_size); - if (strlcat(p, q, p_size) >= p_size) - fortify_panic(FORTIFY_FUNC_strcat, FORTIFY_WRITE, p); + if (p_size <= wanted) + fortify_panic(FORTIFY_FUNC_strcat, FORTIFY_WRITE, p_size, wanted + 1, p); return p; } @@ -426,14 +427,15 @@ char *strncat(char * const POS p, const char * const POS q, __kernel_size_t coun { const size_t p_size = __member_size(p); const size_t q_size = __member_size(q); - size_t p_len, copy_len; + size_t p_len, copy_len, total; if (p_size == SIZE_MAX && q_size == SIZE_MAX) return __underlying_strncat(p, q, count); p_len = strlen(p); copy_len = strnlen(q, count); - if (p_size < p_len + copy_len + 1) - fortify_panic(FORTIFY_FUNC_strncat, FORTIFY_WRITE, p); + total = p_len + copy_len + 1; + if (p_size < total) + fortify_panic(FORTIFY_FUNC_strncat, FORTIFY_WRITE, p_size, total, p); __underlying_memcpy(p + p_len, q, copy_len); p[p_len + copy_len] = '\0'; return p; @@ -474,7 +476,7 @@ __FORTIFY_INLINE bool fortify_memset_chk(__kernel_size_t size, * lengths are unknown.) */ if (p_size != SIZE_MAX && p_size < size) - fortify_panic(FORTIFY_FUNC_memset, FORTIFY_WRITE, true); + fortify_panic(FORTIFY_FUNC_memset, FORTIFY_WRITE, p_size, size, true); return false; } @@ -574,9 +576,9 @@ __FORTIFY_INLINE bool fortify_memcpy_chk(__kernel_size_t size, * lengths are unknown.) */ if (p_size != SIZE_MAX && p_size < size) - fortify_panic(func, FORTIFY_WRITE, true); + fortify_panic(func, FORTIFY_WRITE, p_size, size, true); else if (q_size != SIZE_MAX && q_size < size) - fortify_panic(func, FORTIFY_READ, true); + fortify_panic(func, FORTIFY_READ, p_size, size, true); /* * Warn when writing beyond destination field size. @@ -676,7 +678,7 @@ __FORTIFY_INLINE void *memscan(void * const POS0 p, int c, __kernel_size_t size) if (__compiletime_lessthan(p_size, size)) __read_overflow(); if (p_size < size) - fortify_panic(FORTIFY_FUNC_memscan, FORTIFY_READ, NULL); + fortify_panic(FORTIFY_FUNC_memscan, FORTIFY_READ, p_size, size, NULL); return __real_memscan(p, c, size); } @@ -692,8 +694,10 @@ int memcmp(const void * const POS0 p, const void * const POS0 q, __kernel_size_t if (__compiletime_lessthan(q_size, size)) __read_overflow2(); } - if (p_size < size || q_size < size) - fortify_panic(FORTIFY_FUNC_memcmp, FORTIFY_READ, INT_MIN); + if (p_size < size) + fortify_panic(FORTIFY_FUNC_memcmp, FORTIFY_READ, p_size, size, INT_MIN); + else if (q_size < size) + fortify_panic(FORTIFY_FUNC_memcmp, FORTIFY_READ, q_size, size, INT_MIN); return __underlying_memcmp(p, q, size); } @@ -705,7 +709,7 @@ void *memchr(const void * const POS0 p, int c, __kernel_size_t size) if (__compiletime_lessthan(p_size, size)) __read_overflow(); if (p_size < size) - fortify_panic(FORTIFY_FUNC_memchr, FORTIFY_READ, NULL); + fortify_panic(FORTIFY_FUNC_memchr, FORTIFY_READ, p_size, size, NULL); return __underlying_memchr(p, c, size); } @@ -717,7 +721,7 @@ __FORTIFY_INLINE void *memchr_inv(const void * const POS0 p, int c, size_t size) if (__compiletime_lessthan(p_size, size)) __read_overflow(); if (p_size < size) - fortify_panic(FORTIFY_FUNC_memchr_inv, FORTIFY_READ, NULL); + fortify_panic(FORTIFY_FUNC_memchr_inv, FORTIFY_READ, p_size, size, NULL); return __real_memchr_inv(p, c, size); } @@ -730,7 +734,7 @@ __FORTIFY_INLINE void *kmemdup(const void * const POS0 p, size_t size, gfp_t gfp if (__compiletime_lessthan(p_size, size)) __read_overflow(); if (p_size < size) - fortify_panic(FORTIFY_FUNC_kmemdup, FORTIFY_READ, NULL); + fortify_panic(FORTIFY_FUNC_kmemdup, FORTIFY_READ, p_size, size, NULL); return __real_kmemdup(p, size, gfp); } @@ -767,7 +771,7 @@ char *strcpy(char * const POS p, const char * const POS q) __write_overflow(); /* Run-time check for dynamic size overflow. */ if (p_size < size) - fortify_panic(FORTIFY_FUNC_strcpy, FORTIFY_WRITE, p); + fortify_panic(FORTIFY_FUNC_strcpy, FORTIFY_WRITE, p_size, size, p); __underlying_memcpy(p, q, size); return p; } diff --git a/lib/fortify_kunit.c b/lib/fortify_kunit.c index f0accebeca02..493ec02dd5b3 100644 --- a/lib/fortify_kunit.c +++ b/lib/fortify_kunit.c @@ -17,8 +17,8 @@ /* Redefine fortify_panic() to track failures. */ void fortify_add_kunit_error(int write); -#define fortify_panic(func, write, retfail) do { \ - __fortify_report(FORTIFY_REASON(func, write)); \ +#define fortify_panic(func, write, avail, size, retfail) do { \ + __fortify_report(FORTIFY_REASON(func, write), avail, size); \ fortify_add_kunit_error(write); \ return (retfail); \ } while (0) diff --git a/lib/string_helpers.c b/lib/string_helpers.c index 5e53d42e32bb..5419282e12bd 100644 --- a/lib/string_helpers.c +++ b/lib/string_helpers.c @@ -1016,20 +1016,21 @@ static const char * const fortify_func_name[] = { #undef MAKE_FORTIFY_FUNC_NAME }; -void __fortify_report(const u8 reason) +void __fortify_report(const u8 reason, const size_t avail, const size_t size) { const u8 func = FORTIFY_REASON_FUNC(reason); const bool write = FORTIFY_REASON_DIR(reason); const char *name; name = fortify_func_name[umin(func, FORTIFY_FUNC_UNKNOWN)]; - WARN(1, "%s: detected buffer %s overflow\n", name, str_read_write(!write)); + WARN(1, "%s: detected buffer overflow: %zu byte %s of buffer size %zu\n", + name, size, str_read_write(!write), avail); } EXPORT_SYMBOL(__fortify_report); -void __fortify_panic(const u8 reason) +void __fortify_panic(const u8 reason, const size_t avail, const size_t size) { - __fortify_report(reason); + __fortify_report(reason, avail, size); BUG(); } EXPORT_SYMBOL(__fortify_panic);