From patchwork Thu Apr 6 00:02:00 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kees Cook X-Patchwork-Id: 13202661 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id C9739C7618D for ; Thu, 6 Apr 2023 00:02:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229737AbjDFACQ (ORCPT ); Wed, 5 Apr 2023 20:02:16 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47678 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229481AbjDFACP (ORCPT ); Wed, 5 Apr 2023 20:02:15 -0400 Received: from mail-pl1-x630.google.com (mail-pl1-x630.google.com [IPv6:2607:f8b0:4864:20::630]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 269F759D6 for ; Wed, 5 Apr 2023 17:02:15 -0700 (PDT) Received: by mail-pl1-x630.google.com with SMTP id ja10so35956239plb.5 for ; Wed, 05 Apr 2023 17:02:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1680739334; 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=yl4oifEwokFvb7UV+3PIm/IUduHGwu9hGuhtq+IOj7k=; b=c7cbXFjrLiWnvxHlZxDZRy65SnfcXBG7MdCPGJJ83MsAqWRNtFyns2n23kUsCLZ4hn 1T6sKpu2EQkDDkQ+m4lUJDgXXOtOk/VsL2gbb+1TkufqBn5kU5ZaUZYe+ThMfK7bqEFC IfqkVj//dOEjYyHc7kOeioK5PrTiEFgxpKWAc= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1680739334; 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=yl4oifEwokFvb7UV+3PIm/IUduHGwu9hGuhtq+IOj7k=; b=1RzdvGfaTQF2KhuyjDxjLM5/gVoPz/W/Hk4Dhjm0/wpcYT9cGWtwwbyNen/lSjn15S rt9fsKd29WCaNmsn/22kFBNH/85o9cFS9jL1olnj+PxNWsj1oLnugpf0EEnN06dADroT F6wHt8dCX/u3AnLyv/TsL4qCdLoxsRG9zl4g38+7iSmsbh5894o/7xMm7sOpsuihhpIK VMKu5vzAxSHjdtNSobIm/rdpRJ9ZmXBMX/GtZ7NxDHaCXebExZPs8cFuAcFrEoJbTpC9 jP1y3XsRlLxOh4j9o7zbfEqa8DqMoa/oLyiWhH0XfDDx8JM6b7HzA328EiqmQQeWylYx HvxA== X-Gm-Message-State: AAQBX9dpSuVOiQSI/l9jl/ssJh1JH9KzCFPkT5OH2YzFHzN4th6qZH/s LUFk98+t8Pf9a4vB8dIKdRqNpQ== X-Google-Smtp-Source: AKy350by97Nmv8II4GIg/KLwtBFp+ZTS1qz68AIVZTb5hpgaSt6w6+s7Fz3PZ7U3GS1rlbLGdfb4/w== X-Received: by 2002:a17:902:f685:b0:1a1:3320:be35 with SMTP id l5-20020a170902f68500b001a13320be35mr4569828plg.29.1680739334558; Wed, 05 Apr 2023 17:02:14 -0700 (PDT) Received: from www.outflux.net (198-0-35-241-static.hfc.comcastbusiness.net. [198.0.35.241]) by smtp.gmail.com with ESMTPSA id iy12-20020a170903130c00b0019aa5e0aadesm94957plb.110.2023.04.05.17.02.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 05 Apr 2023 17:02:13 -0700 (PDT) From: Kees Cook To: linux-hardening@vger.kernel.org Cc: Kees Cook , Kees Cook , Andy Shevchenko , Cezary Rojewski , Puyou Lu , Mark Brown , Josh Poimboeuf , Peter Zijlstra , Brendan Higgins , David Gow , Andrew Morton , Nathan Chancellor , Alexander Potapenko , Zhaoyang Huang , Randy Dunlap , Geert Uytterhoeven , Miguel Ojeda , Nick Desaulniers , Liam Howlett , Vlastimil Babka , Dan Williams , Rasmus Villemoes , Yury Norov , "Jason A. Donenfeld" , Sander Vanheule , Eric Biggers , "Masami Hiramatsu (Google)" , Andrey Konovalov , Linus Walleij , Daniel Latypov , =?utf-8?b?Sm9zw6kgRXhww7NzaXRv?= , linux-kernel@vger.kernel.org, kunit-dev@googlegroups.com Subject: [PATCH 1/9] kunit: tool: Enable CONFIG_FORTIFY_SOURCE under UML Date: Wed, 5 Apr 2023 17:02:00 -0700 Message-Id: <20230406000212.3442647-1-keescook@chromium.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230405235832.never.487-kees@kernel.org> References: <20230405235832.never.487-kees@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1288; i=keescook@chromium.org; h=from:subject; bh=vJg4ej+0zf5184Cn6hZT9ZDebanjHiE9mcbmpwHm1/Y=; b=owEBbQKS/ZANAwAKAYly9N/cbcAmAcsmYgBkLgv/xBxCluVnXo3PBfOHJyl251sCmrs8WnyVLVGG d0jLVMKJAjMEAAEKAB0WIQSlw/aPIp3WD3I+bhOJcvTf3G3AJgUCZC4L/wAKCRCJcvTf3G3AJioyD/ 45frMZpbUwAa/3Mzibgs8k057YB/OZAUl+jKPNwAm7gC4pvQX8tXYyGRlkhiJ1f4+gdXWDMUTws+Zb BtK5wLGDvCja19ksWutbgTiowgF3Uk3Ipzu1amZYOru+07rr9d1Oud0DGQPOxMqgTszbhRF9TjkR4k sCez3Ey9pylf9hHNakmuXr1RdGjlS4pysG1ELtNE82yUGf4T41/YHMspCYYUNI4Ww5KmaOX7blJxkV wqukEj2gAppyZPZAkUFKU59slkQLPUXP/VJGa7ZLCSEtVUxUNkf6tUBnQV4+NtkgQAffpTv40DHS0C bxGEkguixxvlv+m5yd2ONNaGfPSQGmcKn+JBgORFAo12/SOSPZ4tdiTguzhzDl4RQBlFvlpO9P33gG wuaHQt8MYZ937GrzO9nRpirP6ckub/I5mAwAHIMyeqb0fBn3/5EQrleyic/KAWZi3s+OgWVHl62rrq eUGR/yyXy//OEk5/9NvTUkzyjiRjY69IH/drENMObjunuJ8HUgSUdJfzycUmUjGbvjvnkRJ/uGW1L8 L9YKezQXJoANyGYQruQxZ/qq/CNLFiHWER4pDnXILHBDBsjIR8LuHIfHNtOYKyMf9tAjPMpEaoxAGw OUiWcRgLitBE2PJyEL+vPQgDLthqnJ/LYQRAKYmjRuWkQm1iKQeKd4oeQUEw== X-Developer-Key: i=keescook@chromium.org; a=openpgp; fpr=A5C3F68F229DD60F723E6E138972F4DFDC6DC026 Precedence: bulk List-ID: X-Mailing-List: linux-hardening@vger.kernel.org From: Kees Cook Since commit ba38961a069b ("um: Enable FORTIFY_SOURCE"), it's possible to run the FORTIFY tests under UML. Enable CONFIG_FORTIFY_SOURCE when running with --altests to gain additional coverage, and by default under UML. Signed-off-by: Kees Cook --- tools/testing/kunit/configs/all_tests.config | 2 ++ tools/testing/kunit/configs/arch_uml.config | 3 +++ 2 files changed, 5 insertions(+) diff --git a/tools/testing/kunit/configs/all_tests.config b/tools/testing/kunit/configs/all_tests.config index f990cbb73250..0393940c706a 100644 --- a/tools/testing/kunit/configs/all_tests.config +++ b/tools/testing/kunit/configs/all_tests.config @@ -9,6 +9,8 @@ CONFIG_KUNIT=y CONFIG_KUNIT_EXAMPLE_TEST=y CONFIG_KUNIT_ALL_TESTS=y +CONFIG_FORTIFY_SOURCE=y + CONFIG_IIO=y CONFIG_EXT4_FS=y diff --git a/tools/testing/kunit/configs/arch_uml.config b/tools/testing/kunit/configs/arch_uml.config index e824ce43b05a..54ad8972681a 100644 --- a/tools/testing/kunit/configs/arch_uml.config +++ b/tools/testing/kunit/configs/arch_uml.config @@ -3,3 +3,6 @@ # Enable virtio/pci, as a lot of tests require it. CONFIG_VIRTIO_UML=y CONFIG_UML_PCI_OVER_VIRTIO=y + +# Enable FORTIFY_SOURCE for wider checking. +CONFIG_FORTIFY_SOURCE=y From patchwork Thu Apr 6 00:02:01 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kees Cook X-Patchwork-Id: 13202688 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B2599C7618D for ; Thu, 6 Apr 2023 00:09:23 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232063AbjDFAJW (ORCPT ); Wed, 5 Apr 2023 20:09:22 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:56436 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234302AbjDFAI5 (ORCPT ); Wed, 5 Apr 2023 20:08:57 -0400 Received: from mail-pl1-x631.google.com (mail-pl1-x631.google.com [IPv6:2607:f8b0:4864:20::631]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id BFF0B134 for ; Wed, 5 Apr 2023 17:08:55 -0700 (PDT) Received: by mail-pl1-x631.google.com with SMTP id o11so35991788ple.1 for ; Wed, 05 Apr 2023 17:08:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1680739735; 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=PneGamL/mgm+LpVlf2vTq3MuyA+fcdySlbCCimj/eTM=; b=W4BgfyEWUsaBx+vivd2BLpkQi/WGqzeS+VVula1M+B91wAnaQaALuUOaa8hKiLd4nB LqiTn5zkTIB85QCcdYl/L/UwYX4Q39T5EvRlreapcfypsJR4buVcXpOQmpe9pyXP4ZTc K2VwuYVzEvRLWOcfVDr1/xS4PoWZgLKDLOg/s= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1680739735; 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=PneGamL/mgm+LpVlf2vTq3MuyA+fcdySlbCCimj/eTM=; b=ti5Vpi6UCQTtiRCaagDUgs3OcEr/u1qstBJVqbAWkvkaMW170wa93Pd+hX616xOn0A Ewh6EI0295aoti45Rhz3K2QyYRFV5d23uXh4ApQZjNAoYbXlTWtNCBw5TF2MDsuuKn/i Lr63Yyb9Lu3SjnL4F/ff5yn0b4utdm4b0xAvnTINKkaNeRXSqGnd/JGzCtAYqdu8d3iF mH5O1M/yjbgat6vL9kday/17vXim3e1K85Y1s/d0a1Ah4n90mreSr5fK4s2NT6fZVhJa /qwBa9gGwVqaoKuzcLvm/QP6roL7fckT8RkDRKPCIZQF6D8i36mVHCHPpcTD++Kp050X ijXQ== X-Gm-Message-State: AAQBX9dd62CImZ0k0Bq/dRnriJqq/nIlEERYR5BZuvKbMCkL+WzJkUPF I9M4N0IKSc3ZOHCNA21xoIgo0g== X-Google-Smtp-Source: AKy350bYrY3LXp+tLT2F2M6EDrbqW93W3G0pJujFJ65kTJO2hzuYucdaHMEdU7U+ZpyMcR9amj+Irg== X-Received: by 2002:a17:90b:4d05:b0:240:9e3d:d532 with SMTP id mw5-20020a17090b4d0500b002409e3dd532mr9052093pjb.8.1680739735192; Wed, 05 Apr 2023 17:08:55 -0700 (PDT) Received: from www.outflux.net (198-0-35-241-static.hfc.comcastbusiness.net. [198.0.35.241]) by smtp.gmail.com with ESMTPSA id kf7-20020a17090305c700b001a06b33923bsm93820plb.164.2023.04.05.17.08.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 05 Apr 2023 17:08:54 -0700 (PDT) From: Kees Cook To: linux-hardening@vger.kernel.org Cc: Kees Cook , Kees Cook , Andy Shevchenko , Cezary Rojewski , Puyou Lu , Mark Brown , Josh Poimboeuf , Peter Zijlstra , Brendan Higgins , David Gow , Andrew Morton , Nathan Chancellor , Alexander Potapenko , Zhaoyang Huang , Randy Dunlap , Geert Uytterhoeven , Miguel Ojeda , Nick Desaulniers , Liam Howlett , Vlastimil Babka , Dan Williams , Rasmus Villemoes , Yury Norov , "Jason A. Donenfeld" , Sander Vanheule , Eric Biggers , "Masami Hiramatsu (Google)" , Andrey Konovalov , Linus Walleij , Daniel Latypov , =?utf-8?b?Sm9zw6kgRXhww7NzaXRv?= , linux-kernel@vger.kernel.org, kunit-dev@googlegroups.com Subject: [PATCH 2/9] fortify: Allow KUnit test to build without FORTIFY Date: Wed, 5 Apr 2023 17:02:01 -0700 Message-Id: <20230406000212.3442647-2-keescook@chromium.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230405235832.never.487-kees@kernel.org> References: <20230405235832.never.487-kees@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=2019; i=keescook@chromium.org; h=from:subject; bh=SpdgFxHiZ4ARZf+4i1NeU7Y8FK+0OW7wyr+j9YHonQM=; b=owEBbQKS/ZANAwAKAYly9N/cbcAmAcsmYgBkLgv/+oSAYnNTJZYXyeczo+2pwrJiX+S998cIveOx dv7sKJaJAjMEAAEKAB0WIQSlw/aPIp3WD3I+bhOJcvTf3G3AJgUCZC4L/wAKCRCJcvTf3G3AJnxJEA CNJYzScMB7ymRkvO6c4S//HNJjuM0W5yyy0EvjjCUlx+L8sCNKXmdALc985icEfYzfD5K8vw1PRXX7 C8uiv3CUHvw2fVT6NTmm6CdrXNuNUcMHjoSEOkEAJFarAGJESKN4ypcJPmvyTozFIvWoKkftYd1KhH dobf/C5zPIXz88aI44knQFGTZE/z5uyP4dGtvp9Sf64lU4+MnfWbZkPE4MWUVuk7lnPpE5MEKQ1Yqe yI8e7XFRVNCDXgoY0ltDMizSrwjG5DyBONZA1/OrBczdieYgJ3/MycxAVBWNbG3QQpCW8iY0ALx1mo 8j2wQ6/IT7Xxyurhz/hcpPX9OqCnTz9gkf0lCX60Uju7iPFRC9G3wYJ2MTHVAbn4E0Vv3cWkZhmmpe AQPx6KAMIj09/u6UyAf2o6FP3j6K0vqCq1ZfSc5c2yNuoihFq8XOmeKkBZLFJ8tOnY7tdZcArLbrMA chRKxelhk3wW8wtvC22yNIlNpH8Y35VXuQeJea8Jarrr1kYRPPQENEMwzPEKb442KET5sIxAZtZQRw TUblyPfA+RF3czGsvA2osA4DnJqsXoSYOR3oZ44qJMvjsglAdBFVBkoK/znA2MOL4ocWT8N8/qcluK iWvxlRaHFVkdmNeGPO3FL7lUDKnxNB+LigII/C6c9r1SNNqxfRqz1GjaX08A== X-Developer-Key: i=keescook@chromium.org; a=openpgp; fpr=A5C3F68F229DD60F723E6E138972F4DFDC6DC026 Precedence: bulk List-ID: X-Mailing-List: linux-hardening@vger.kernel.org From: Kees Cook 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 --- lib/Kconfig.debug | 2 +- lib/fortify_kunit.c | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index c8b379e2e9ad..d48a5f4b471e 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -2614,7 +2614,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 c8c33cbaae9e..d054fc20a7d5 100644 --- a/lib/fortify_kunit.c +++ b/lib/fortify_kunit.c @@ -25,8 +25,21 @@ 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"; +/* Handle being built without CONFIG_FORTIFY_SOURCE */ +#ifndef __compiletime_strlen +# define __compiletime_strlen __builtin_strlen +#endif + +#define skip_without_fortify() \ +do { \ + if (!IS_ENABLED(CONFIG_FORTIFY_SOURCE)) \ + kunit_skip(test, "Not built with CONFIG_FORTIFY_SOURCE=y"); \ +} while (0) + static void known_sizes_test(struct kunit *test) { + skip_without_fortify(); + KUNIT_EXPECT_EQ(test, __compiletime_strlen("88888888"), 8); KUNIT_EXPECT_EQ(test, __compiletime_strlen(array_of_10), 10); KUNIT_EXPECT_EQ(test, __compiletime_strlen(ptr_of_11), 11); @@ -60,6 +73,8 @@ static noinline size_t want_minus_one(int pick) static void control_flow_split_test(struct kunit *test) { + skip_without_fortify(); + KUNIT_EXPECT_EQ(test, want_minus_one(pick), SIZE_MAX); } From patchwork Thu Apr 6 00:02:02 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kees Cook X-Patchwork-Id: 13202664 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 79158C761A6 for ; Thu, 6 Apr 2023 00:02:22 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232719AbjDFACU (ORCPT ); Wed, 5 Apr 2023 20:02:20 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47782 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229587AbjDFACT (ORCPT ); Wed, 5 Apr 2023 20:02:19 -0400 Received: from mail-pf1-x42b.google.com (mail-pf1-x42b.google.com [IPv6:2607:f8b0:4864:20::42b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E06135B92 for ; Wed, 5 Apr 2023 17:02:17 -0700 (PDT) Received: by mail-pf1-x42b.google.com with SMTP id bm13so10636227pfb.5 for ; Wed, 05 Apr 2023 17:02:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1680739337; 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=ihHu/77nnwOlD0yIDOkgSICbQg4UYxIlXHTf7scrixI=; b=C+wnnFr1hwIr9HKbn6ntv8TFTvMwzfAwoDQnZM0sBfu9WX+uzyklLEx4XcRwUridjf /g3NAUcd1BBTIaKOaILN+Hr5Ysj2wpFac9yWZZ1XUWEjJBeUCaAmm/SvaJb7Qzd3DWI+ Fc9fqIxEG0Gjo9RQFjgPkvsQe4gYKXJmbZ2kk= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1680739337; 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=ihHu/77nnwOlD0yIDOkgSICbQg4UYxIlXHTf7scrixI=; b=dhQCnujGL3LbfrAgfybibn0WiJNqqtTNWdvMhLr+WyZ9bZl5bcF66MJB/nBC5cOVIQ ufCnbDsJO7T++e/vAWVTO1sL+38+peK1dWrnKh/p1jXXlxarMMp1wwEfDxxnJ9KqAHwN fTESQOjek8i3wH82OVGk5oryh1yk6s65sJwHwFhHudxD+2fWnVaka3jKBMHUBhTVUkEQ laCAvaqQOj8pemcxxNVKtGIi9gk3uElH/MplVSiMMX85SgYQsdaGaZ/Ob4J7oj/uVyYo NY9fwvvGCQW9e549AtBHa6Yb4IGbZXgT+XtRLLddnFvtEYt0GextZXKBfFU5PnnUr+et P1kg== X-Gm-Message-State: AAQBX9eETuMIldybIPh77+3tYtTGjwyt36RtppyG3uwiemDvKu13u2BL LggnWel8JtEuzJFUoxswf3vdgw== X-Google-Smtp-Source: AKy350bu+D5tm8a2pBp6SySsMEwThkWPnl59XxSW4bom/2xnxgS0kO4ReDQvA0MNoBkL8lT7ZGafPg== X-Received: by 2002:aa7:9423:0:b0:627:8e40:68d8 with SMTP id y3-20020aa79423000000b006278e4068d8mr7319771pfo.18.1680739337281; Wed, 05 Apr 2023 17:02:17 -0700 (PDT) Received: from www.outflux.net (198-0-35-241-static.hfc.comcastbusiness.net. [198.0.35.241]) by smtp.gmail.com with ESMTPSA id n9-20020aa79049000000b00625e885a6ffsm11635623pfo.18.2023.04.05.17.02.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 05 Apr 2023 17:02:13 -0700 (PDT) From: Kees Cook To: linux-hardening@vger.kernel.org Cc: Kees Cook , Kees Cook , Andy Shevchenko , Cezary Rojewski , Puyou Lu , Mark Brown , Josh Poimboeuf , Peter Zijlstra , Brendan Higgins , David Gow , Andrew Morton , Nathan Chancellor , Alexander Potapenko , Zhaoyang Huang , Randy Dunlap , Geert Uytterhoeven , Miguel Ojeda , Nick Desaulniers , Liam Howlett , Vlastimil Babka , Dan Williams , Rasmus Villemoes , Yury Norov , "Jason A. Donenfeld" , Sander Vanheule , Eric Biggers , "Masami Hiramatsu (Google)" , Andrey Konovalov , Linus Walleij , Daniel Latypov , =?utf-8?b?Sm9zw6kgRXhww7NzaXRv?= , linux-kernel@vger.kernel.org, kunit-dev@googlegroups.com Subject: [PATCH 3/9] string: Add Kunit tests for strcat() family Date: Wed, 5 Apr 2023 17:02:02 -0700 Message-Id: <20230406000212.3442647-3-keescook@chromium.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230405235832.never.487-kees@kernel.org> References: <20230405235832.never.487-kees@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=5480; i=keescook@chromium.org; h=from:subject; bh=TwKlXl0mlDA9aUwwDPbJaiGBGoVKgl4jR+SCssFeKos=; b=owEBbQKS/ZANAwAKAYly9N/cbcAmAcsmYgBkLgv/w9lYG8TKO2pLdSGWjeJdz4gWEQKB8J8NlI2U lCXR/oeJAjMEAAEKAB0WIQSlw/aPIp3WD3I+bhOJcvTf3G3AJgUCZC4L/wAKCRCJcvTf3G3AJkrPD/ 4tWkIiWa0xJaDvh14pSs0DPg5BaVbTIgpacy939WxRJYFB5IqVFbROCHrjrYPvw3RbO6Ur7PXfzA83 oVE0L4cdpb8ja3B7YicriDJnvI9DGSOytYgPGFOLXEzhgtgMywfa1RGUMu8xzJuX5GDyXoWD/0iNva MwIa+kUhPmik2QkyZBjjeBkryhnkaWcvn6so+24JKCPwvrnNEUpCGHFqG1QkM8KYF/WLBipLNCKopu RVYonEVOmVAjppym0p8STjAGhyJwrdOX6gizbCAoxgx74o1s3YQudGTloGoNOUvkrBtxOj66rFnFvj K4SWyZcQNeK7+W6hU2WHGeLiYMCVuqgyaeY+TKWfskT0fFbzjpAEp/VKsXFU0+8kwY4w09fYIreqhK Hpd24C9DQEbuc53RJ3gjcKjCHTrDgfIBn3lh8truyL/r68oF9npR5sgw8gsj2qM9GsM4T4iOCjmh/a AGKFFNNxtgNEbVQgiYdiFcDOMzI3rAHSyn7Bks/TVznBw75Jmlni9DddObi51AR39rGooGMB47Hh0c HWt6CsKuw3zBZIdfS/QrNvsx1Kufr+Xlt4edUaxBXh4kJGif10BfKXIqQjnUrJrpI9lYfw701wB7lo WI58U+HyG+iORfD+4byCR0LnU1t7yxCe6NnjLehv6DvuMISjiqxfBJQwj2oQ== X-Developer-Key: i=keescook@chromium.org; a=openpgp; fpr=A5C3F68F229DD60F723E6E138972F4DFDC6DC026 Precedence: bulk List-ID: X-Mailing-List: linux-hardening@vger.kernel.org From: Kees Cook Add tests to make sure the strcat() family of functions behave correctly. Signed-off-by: Kees Cook --- MAINTAINERS | 1 + lib/Kconfig.debug | 5 +++ lib/Makefile | 1 + lib/strcat_kunit.c | 100 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 107 insertions(+) create mode 100644 lib/strcat_kunit.c diff --git a/MAINTAINERS b/MAINTAINERS index ec57c42ed544..86c0012b5130 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8021,6 +8021,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/har F: include/linux/fortify-string.h F: lib/fortify_kunit.c F: lib/memcpy_kunit.c +F: lib/strcat_kunit.c F: lib/strscpy_kunit.c F: lib/test_fortify/* F: scripts/test_fortify.sh diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index d48a5f4b471e..86157aa5e979 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -2631,6 +2631,11 @@ config HW_BREAKPOINT_KUNIT_TEST If unsure, say N. +config STRCAT_KUNIT_TEST + tristate "Test strcat() family of functions at runtime" if !KUNIT_ALL_TESTS + depends on KUNIT + default KUNIT_ALL_TESTS + config STRSCPY_KUNIT_TEST tristate "Test strscpy*() family of functions at runtime" if !KUNIT_ALL_TESTS depends on KUNIT diff --git a/lib/Makefile b/lib/Makefile index baf2821f7a00..6582d8fe1a77 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -389,6 +389,7 @@ obj-$(CONFIG_STACKINIT_KUNIT_TEST) += stackinit_kunit.o CFLAGS_fortify_kunit.o += $(call cc-disable-warning, unsequenced) CFLAGS_fortify_kunit.o += $(DISABLE_STRUCTLEAK_PLUGIN) obj-$(CONFIG_FORTIFY_KUNIT_TEST) += fortify_kunit.o +obj-$(CONFIG_STRCAT_KUNIT_TEST) += strcat_kunit.o obj-$(CONFIG_STRSCPY_KUNIT_TEST) += strscpy_kunit.o obj-$(CONFIG_SIPHASH_KUNIT_TEST) += siphash_kunit.o diff --git a/lib/strcat_kunit.c b/lib/strcat_kunit.c new file mode 100644 index 000000000000..b6428c3a557f --- /dev/null +++ b/lib/strcat_kunit.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Kernel module for testing 'strcat' family of functions. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include + +static void strcat_test(struct kunit *test) +{ + char dest[8]; + + /* Destination is terminated. */ + memset(dest, 0, sizeof(dest)); + KUNIT_EXPECT_EQ(test, strlen(dest), 0); + /* Empty copy does nothing. */ + KUNIT_EXPECT_TRUE(test, strcat(dest, "") == dest); + KUNIT_EXPECT_STREQ(test, dest, ""); + /* 4 characters copied in, stops at %NUL. */ + KUNIT_EXPECT_TRUE(test, strcat(dest, "four\000123") == dest); + KUNIT_EXPECT_STREQ(test, dest, "four"); + KUNIT_EXPECT_EQ(test, dest[5], '\0'); + /* 2 more characters copied in okay. */ + KUNIT_EXPECT_TRUE(test, strcat(dest, "AB") == dest); + KUNIT_EXPECT_STREQ(test, dest, "fourAB"); +} + +static void strncat_test(struct kunit *test) +{ + char dest[8]; + + /* Destination is terminated. */ + memset(dest, 0, sizeof(dest)); + KUNIT_EXPECT_EQ(test, strlen(dest), 0); + /* Empty copy of size 0 does nothing. */ + KUNIT_EXPECT_TRUE(test, strncat(dest, "", 0) == dest); + KUNIT_EXPECT_STREQ(test, dest, ""); + /* Empty copy of size 1 does nothing too. */ + KUNIT_EXPECT_TRUE(test, strncat(dest, "", 1) == dest); + KUNIT_EXPECT_STREQ(test, dest, ""); + /* Copy of max 0 characters should do nothing. */ + KUNIT_EXPECT_TRUE(test, strncat(dest, "asdf", 0) == dest); + KUNIT_EXPECT_STREQ(test, dest, ""); + + /* 4 characters copied in, even if max is 8. */ + KUNIT_EXPECT_TRUE(test, strncat(dest, "four\000123", 8) == dest); + KUNIT_EXPECT_STREQ(test, dest, "four"); + KUNIT_EXPECT_EQ(test, dest[5], '\0'); + /* 2 characters copied in okay, 2 ignored. */ + KUNIT_EXPECT_TRUE(test, strncat(dest, "ABCD", 2) == dest); + KUNIT_EXPECT_STREQ(test, dest, "fourAB"); +} + +static void strlcat_test(struct kunit *test) +{ + char dest[8] = ""; + + /* Destination is terminated. */ + KUNIT_EXPECT_EQ(test, strlen(dest), 0); + /* Empty copy is size 0. */ + KUNIT_EXPECT_EQ(test, strlcat(dest, "", sizeof(dest)), 0); + KUNIT_EXPECT_STREQ(test, dest, ""); + /* Size 1 should keep buffer terminated, report size of source only. */ + KUNIT_EXPECT_EQ(test, strlcat(dest, "four", 1), 4); + KUNIT_EXPECT_STREQ(test, dest, ""); + + /* 4 characters copied in. */ + KUNIT_EXPECT_EQ(test, strlcat(dest, "four", sizeof(dest)), 4); + KUNIT_EXPECT_STREQ(test, dest, "four"); + /* 2 characters copied in okay, gets to 6 total. */ + KUNIT_EXPECT_EQ(test, strlcat(dest, "AB", sizeof(dest)), 6); + KUNIT_EXPECT_STREQ(test, dest, "fourAB"); + /* 2 characters ignored if max size (7) reached. */ + KUNIT_EXPECT_EQ(test, strlcat(dest, "CD", 7), 8); + KUNIT_EXPECT_STREQ(test, dest, "fourAB"); + /* 1 of 2 characters skipped, now at true max size. */ + KUNIT_EXPECT_EQ(test, strlcat(dest, "EFG", sizeof(dest)), 9); + KUNIT_EXPECT_STREQ(test, dest, "fourABE"); + /* Everything else ignored, now at full size. */ + KUNIT_EXPECT_EQ(test, strlcat(dest, "1234", sizeof(dest)), 11); + KUNIT_EXPECT_STREQ(test, dest, "fourABE"); +} + +static struct kunit_case strcat_test_cases[] = { + KUNIT_CASE(strcat_test), + KUNIT_CASE(strncat_test), + KUNIT_CASE(strlcat_test), + {} +}; + +static struct kunit_suite strcat_test_suite = { + .name = "strcat", + .test_cases = strcat_test_cases, +}; + +kunit_test_suite(strcat_test_suite); + +MODULE_LICENSE("GPL"); From patchwork Thu Apr 6 00:02:03 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kees Cook X-Patchwork-Id: 13202662 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 17FC3C7619A for ; Thu, 6 Apr 2023 00:02:20 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231530AbjDFACS (ORCPT ); Wed, 5 Apr 2023 20:02:18 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47718 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230527AbjDFACR (ORCPT ); Wed, 5 Apr 2023 20:02:17 -0400 Received: from mail-pj1-x1036.google.com (mail-pj1-x1036.google.com [IPv6:2607:f8b0:4864:20::1036]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9A0E859E3 for ; Wed, 5 Apr 2023 17:02:16 -0700 (PDT) Received: by mail-pj1-x1036.google.com with SMTP id om3-20020a17090b3a8300b0023efab0e3bfso41190626pjb.3 for ; Wed, 05 Apr 2023 17:02:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1680739336; 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=ICBa1DtI6i1NF0+JonXqQyqUlpz7dmxfeFViA4mHGno=; b=YwjTtcaHeXgmNQCy+UZfNoSOdXZjRkV1nS2FVwslb/8WY3CT6punFfDFo1A6SEXcEN qgBvZR+YGmwakCTv/2s10NaRAHXxmobJgDox8sh2B8NnrDVlMF7kykKRvSfJHrnDfnaU CrwurUlZu5Q5vMrJKVxFEIoCEymN3rFYQ3lAE= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1680739336; 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=ICBa1DtI6i1NF0+JonXqQyqUlpz7dmxfeFViA4mHGno=; b=S/SP+Dkklo9+TBiuAknZrjVBEEVtBO331P0m308b75T77vtyQlLSb3vUG50h89hOh6 ADLqBrzPx6tw95Ki69fhia9PH6BT/3S09UqBYjom2G2bqh8LnOsrVBlfp9qcbW2Hp4I3 Aj4Jojhrf771OVsjjXo7CrIe6MNPwXKZt905kxeN9ef3D/CHE2fwXI+s5OZmnMTz866W qPmrcTFAMtRUVbzZuMU6DCGK4sbsvghRlD6NyCIPnG5++a9CSkBvKAQgVANC+a1Y3P1X Cso2hMoBWx4kYrOnMQuGkWO1MTiZl8GsDpl8adwLlK0KdBZ+JSdjHHG2js4BGrpXPnzD hdfA== X-Gm-Message-State: AAQBX9d1kxpCcNC1MKHzya5+UVPfSgNaC6GE62BUGqctmt0cfyHlZ0pg DVOcDTAD2KIvO8Z2toMRI77B8A== X-Google-Smtp-Source: AKy350Z+Cz1lIx0yILCxOSuk2mkykSQoFslODV+JxiUeF7G2r//dE2JEl6b2FjPX2FxSt53YJBCWjA== X-Received: by 2002:a05:6a20:835d:b0:d6:c9e2:1795 with SMTP id z29-20020a056a20835d00b000d6c9e21795mr788533pzc.27.1680739335996; Wed, 05 Apr 2023 17:02:15 -0700 (PDT) Received: from www.outflux.net (198-0-35-241-static.hfc.comcastbusiness.net. [198.0.35.241]) by smtp.gmail.com with ESMTPSA id e5-20020a62ee05000000b005e099d7c30bsm11029461pfi.205.2023.04.05.17.02.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 05 Apr 2023 17:02:13 -0700 (PDT) From: Kees Cook To: linux-hardening@vger.kernel.org Cc: Kees Cook , Kees Cook , Andy Shevchenko , Cezary Rojewski , Puyou Lu , Mark Brown , Josh Poimboeuf , Peter Zijlstra , Brendan Higgins , David Gow , Andrew Morton , Nathan Chancellor , Alexander Potapenko , Zhaoyang Huang , Randy Dunlap , Geert Uytterhoeven , Miguel Ojeda , Nick Desaulniers , Liam Howlett , Vlastimil Babka , Dan Williams , Rasmus Villemoes , Yury Norov , "Jason A. Donenfeld" , Sander Vanheule , Eric Biggers , "Masami Hiramatsu (Google)" , Andrey Konovalov , Linus Walleij , Daniel Latypov , =?utf-8?b?Sm9zw6kgRXhww7NzaXRv?= , linux-kernel@vger.kernel.org, kunit-dev@googlegroups.com Subject: [PATCH 4/9] fortify: Add protection for strlcat() Date: Wed, 5 Apr 2023 17:02:03 -0700 Message-Id: <20230406000212.3442647-4-keescook@chromium.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230405235832.never.487-kees@kernel.org> References: <20230405235832.never.487-kees@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=3250; i=keescook@chromium.org; h=from:subject; bh=rj52x4JBej+qEjIHIHz74SGX8jVWOZs51u/oWt76v9o=; b=owEBbQKS/ZANAwAKAYly9N/cbcAmAcsmYgBkLgv//oEVXTENJ5N/QlhC1PcMOKe+fLOu/OWZjhUq cGEUJoqJAjMEAAEKAB0WIQSlw/aPIp3WD3I+bhOJcvTf3G3AJgUCZC4L/wAKCRCJcvTf3G3AJtbXD/ 0cKX83TNsTe99BigymaFdb/hrWnqygWzdMcLVPDMj6bXddgFVN05PUZvBqP0Jwk0Mq8PN+cVw1xnQK hhPe185jF6FVowhCd83A8Awzl37jFuXW3oIZnifmwtWfbrkV2NSpxlbyqqO1vdo0ZYVaCYCfiiyUql MMJn5QehjOT1vS6A+jJOPBaN9hyxiXjyUYtFvcwjbLXvSyU6plrXedx7Yw4T6Mc0rlBvcr19NfjVtx SBDX5WCJVrmYXhUQKAexomoOvuTHz7TW7gOBFNdtiVvHKOsM2QFnrcQ44xww1H1yZIeF21pWNK0Ure I2WsKEN9IDFqFN5sXbkRlKB5xgU3P3cO5wUw836+tvzaYwdYgrXCuMDD90mJ3kmNhj0NCdd8ugRoR9 xwidD22KGNV1BJA9dXt3yvPXn/p/zb5rgA2ltA+c+A2G17GmupmfOiRu+b/8PmYwyclrcAMVdMu7hr l5OyX3aP9Wy7l8plUlzY9SACEyxMyGShBhxL2VPBQRdQ3aOUdJ4Lx87cgMNGiP8UUNdUwd1PPuqGDF fVOmKgvKW7q4/GZZAwzD1UJJ9ByTmUdCbacrmlat93+NwU6D3/vpuhW5PYQVF6ZshQ7LFJvVU76sth fyZ5LHa4lq99coCOec13GLmFrtPJnP8d6IguezioiOVkUvn42/KNyEaccZiw== X-Developer-Key: i=keescook@chromium.org; a=openpgp; fpr=A5C3F68F229DD60F723E6E138972F4DFDC6DC026 Precedence: bulk List-ID: X-Mailing-List: linux-hardening@vger.kernel.org From: Kees Cook The definition of strcat() was was defined in terms of unfortified strlcat(), but that meant there was no bounds checking done on the internal strlen() calls, and the (bounded) copy would be performed before reporting a failure. Additionally, pathological cases (i.e. unterminated destination buffer) did not make calls to fortify_panic(), which will make future unit testing more difficult. Instead, explicitly define a fortified strlcat() wrapper for strcat() to use. Signed-off-by: Kees Cook --- include/linux/fortify-string.h | 64 ++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/include/linux/fortify-string.h b/include/linux/fortify-string.h index c9de1f59ee80..875689aa83c3 100644 --- a/include/linux/fortify-string.h +++ b/include/linux/fortify-string.h @@ -371,6 +371,70 @@ __FORTIFY_INLINE ssize_t strscpy(char * const POS p, const char * const POS q, s return __real_strscpy(p, q, len); } +/* Defined after fortified strlen() to reuse it. */ +extern size_t __real_strlcat(char *p, const char *q, size_t avail) __RENAME(strlcat); +/** + * strlcat - Append a string to an existing string + * + * @p: pointer to %NUL-terminated string to append to + * @q: pointer to %NUL-terminated string to append from + * @avail: Maximum bytes available in @p + * + * Appends %NUL-terminated string @q after the %NUL-terminated + * string at @p, but will not write beyond @avail bytes total, + * potentially truncating the copy from @q. @p will stay + * %NUL-terminated only if a %NUL already existed within + * the @avail bytes of @p. If so, the resulting number of + * bytes copied from @q will be at most "@avail - strlen(@p) - 1". + * + * Do not use this function. While FORTIFY_SOURCE tries to avoid + * read and write overflows, this is only possible when the sizes + * of @p and @q are known to the compiler. Prefer building the + * string with formatting, via scnprintf(), seq_buf, or similar. + * + * Returns total bytes that _would_ have been contained by @p + * regardless of truncation, similar to snprintf(). If return + * value is >= @avail, the string has been truncated. + * + */ +__FORTIFY_INLINE +size_t strlcat(char * const POS p, const char * const POS q, size_t avail) +{ + size_t p_len, copy_len; + size_t p_size = __member_size(p); + size_t q_size = __member_size(q); + size_t actual, wanted; + + /* Give up immediately if both buffer sizes are unknown. */ + if (p_size == SIZE_MAX && q_size == SIZE_MAX) + return __real_strlcat(p, q, avail); + + p_len = strnlen(p, avail); + copy_len = strlen(q); + wanted = actual = p_len + copy_len; + + /* Cannot append any more: report truncation. */ + if (avail <= p_len) + return wanted; + + /* Give up if string is already overflowed. */ + if (p_size <= p_len) + fortify_panic(__func__); + + if (actual >= avail) { + copy_len = avail - p_len - 1; + actual = p_len + copy_len; + } + + /* Give up if copy will overflow. */ + if (p_size <= actual) + fortify_panic(__func__); + __underlying_memcpy(p + p_len, q, copy_len); + p[actual] = '\0'; + + return wanted; +} + /** * strncat - Append a string to an existing string * From patchwork Thu Apr 6 00:02:04 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kees Cook X-Patchwork-Id: 13202665 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 15B7CC7619A for ; Thu, 6 Apr 2023 00:02:23 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232896AbjDFACV (ORCPT ); Wed, 5 Apr 2023 20:02:21 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47812 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232252AbjDFACT (ORCPT ); Wed, 5 Apr 2023 20:02:19 -0400 Received: from mail-pj1-x1036.google.com (mail-pj1-x1036.google.com [IPv6:2607:f8b0:4864:20::1036]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6DBD26E82 for ; Wed, 5 Apr 2023 17:02:18 -0700 (PDT) Received: by mail-pj1-x1036.google.com with SMTP id lr16-20020a17090b4b9000b0023f187954acso38953884pjb.2 for ; Wed, 05 Apr 2023 17:02:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1680739338; 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=ExS1X1Asjk++jX2/Mr+aGsXWXjGWAeASIa6ARPjpF0s=; b=KEWpEbnIM66/k6Fl8lCg8J+TrhHMvhMCQDYDaDVmogicejBB6916cUXGdx/RY09kcG hBmb95mrp2ZAvyKnwpzjCUiRTAl/+vYw5D3mc38QSjWeoBPBhN7v84E7UMtxWozTF2OJ sfIE6kWBGdOqf4Va5AZMZCWGF4srD5lcdldts= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1680739338; 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=ExS1X1Asjk++jX2/Mr+aGsXWXjGWAeASIa6ARPjpF0s=; b=PkPDCGHnd0npMFc6O4BMkvt4BsdwP49jwl8pubXr6NqgYryfkGs/kW9Za6G/W89KVw plGbvyvVIWjdxdA6aJ0azW4yNk64oNDQSCqijqa4dCtmfnWcvOutixLYgM3XGLVZZTuc XNO3etNI5oiKb7MR39WKDxTwkpcC1KWuFxJ2Fjke76qKnB4E/aw7tfU+zN3RJMOEGaBT dTDSZ7An6uDovYsR9rIDUVkz3gE++gFp3DA4e130YDsswNFXizlrlg35Vk+7+lN5zsXw o9GTyPEYeGhuZXzr62MyxsSyeWNIyWbjE7j8HTUCJf1ys71S4LOpQxwsM1p0gFmSX1+3 Qh8g== X-Gm-Message-State: AAQBX9c56sOi5coqnLjeYtUgzFBZbLNNZUIYEWIBLD7q/kmr4TRmby7d ncRWlbQtA9vyOcE/tFvGB6W3YQ== X-Google-Smtp-Source: AKy350YpahY9tQxkeh6meEYNw3t9u3+e9C0Er0CTpuKk8Ndi/YL+LO1EN4IEqvq0UCpr5wuMQM5dgw== X-Received: by 2002:a17:902:c94e:b0:1a0:65d3:bae4 with SMTP id i14-20020a170902c94e00b001a065d3bae4mr9699691pla.38.1680739337837; Wed, 05 Apr 2023 17:02:17 -0700 (PDT) Received: from www.outflux.net (198-0-35-241-static.hfc.comcastbusiness.net. [198.0.35.241]) by smtp.gmail.com with ESMTPSA id p9-20020a170902b08900b0019e8915b1b5sm94611plr.105.2023.04.05.17.02.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 05 Apr 2023 17:02:17 -0700 (PDT) From: Kees Cook To: linux-hardening@vger.kernel.org Cc: Kees Cook , Kees Cook , Andy Shevchenko , Cezary Rojewski , Puyou Lu , Mark Brown , Josh Poimboeuf , Peter Zijlstra , Brendan Higgins , David Gow , Andrew Morton , Nathan Chancellor , Alexander Potapenko , Zhaoyang Huang , Randy Dunlap , Geert Uytterhoeven , Miguel Ojeda , Nick Desaulniers , Liam Howlett , Vlastimil Babka , Dan Williams , Rasmus Villemoes , Yury Norov , "Jason A. Donenfeld" , Sander Vanheule , Eric Biggers , "Masami Hiramatsu (Google)" , Andrey Konovalov , Linus Walleij , Daniel Latypov , =?utf-8?b?Sm9zw6kgRXhww7NzaXRv?= , linux-kernel@vger.kernel.org, kunit-dev@googlegroups.com Subject: [PATCH 5/9] fortify: strcat: Move definition to use fortified strlcat() Date: Wed, 5 Apr 2023 17:02:04 -0700 Message-Id: <20230406000212.3442647-5-keescook@chromium.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230405235832.never.487-kees@kernel.org> References: <20230405235832.never.487-kees@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=2624; i=keescook@chromium.org; h=from:subject; bh=O4OjFtVJ1tD3TCO7wTWZXRQ046029J23gw/1XMQ1O9Q=; b=owEBbQKS/ZANAwAKAYly9N/cbcAmAcsmYgBkLgv/+JEBhkqs9VsEq7y+wgME8WyhveIf5l89VWz3 AvrwAtSJAjMEAAEKAB0WIQSlw/aPIp3WD3I+bhOJcvTf3G3AJgUCZC4L/wAKCRCJcvTf3G3AJniDD/ 0V5CRN6GGGJdY8FU0vXH9y7xdXiBbN5MFFeYHQoaCP7lqkYn1TRcvNrmtPkuQIGjZnGXbn+ldsUrvV jkxwJ8JpVpqfIC1ibhYHJ4o8YRe/uMFWzORpNOqmWTMoB/cei1+5afll5SMW1OjW1ch7EjDpYZXS1n WpLAB9D8ZQMcs6+gld3A3cB+CzR1IUQ6COJcvbOR8kV5iqWfXvsasBwPdwJiJHv/PYt6vaoKOrfOWg pfWcesAXe72Wmi2cV/+q0hCjv5+ayGB79Uc/VxtoaplGonA4rkJa9B7fywwIu5Ies8q3T5W4h3R2uX 5LdB8eqUuszkdjxxaVDGrNEUjqagVMNZnYpobfs+95DYhbu4gxBKfR+IZQW1KKogha4mJ8NJPDskUk Gr77XjOP8BCqdIodxcWGdaGaCYeIrcfTMu+c7j3xmoZLhxaVI29WqUkShAi46f20FIKxSLcHosBiuF T2tvZYSwpt/rC0gr135kL9xLZopQMxaSlKQUBXStie8QRRd2YsFNaxUxjUB3FMOqOY/dVqlRjnIdnh nHDMa2mwEohRoW7/IvyAeFHvR5GjE3R+PkfCSo0FnX2JGxTYmZqMCB5OWzdKgX7E1uzF80JFHJQNp+ 9DmWdlBGTPj0I+5Nwipfd7HfdVVNL5PTMVBo+TRgSWa+siRVBjLWKGBAQpIA== X-Developer-Key: i=keescook@chromium.org; a=openpgp; fpr=A5C3F68F229DD60F723E6E138972F4DFDC6DC026 Precedence: bulk List-ID: X-Mailing-List: linux-hardening@vger.kernel.org From: Kees Cook Move the definition of fortified strcat() to after strlcat() to use it for bounds checking. Signed-off-by: Kees Cook --- include/linux/fortify-string.h | 53 +++++++++++++++++----------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/include/linux/fortify-string.h b/include/linux/fortify-string.h index 875689aa83c3..41dbd641f55c 100644 --- a/include/linux/fortify-string.h +++ b/include/linux/fortify-string.h @@ -151,33 +151,6 @@ char *strncpy(char * const POS p, const char *q, __kernel_size_t size) return __underlying_strncpy(p, q, size); } -/** - * strcat - Append a string to an existing string - * - * @p: pointer to NUL-terminated string to append to - * @q: pointer to NUL-terminated source string to append from - * - * Do not use this function. While FORTIFY_SOURCE tries to avoid - * read and write overflows, this is only possible when the - * destination buffer size is known to the compiler. Prefer - * building the string with formatting, via scnprintf() or similar. - * At the very least, use strncat(). - * - * Returns @p. - * - */ -__FORTIFY_INLINE __diagnose_as(__builtin_strcat, 1, 2) -char *strcat(char * const POS p, const char *q) -{ - size_t p_size = __member_size(p); - - if (p_size == SIZE_MAX) - return __underlying_strcat(p, q); - if (strlcat(p, q, p_size) >= p_size) - fortify_panic(__func__); - return p; -} - extern __kernel_size_t __real_strnlen(const char *, __kernel_size_t) __RENAME(strnlen); /** * strnlen - Return bounded count of characters in a NUL-terminated string @@ -435,6 +408,32 @@ size_t strlcat(char * const POS p, const char * const POS q, size_t avail) return wanted; } +/* Defined after fortified strlcat() to reuse it. */ +/** + * strcat - Append a string to an existing string + * + * @p: pointer to NUL-terminated string to append to + * @q: pointer to NUL-terminated source string to append from + * + * Do not use this function. While FORTIFY_SOURCE tries to avoid + * read and write overflows, this is only possible when the + * destination buffer size is known to the compiler. Prefer + * building the string with formatting, via scnprintf() or similar. + * At the very least, use strncat(). + * + * Returns @p. + * + */ +__FORTIFY_INLINE __diagnose_as(__builtin_strcat, 1, 2) +char *strcat(char * const POS p, const char *q) +{ + size_t p_size = __member_size(p); + + if (strlcat(p, q, p_size) >= p_size) + fortify_panic(__func__); + return p; +} + /** * strncat - Append a string to an existing string * From patchwork Thu Apr 6 00:02:05 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kees Cook X-Patchwork-Id: 13202669 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B59A4C7618D for ; Thu, 6 Apr 2023 00:02:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234233AbjDFACs (ORCPT ); Wed, 5 Apr 2023 20:02:48 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47822 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233294AbjDFACX (ORCPT ); Wed, 5 Apr 2023 20:02:23 -0400 Received: from mail-pl1-x635.google.com (mail-pl1-x635.google.com [IPv6:2607:f8b0:4864:20::635]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6A8097685 for ; Wed, 5 Apr 2023 17:02:19 -0700 (PDT) Received: by mail-pl1-x635.google.com with SMTP id f22so31833639plr.0 for ; Wed, 05 Apr 2023 17:02:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1680739339; 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=GVeZITSFOSVosu4yNU0kSm2zQr6awWpiVL1awBqfF1Y=; b=B/TOjH/gJOJvYf37Vu/+Cs/gN7vffw64dSKGAky15XutUioGX5f8rLUB9TzZ4m3Ufj XexxluTFeBPrRAqfB7h5gMNZIl95Kgy77dUpp5Yk913Cx8biwmbNpK95u1Xufd4OlsLU +cxukVTpxvXiAZJx8YEOP64dJ5NbP/bw2zVcc= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1680739339; 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=GVeZITSFOSVosu4yNU0kSm2zQr6awWpiVL1awBqfF1Y=; b=xUuGbpUjdYakRknuSk8c1NN/NCCs6+bkkKbJhBrlFjJI6xF+FOwVUgtdRzhdDV3bhM yVtxrT4j8PsmSRj8c7PZ6jC2tJZb5uAyEGwOSbCXz50w8kXCm+6/G4xanLpFilUJhJIM yXORXqw/VdPe1imn6NmgjMYoIhp+2f30tEefWkKLOe8RrB0v3NX8QMYYv6CTiIdMW4uv Gy3G82Tvm+lj1fgQ3tgXp4jvZ0qFf7sBbA1ON/5902q/g9ktnIEgOdBZPgsDOZl5Zn0c X3tU6/r9Uxr32BHUqKNxGm7F72Axtmg1mPgcGV8BEdSt5E/BlVvNIIi8S/6mWfxg5Avy DhMw== X-Gm-Message-State: AAQBX9dXpSyM8WGPT76JcYM+DRQiKAOBajMHA2upCWZvYGxLnaAU8u2h e/G/PIoA6Rhc/bNkeDguMM/5Jg== X-Google-Smtp-Source: AKy350Zu4aYpBrViFItjynucrhtslV0Npr6rpnlclbLBmZBOoQobu3mUEtgBTTXVCYiPsCQfgeXZIg== X-Received: by 2002:a17:902:f544:b0:1a1:b8cc:59da with SMTP id h4-20020a170902f54400b001a1b8cc59damr9433799plf.33.1680739338883; Wed, 05 Apr 2023 17:02:18 -0700 (PDT) Received: from www.outflux.net (198-0-35-241-static.hfc.comcastbusiness.net. [198.0.35.241]) by smtp.gmail.com with ESMTPSA id y4-20020a1709027c8400b001a04d37a4acsm106056pll.9.2023.04.05.17.02.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 05 Apr 2023 17:02:17 -0700 (PDT) From: Kees Cook To: linux-hardening@vger.kernel.org Cc: Kees Cook , Andy Shevchenko , Cezary Rojewski , Puyou Lu , Mark Brown , Josh Poimboeuf , Peter Zijlstra , Brendan Higgins , David Gow , Andrew Morton , Nathan Chancellor , Alexander Potapenko , Zhaoyang Huang , Randy Dunlap , Geert Uytterhoeven , Miguel Ojeda , Nick Desaulniers , Liam Howlett , Vlastimil Babka , Dan Williams , Rasmus Villemoes , Yury Norov , "Jason A. Donenfeld" , Sander Vanheule , Eric Biggers , "Masami Hiramatsu (Google)" , Andrey Konovalov , Linus Walleij , Daniel Latypov , =?utf-8?b?Sm9zw6kgRXhww7NzaXRv?= , linux-kernel@vger.kernel.org, kunit-dev@googlegroups.com Subject: [PATCH 6/9] fortify: Split reporting and avoid passing string pointer Date: Wed, 5 Apr 2023 17:02:05 -0700 Message-Id: <20230406000212.3442647-6-keescook@chromium.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230405235832.never.487-kees@kernel.org> References: <20230405235832.never.487-kees@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=11820; h=from:subject; bh=unK9/F7S4FUxqEzGNDbeCYLXq18RsKeeVFU9xRkUXsI=; b=owEBbQKS/ZANAwAKAYly9N/cbcAmAcsmYgBkLgv/Wwi9CD4BEpJoI75TveBPXRcut2QdfsP7sq0t vflFcsmJAjMEAAEKAB0WIQSlw/aPIp3WD3I+bhOJcvTf3G3AJgUCZC4L/wAKCRCJcvTf3G3AJhDAD/ 9+VFjHhYUtmkAtvm/ml4DQSaa6P/0Y+N6+4FhTbbGHVKigx+qRFJV4RRq6kasXh3ZzA8EHhUq7fbTp Iec2D1Nlyex5HIwNr+118srvlS2SZUOnEQxpyu9IBzmPdX7fMiwJ38qypqUkcuRNwapFb7Ppy4nCyX 0o+0bqQRv3cX5txcZXrJL0lNw6xn4Ci142qhSQNAhJ8Fuu3ITrrXSVtxDc53J42BoBpEYGEz4f9klT s4kjKEbhdcefB3fkY1S6s/b7pfcbGDzQYoWyERPI7u3dMGVW7Tw7qn/yeDbX0uJPpXAztjQdFlKwln wm2twRnu5aSFU082MWwjMvy+5Ld7f8ZJmsUfYGTTSbft17ac4Qi+Gqy0gyOkktm57sNpZ/tlGauNzG TkFSzIFmnpQyyM4aRjRHpPF7LgCdoC6shVaF1JkTuqUhJrJMjYtKPAB2QcDm86lXypAl9TWjyj8/tO 8r8a9Ob782ODDAiOlGHlrHzQV0/UxbDRz1hlOpiwsqew89XDt2D6w0eEdd1Bq5MHXBWWjBnc7n7roV v+aD1CE5viiZHAWgjCsRQYHTFDvkxBRblO5zW7VcsnkimaRSd8/g8f9kSqUXnm/ScbKAS6RVcPzhRG R81/JwTKZanUtzfC5kSl/oR43vWw9l9GoMc9K5LmNzgGs4FM2hVXxrcTbV8w== X-Developer-Key: i=keescook@chromium.org; a=openpgp; fpr=A5C3F68F229DD60F723E6E138972F4DFDC6DC026 Precedence: bulk List-ID: X-Mailing-List: linux-hardening@vger.kernel.org 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 int argument. This mainly ends up saving some 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 Cc: Andy Shevchenko Cc: Cezary Rojewski Cc: Puyou Lu Cc: Mark Brown Cc: linux-hardening@vger.kernel.org Signed-off-by: Kees Cook --- include/linux/fortify-string.h | 72 +++++++++++++++++++++++----------- lib/string_helpers.c | 70 +++++++++++++++++++++++++++++++-- tools/objtool/check.c | 2 +- 3 files changed, 118 insertions(+), 26 deletions(-) diff --git a/include/linux/fortify-string.h b/include/linux/fortify-string.h index 41dbd641f55c..6db4052db459 100644 --- a/include/linux/fortify-string.h +++ b/include/linux/fortify-string.h @@ -9,7 +9,34 @@ #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(func, write) (((func) << 1) | !!(write)) + +#define fortify_panic(func, write) \ + __fortify_panic(fortify_reason(func, write)) + +#define FORTIFY_READ 0 +#define FORTIFY_WRITE 1 + +#define FORTIFY_FUNC_strncpy 0 +#define FORTIFY_FUNC_strnlen 1 +#define FORTIFY_FUNC_strlen 2 +#define FORTIFY_FUNC_strlcpy 3 +#define FORTIFY_FUNC_strscpy 4 +#define FORTIFY_FUNC_strlcat 5 +#define FORTIFY_FUNC_strcat 6 +#define FORTIFY_FUNC_strncat 7 +#define FORTIFY_FUNC_memset 8 +#define FORTIFY_FUNC_memcpy 9 +#define FORTIFY_FUNC_memmove 10 +#define FORTIFY_FUNC_memscan 11 +#define FORTIFY_FUNC_memcmp 12 +#define FORTIFY_FUNC_memchr 13 +#define FORTIFY_FUNC_memchr_inv 14 +#define FORTIFY_FUNC_kmemdup 15 +#define FORTIFY_FUNC_strcpy 16 + +void __fortify_report(u8 reason); +void __fortify_panic(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()?"); @@ -147,7 +174,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); } @@ -178,7 +205,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; } @@ -214,7 +241,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; } @@ -256,7 +283,7 @@ __FORTIFY_INLINE size_t strlcpy(char * const POS p, const char * const POS q, si } if (size) { if (len >= p_size) - fortify_panic(__func__); + fortify_panic(FORTIFY_FUNC_strlcpy, FORTIFY_WRITE); __underlying_memcpy(p, q, len); p[len] = '\0'; } @@ -334,7 +361,7 @@ __FORTIFY_INLINE ssize_t strscpy(char * const POS p, const char * const POS q, s * 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: @@ -392,7 +419,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; @@ -401,7 +428,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'; @@ -430,7 +457,7 @@ char *strcat(char * const POS p, const char *q) 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; } @@ -466,7 +493,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; @@ -507,7 +534,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) ({ \ @@ -561,7 +588,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)) { /* @@ -605,9 +632,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. @@ -640,7 +668,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__ ":" __stringify(__LINE__), \ @@ -707,7 +735,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); } @@ -724,7 +752,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); } @@ -736,7 +764,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); } @@ -748,7 +776,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); } @@ -761,7 +789,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); } @@ -798,7 +826,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 230020a2e076..631c50657096 100644 --- a/lib/string_helpers.c +++ b/lib/string_helpers.c @@ -1021,10 +1021,74 @@ 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) +void __fortify_report(u8 reason) { - pr_emerg("detected buffer overflow in %s\n", name); + const char *name; + const bool write = !!(reason & 0x1); + + switch (reason >> 1) { + case FORTIFY_FUNC_strncpy: + name = "strncpy"; + break; + case FORTIFY_FUNC_strnlen: + name = "strnlen"; + break; + case FORTIFY_FUNC_strlen: + name = "strlen"; + break; + case FORTIFY_FUNC_strlcpy: + name = "strlcpy"; + break; + case FORTIFY_FUNC_strscpy: + name = "strscpy"; + break; + case FORTIFY_FUNC_strlcat: + name = "strlcat"; + break; + case FORTIFY_FUNC_strcat: + name = "strcat"; + break; + case FORTIFY_FUNC_strncat: + name = "strncat"; + break; + case FORTIFY_FUNC_memset: + name = "memset"; + break; + case FORTIFY_FUNC_memcpy: + name = "memcpy"; + break; + case FORTIFY_FUNC_memmove: + name = "memmove"; + break; + case FORTIFY_FUNC_memscan: + name = "memscan"; + break; + case FORTIFY_FUNC_memcmp: + name = "memcmp"; + break; + case FORTIFY_FUNC_memchr: + name = "memchr"; + break; + case FORTIFY_FUNC_memchr_inv: + name = "memchr_inv"; + break; + case FORTIFY_FUNC_kmemdup: + name = "kmemdup"; + break; + case FORTIFY_FUNC_strcpy: + name = "strcpy"; + break; + default: + name = "unknown"; + } + WARN(1, "%s: detected buffer %s overflow\n", name, write ? "write" : "read"); +} +EXPORT_SYMBOL(__fortify_report); + +void __fortify_panic(const u8 reason) +{ + __fortify_report(reason); BUG(); } -EXPORT_SYMBOL(fortify_panic); +EXPORT_SYMBOL(__fortify_panic); #endif /* CONFIG_FORTIFY_SOURCE */ diff --git a/tools/objtool/check.c b/tools/objtool/check.c index f937be1afe65..2d0a67ce1c51 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -197,6 +197,7 @@ static bool __dead_end_function(struct objtool_file *file, struct symbol *func, * attribute isn't provided in ELF data. Keep 'em sorted. */ static const char * const global_noreturns[] = { + "__fortify_panic", "__invalid_creds", "__module_put_and_kthread_exit", "__reiserfs_panic", @@ -208,7 +209,6 @@ static bool __dead_end_function(struct objtool_file *file, struct symbol *func, "do_group_exit", "do_task_dead", "ex_handler_msr_mce", - "fortify_panic", "kthread_complete_and_exit", "kthread_exit", "kunit_try_catch_throw", From patchwork Thu Apr 6 00:02:06 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kees Cook X-Patchwork-Id: 13202666 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 715F5C7618D for ; Thu, 6 Apr 2023 00:02:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233463AbjDFACX (ORCPT ); Wed, 5 Apr 2023 20:02:23 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47822 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232605AbjDFACU (ORCPT ); Wed, 5 Apr 2023 20:02:20 -0400 Received: from mail-pl1-x636.google.com (mail-pl1-x636.google.com [IPv6:2607:f8b0:4864:20::636]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id BA55C72B5 for ; Wed, 5 Apr 2023 17:02:18 -0700 (PDT) Received: by mail-pl1-x636.google.com with SMTP id ix20so35972687plb.3 for ; Wed, 05 Apr 2023 17:02:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1680739338; 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=fe5ivn1TRo1YQZGpjtcwb12k/6+IjegXd9dcvsAQqpo=; b=Eieac6hsjf/ZtZ6plrcl1dl/HZP04XmleXkBFTI8obqP7vSWmG64v4kacbMCs8dpWt go4DkRkXiLIgYWKgo9aqhUljI7kk5PM9t65E4/tHOpurAAv4no0mFm6pa3zp6QANyXKf Ua3KKUxFXnPibYqx9V1JXt7QQvw0ItwgQNNB0= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1680739338; 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=fe5ivn1TRo1YQZGpjtcwb12k/6+IjegXd9dcvsAQqpo=; b=7Kz8loMsdc9SIACap09MMH2Oe7ZTN9fLQZoxyd/JpeR5OHrZ9WE3gV2Y9aVn2y3a7T tlbYXN29pcACJnh3KxhP2+TaXutmBVTyL+L/aEr/EnsTks73f1BMQXXcFdqU1JSBxNea uGS536B74Z9ZHw1lMQ9JHc06HTVc96oF8YeTtABha0b6QX8xkuoPTKrrkWHwToVtlWk/ jX7CosyjMnCuP8SzdgCYNASpHDqiq/1GRm5pxQR9K++yR8EPKbZh/azkEkuEJT9ZcVdV eu9JBqfRgOqDkdobCFGwtJeMd/MJAP/w7+TtePUd1mQ/nm81DKb3eveoPZoX0MezrKND 2H7g== X-Gm-Message-State: AAQBX9f1lyUSj8Mom1OIgf9Uo4hTVR4ApY4Eq9wJDSY8m6/LgosEPsXV s2yd6EgEGLqnZeKV3uGqpEs1Ow== X-Google-Smtp-Source: AKy350aomOqDqvXhNunTGZYDcgBayMXIVPMHGOPLs/CQ7Cm2Nz82I5oLxglulGszlyVNWhM7GqGS0w== X-Received: by 2002:a17:903:2306:b0:1a0:4046:23f2 with SMTP id d6-20020a170903230600b001a0404623f2mr9242650plh.56.1680739338311; Wed, 05 Apr 2023 17:02:18 -0700 (PDT) Received: from www.outflux.net (198-0-35-241-static.hfc.comcastbusiness.net. [198.0.35.241]) by smtp.gmail.com with ESMTPSA id a18-20020a170902b59200b0019f1264c7d7sm94897pls.103.2023.04.05.17.02.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 05 Apr 2023 17:02:17 -0700 (PDT) From: Kees Cook To: linux-hardening@vger.kernel.org Cc: Kees Cook , Kees Cook , Andy Shevchenko , Cezary Rojewski , Puyou Lu , Mark Brown , Josh Poimboeuf , Peter Zijlstra , Brendan Higgins , David Gow , Andrew Morton , Nathan Chancellor , Alexander Potapenko , Zhaoyang Huang , Randy Dunlap , Geert Uytterhoeven , Miguel Ojeda , Nick Desaulniers , Liam Howlett , Vlastimil Babka , Dan Williams , Rasmus Villemoes , Yury Norov , "Jason A. Donenfeld" , Sander Vanheule , Eric Biggers , "Masami Hiramatsu (Google)" , Andrey Konovalov , Linus Walleij , Daniel Latypov , =?utf-8?b?Sm9zw6kgRXhww7NzaXRv?= , linux-kernel@vger.kernel.org, kunit-dev@googlegroups.com Subject: [PATCH 7/9] fortify: Provide KUnit counters for failure testing Date: Wed, 5 Apr 2023 17:02:06 -0700 Message-Id: <20230406000212.3442647-7-keescook@chromium.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230405235832.never.487-kees@kernel.org> References: <20230405235832.never.487-kees@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=10804; i=keescook@chromium.org; h=from:subject; bh=sfFlPo/mYV5VxdWixlpvcwAc+CtRK0pyQL7GL4KflJE=; b=owEBbQKS/ZANAwAKAYly9N/cbcAmAcsmYgBkLgv/qRZxzYzlqXt/wBRNW5IQR7wBwCY6ToiHUJ3z CNBFVL+JAjMEAAEKAB0WIQSlw/aPIp3WD3I+bhOJcvTf3G3AJgUCZC4L/wAKCRCJcvTf3G3AJrLzEA CzZ2FJuTA4dSd+rwj+KZD5cCIco6tJp5KYGB1dXhNiIWkEElDOF1wuQdwEiexK9jwUJZoD6YmdRSU8 TMtyStxj/IF6hySQyYpNkxeNyRDZjdWxWj7ABcr+5FfY3xQxPBuKizXW13+QGrBLDju6jSAay/CleP fvXvsDLWKCGgN/Hwy1D6va8eSbDV1I4w9hkKOyOLXp8OyxSQ0RZPVijwC2eCr+EV5oeLKWKq8IL1mq eQq9nCvh1wx3IEBpSilz2776YQ7iAe1TiAWUID4L0xxgh02Nx4aakR6YAh4vq+7/5KmW5UVMJ3BvBI WB3RTHOeMpwNGAgHkAlLRN7SsQ13raE7svGvgWDAOvEkXvtIYx/BSib6r/5SmsaRg2gPB6SGluJS94 6lYilIjUMchmfbcFQqqbX4ze2LFzE4fsm/mxTNkyfu2z3EjbRjJp5xsdUpgl9YM6FxvL6/P6oU8JVF tV10AlH83wkqVa3N54CRjBmIKZrMxNtmNpN4I83Lgh0buZDlYs85JGDsyDKiPxUasoVSedJxIpcZ5u 1Qvq3JQMetSjq+GFrMr/L+ax8uG8rmcl6Ro39BPGplzrR+/2oTIE8/cBeyPLLgOqZDFI1nJYytd89j RP74yoh1iJxniMF6OM0iAqm73tnaArMMeuFt61Nf2lHa5jOeFcIp5l0enxFg== X-Developer-Key: i=keescook@chromium.org; a=openpgp; fpr=A5C3F68F229DD60F723E6E138972F4DFDC6DC026 Precedence: bulk List-ID: X-Mailing-List: linux-hardening@vger.kernel.org From: Kees Cook 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 for 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 --- include/linux/fortify-string.h | 45 +++++++++++++++++--------------- lib/fortify_kunit.c | 47 ++++++++++++++++++++++++++++++++++ lib/string_helpers.c | 3 +++ 3 files changed, 75 insertions(+), 20 deletions(-) diff --git a/include/linux/fortify-string.h b/include/linux/fortify-string.h index 6db4052db459..2bbee7b28e71 100644 --- a/include/linux/fortify-string.h +++ b/include/linux/fortify-string.h @@ -11,8 +11,12 @@ #define fortify_reason(func, write) (((func) << 1) | !!(write)) -#define fortify_panic(func, write) \ +#ifdef FORTIFY_KUNIT_OVERRIDE +# define fortify_panic kunit_fortify_panic +#else +# define fortify_panic(func, write, retfail) \ __fortify_panic(fortify_reason(func, write)) +#endif #define FORTIFY_READ 0 #define FORTIFY_WRITE 1 @@ -174,7 +178,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); } @@ -205,7 +209,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; } @@ -241,7 +245,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; } @@ -283,7 +287,7 @@ __FORTIFY_INLINE size_t strlcpy(char * const POS p, const char * const POS q, si } if (size) { if (len >= p_size) - fortify_panic(FORTIFY_FUNC_strlcpy, FORTIFY_WRITE); + fortify_panic(FORTIFY_FUNC_strlcpy, FORTIFY_WRITE, q_len); __underlying_memcpy(p, q, len); p[len] = '\0'; } @@ -361,7 +365,7 @@ __FORTIFY_INLINE ssize_t strscpy(char * const POS p, const char * const POS q, s * 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: @@ -419,7 +423,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; @@ -428,7 +432,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'; @@ -457,7 +461,7 @@ char *strcat(char * const POS p, const char *q) 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; } @@ -493,13 +497,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) { @@ -534,7 +538,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) ({ \ @@ -633,9 +638,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. @@ -735,7 +740,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); } @@ -752,7 +757,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); } @@ -764,7 +769,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); } @@ -776,7 +781,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); } @@ -789,7 +794,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); } @@ -826,7 +831,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 d054fc20a7d5..f7523c25d341 100644 --- a/lib/fortify_kunit.c +++ b/lib/fortify_kunit.c @@ -15,12 +15,28 @@ */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +/* Call kunit_fortify_panic() instead of fortify_panic() */ +#define FORTIFY_KUNIT_OVERRIDE +void fortify_add_kunit_error(int write); +#define kunit_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 +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"; @@ -36,6 +52,25 @@ do { \ kunit_skip(test, "Not built with CONFIG_FORTIFY_SOURCE=y"); \ } while (0) +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) { skip_without_fortify(); @@ -322,6 +357,17 @@ DEFINE_ALLOC_SIZE_TEST_PAIR(kvmalloc) } while (0) DEFINE_ALLOC_SIZE_TEST_PAIR(devm_kmalloc) +static int fortify_test_init(struct kunit *test) +{ + kunit_add_named_resource(test, NULL, NULL, &read_resource, + "fortify_read_overflows", + &fortify_read_overflows); + kunit_add_named_resource(test, NULL, NULL, &write_resource, + "fortify_write_overflows", + &fortify_write_overflows); + return 0; +} + static struct kunit_case fortify_test_cases[] = { KUNIT_CASE(known_sizes_test), KUNIT_CASE(control_flow_split_test), @@ -338,6 +384,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, }; diff --git a/lib/string_helpers.c b/lib/string_helpers.c index 631c50657096..5bb65f623e40 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 @@ -1091,4 +1093,5 @@ void __fortify_panic(const u8 reason) BUG(); } EXPORT_SYMBOL(__fortify_panic); + #endif /* CONFIG_FORTIFY_SOURCE */ From patchwork Thu Apr 6 00:02:07 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kees Cook X-Patchwork-Id: 13202668 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 043B6C761A6 for ; Thu, 6 Apr 2023 00:02:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234302AbjDFACt (ORCPT ); Wed, 5 Apr 2023 20:02:49 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48048 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232605AbjDFACZ (ORCPT ); Wed, 5 Apr 2023 20:02:25 -0400 Received: from mail-pj1-x102b.google.com (mail-pj1-x102b.google.com [IPv6:2607:f8b0:4864:20::102b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 031447A8D for ; Wed, 5 Apr 2023 17:02:20 -0700 (PDT) Received: by mail-pj1-x102b.google.com with SMTP id q102so35661470pjq.3 for ; Wed, 05 Apr 2023 17:02:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1680739340; 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=aGVmFbO1Y3SrBAnAifK0ihmdOIjmdFYZw8LCIDp6wGM=; b=LxuIX9/ul4iLS8S1aSht29ovUoxEWtGqAQzkZBAuM1aqCB5+3YSKswbAGoEOIAFZHU 5+ZEOX44gt7UQ/6UDEk2oYWZHiuiZJCSUgfTLAzMP7qUKPaD9+iZvbO+rCCFfPu902DU yKxO2jiNZJkpWIJ/VwUKDp+AP65jBVlINGOLs= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1680739340; 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=aGVmFbO1Y3SrBAnAifK0ihmdOIjmdFYZw8LCIDp6wGM=; b=Voey1Ejtm95SmD+OteM3TiyuhEQhoDJ21/wPmQie2eCdeVP3USn1dzmI+EiCJs77WI 3mgTbTbfXTYv0xr23mLWngvRUrSm6KbdMspxJHqj7Z0R4PykWWfEBIWyJEd2AzWgtBmm 03DTktI6aig9LdT2TUNBzerUSWZrSfAg06tQijOrtGqMHKGRct/xxu4TSHewHyjvo/oX Wl+NjMrcyzLuUf94jk2IGPKVHVa+iIla0NsNH66wJARZnDlkRfSd7Rxm+J+sM5S63+zP Xv10iKdUYvfaNF5Abft3LECnwPqhmcuUtDxpfxUgv9TpWMd4rXWKy8ZBuqwIe5qXgg2A kixQ== X-Gm-Message-State: AAQBX9fX3PrjL9yoT8rb5gJmbMpuHestiWA1freFbqHGWGtep0I3AdLi gal/t5cu0doDqeDPPV5D71n86g== X-Google-Smtp-Source: AKy350ZzdST7ynXxaHkIFbOo3qCq3yyAa47OlYAX17JCcj3YVJqO9U5rE0aLbsCxGmdw5S4FmAKxkA== X-Received: by 2002:a17:902:d2ce:b0:1a2:1a5b:cc69 with SMTP id n14-20020a170902d2ce00b001a21a5bcc69mr9937827plc.32.1680739339837; Wed, 05 Apr 2023 17:02:19 -0700 (PDT) Received: from www.outflux.net (198-0-35-241-static.hfc.comcastbusiness.net. [198.0.35.241]) by smtp.gmail.com with ESMTPSA id p10-20020a170902b08a00b0019cbd37a335sm93665plr.93.2023.04.05.17.02.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 05 Apr 2023 17:02:17 -0700 (PDT) From: Kees Cook To: linux-hardening@vger.kernel.org Cc: Kees Cook , Kees Cook , Andy Shevchenko , Cezary Rojewski , Puyou Lu , Mark Brown , Josh Poimboeuf , Peter Zijlstra , Brendan Higgins , David Gow , Andrew Morton , Nathan Chancellor , Alexander Potapenko , Zhaoyang Huang , Randy Dunlap , Geert Uytterhoeven , Miguel Ojeda , Nick Desaulniers , Liam Howlett , Vlastimil Babka , Dan Williams , Rasmus Villemoes , Yury Norov , "Jason A. Donenfeld" , Sander Vanheule , Eric Biggers , "Masami Hiramatsu (Google)" , Andrey Konovalov , Linus Walleij , Daniel Latypov , =?utf-8?b?Sm9zw6kgRXhww7NzaXRv?= , linux-kernel@vger.kernel.org, kunit-dev@googlegroups.com Subject: [PATCH 8/9] fortify: Add KUnit tests for runtime overflows Date: Wed, 5 Apr 2023 17:02:07 -0700 Message-Id: <20230406000212.3442647-8-keescook@chromium.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230405235832.never.487-kees@kernel.org> References: <20230405235832.never.487-kees@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=29256; i=keescook@chromium.org; h=from:subject; bh=93uSHieIIEjRUuw59HxOto+9Z0ygPJhGlaPpgK8yUi0=; b=owEBbQKS/ZANAwAKAYly9N/cbcAmAcsmYgBkLgv/Eee2x9D/oTNO8DmEDkp1d4E7VwNznal84U3D Z5T/FeaJAjMEAAEKAB0WIQSlw/aPIp3WD3I+bhOJcvTf3G3AJgUCZC4L/wAKCRCJcvTf3G3AJuQnD/ 9HcOyFb96Sg7V/6cPe30hGahUjaFGv1GJfhTo1eskyLJvf490QordO1B8ZxUJ37i4l7HNDFoBD7gbU k+V3dnqQuks53s7OPhS51I38yshsdhmdHmoHtXIYfc3SrbbmPG2cgWRGo8xtqSK9Kva2myh2nFax6y tbcRrFiKQxBcM0M/V3fuw4Mmd1M2yp3diPAEnlzdTx8OoKsMedFtr0IG9azL8xTx8z9rb9kfIkaw52 UXeFOXOHo/aiZhe/n2X5rWhbywBQ06NVg4HN9pSFtHCWW41y2FbQmfdMZvA6rgSrG2fyLrVKf0Y+Qt hwRvjEmAjmQXowe0K/zJ2NUFDmaCyey3jnrf+V2nqK2gk0qdaZxCcArh7tPail+7pDkS4BdNlbEHNP JaKkLIFa1vz3MXuBXLBWNJZXeIYnBk9g4620cJU25/tpaGtCs6dFFMLrp8oWa8orSkMnRxFG+01yX8 dUMGsEA2wF2LIqJZgnQ9orDYUk+fYxHowQ/h67qjTqqjBW1fEtRVFcIUTFMR6WBzvNy7DrtjBO5mZY AypftjP2RcG3FS1fucjbxIt37jlDlR67LRUa/xShcHMBtMtNgMUVeLu25sDGt2ZNUz1UlGarFdLMtH 3MEXbzgAEBC/+yUST0x/69+evQL2UvyD/WBQ6oDdw/kuLYC7PlXJLkcwVnuQ== X-Developer-Key: i=keescook@chromium.org; a=openpgp; fpr=A5C3F68F229DD60F723E6E138972F4DFDC6DC026 Precedence: bulk List-ID: X-Mailing-List: linux-hardening@vger.kernel.org From: Kees Cook 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. Signed-off-by: Kees Cook --- lib/fortify_kunit.c | 733 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 733 insertions(+) diff --git a/lib/fortify_kunit.c b/lib/fortify_kunit.c index f7523c25d341..b7c884037629 100644 --- a/lib/fortify_kunit.c +++ b/lib/fortify_kunit.c @@ -357,6 +357,723 @@ 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 = 0; + +static void strlen_test(struct kunit *test) +{ + struct fortify_padding pad = { }; + int i, end = sizeof(pad.buf) - 1; + + skip_without_fortify(); + + fortify_read_overflows = 0; + + /* 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; + + skip_without_fortify(); + + fortify_read_overflows = 0; + + /* 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; + + skip_without_fortify(); + + /* Fill 31 bytes with valid characters. */ + for (i = 0; i < sizeof(src) - 2; i++) + src[i] = i + '0'; + + fortify_read_overflows = 0; + fortify_write_overflows = 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!"; + + skip_without_fortify(); + + fortify_write_overflows = 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 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 strlcpy_test(struct kunit *test) +{ + struct fortify_padding pad = { }; + char src[] = "Copy me fully into a small buffer and I will overflow!"; + char tiny[4] = "abcd"; + + skip_without_fortify(); + + fortify_read_overflows = 0; + fortify_write_overflows = 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 strlcpy() 1 less than of max size. */ + KUNIT_ASSERT_EQ(test, strlcpy(pad.buf, src, + sizeof(pad.buf) + unconst - 1), + sizeof(src) - 1); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); + 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 strlcpy. */ + KUNIT_ASSERT_EQ(test, strlcpy(pad.buf, src, + sizeof(pad.buf) + unconst), + sizeof(src) - 1); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); + 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, strlcpy(pad.buf, src, + sizeof(pad.buf) + unconst + 1), + sizeof(src) - 1); + /* Should catch the overflow. */ + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); + 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, strlcpy(pad.buf, src, + sizeof(src) * 2 + unconst), + sizeof(src) - 1); + /* Should catch the overflow. */ + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); + 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); + + /* Catch over-read. */ + KUNIT_ASSERT_EQ(test, strlcpy(pad.buf, tiny, + sizeof(pad.buf) + unconst), + sizeof(tiny)); + KUNIT_EXPECT_EQ(test, fortify_read_overflows, 1); + KUNIT_EXPECT_EQ(test, fortify_write_overflows, 2); +} + +static void strscpy_test(struct kunit *test) +{ + struct fortify_padding pad = { }; + char src[] = "Copy me fully into a small buffer and I will overflow!"; + + skip_without_fortify(); + + fortify_write_overflows = 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 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; + + skip_without_fortify(); + + fortify_write_overflows = 0; + + /* 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; + + skip_without_fortify(); + + fortify_read_overflows = 0; + fortify_write_overflows = 0; + + /* 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; + + skip_without_fortify(); + + fortify_read_overflows = 0; + fortify_write_overflows = 0; + + /* 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; + + skip_without_fortify(); + + fortify_read_overflows = 0; + + 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; + + skip_without_fortify(); + + fortify_read_overflows = 0; + + 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; + + skip_without_fortify(); + + fortify_read_overflows = 0; + + /* 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; + + skip_without_fortify(); + + fortify_read_overflows = 0; + + /* 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; + + skip_without_fortify(); + + fortify_read_overflows = 0; + + /* 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) { kunit_add_named_resource(test, NULL, NULL, &read_resource, @@ -379,6 +1096,22 @@ 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(strlcpy_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-clobber 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 Thu Apr 6 00:02:08 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kees Cook X-Patchwork-Id: 13202667 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E54CFC7618D for ; Thu, 6 Apr 2023 00:02:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234033AbjDFACa (ORCPT ); Wed, 5 Apr 2023 20:02:30 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47938 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233119AbjDFACW (ORCPT ); Wed, 5 Apr 2023 20:02:22 -0400 Received: from mail-pj1-x1033.google.com (mail-pj1-x1033.google.com [IPv6:2607:f8b0:4864:20::1033]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4BDE176BA for ; Wed, 5 Apr 2023 17:02:20 -0700 (PDT) Received: by mail-pj1-x1033.google.com with SMTP id f6-20020a17090ac28600b0023b9bf9eb63so38947502pjt.5 for ; Wed, 05 Apr 2023 17:02:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1680739339; 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=TMkBWw2BkDKd0w5gBkiKI3BqJHl3IJqXN2BQevcFbNE=; b=fFL2rtSqkSU6Bt+5odQRtTqVNA9TuWc9jO+VTD4Yl8/W8fT4IdqxL+4K74Hwv+5XqE rz0aomwvMjXdOe6kM+YcxrMO2Md7V+uVUFwwpbRoB39+t5Hxtqg2T0GZDYD2qot0e48t JiZkOttLyLyYjk791fN7F3Pe6ZTRSLSGhHaR4= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1680739339; 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=TMkBWw2BkDKd0w5gBkiKI3BqJHl3IJqXN2BQevcFbNE=; b=5ib5kUDHvADL3/az/Ph1qXKnH6nMqBYQKzmtADSXWEcA/z9vFezBkMs3V1yF07pOe+ TP4nidamO39ge9BG6xkvZrxRgNSbh/MuSdgdej+3/nMeGzA2l/luTxwVkodOrIe22vhN uIqAIV4H9S40ltnO+90YaDyQR3I/9JcCcynebDc9ignM863WWQMxXYqYixWc2MaaoOh8 PPDOSMxGk7+EWFfDyrG2IgbUUi1C1MnJIXFajEo79+seRlb/wHMXyLgSs4TkcgstSwQk Xs+KBFJ6FSrNATmWG+1oTh8y9eaASw1F2le2iOFiGu5Z/CLhbi98O7olXQPZ4vcwBdNt ozsw== X-Gm-Message-State: AAQBX9fUxu0C8Cui220qdR4w6iQu49QL0Sn1kbQ6mftowFye4c7jiFME LQ72KwyenbVWVxmOIDRuGQll1A== X-Google-Smtp-Source: AKy350YqaHGkpTFSAl5wJB7/y7MxIVBsC8pLSIGq4AAJ2gq+A3ctEZj3LVvMlzkHp8A0Eaa1T/1csg== X-Received: by 2002:a17:902:d2c9:b0:19d:7a4:4063 with SMTP id n9-20020a170902d2c900b0019d07a44063mr10558907plc.46.1680739339380; Wed, 05 Apr 2023 17:02:19 -0700 (PDT) Received: from www.outflux.net (198-0-35-241-static.hfc.comcastbusiness.net. [198.0.35.241]) by smtp.gmail.com with ESMTPSA id v3-20020a170902b7c300b001a240f053aasm82939plz.180.2023.04.05.17.02.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 05 Apr 2023 17:02:17 -0700 (PDT) From: Kees Cook To: linux-hardening@vger.kernel.org Cc: Kees Cook , Kees Cook , Andy Shevchenko , Cezary Rojewski , Puyou Lu , Mark Brown , Josh Poimboeuf , Peter Zijlstra , Brendan Higgins , David Gow , Andrew Morton , Nathan Chancellor , Alexander Potapenko , Zhaoyang Huang , Randy Dunlap , Geert Uytterhoeven , Miguel Ojeda , Nick Desaulniers , Liam Howlett , Vlastimil Babka , Dan Williams , Rasmus Villemoes , Yury Norov , "Jason A. Donenfeld" , Sander Vanheule , Eric Biggers , "Masami Hiramatsu (Google)" , Andrey Konovalov , Linus Walleij , Daniel Latypov , =?utf-8?b?Sm9zw6kgRXhww7NzaXRv?= , linux-kernel@vger.kernel.org, kunit-dev@googlegroups.com Subject: [PATCH 9/9] fortify: Improve buffer overflow reporting Date: Wed, 5 Apr 2023 17:02:08 -0700 Message-Id: <20230406000212.3442647-9-keescook@chromium.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230405235832.never.487-kees@kernel.org> References: <20230405235832.never.487-kees@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=10763; i=keescook@chromium.org; h=from:subject; bh=sUo3OusVEyTd1TzMe089/10RvDWd1ECiLD0iA7jbIMo=; b=owEBbQKS/ZANAwAKAYly9N/cbcAmAcsmYgBkLgv/K75AFXHO/hOzjfREHpqcERlLV3XfGhVps4a5 3V4CDYyJAjMEAAEKAB0WIQSlw/aPIp3WD3I+bhOJcvTf3G3AJgUCZC4L/wAKCRCJcvTf3G3AJmK6D/ 9gmGPNETiVsquqPmXbjRCt8fY5qeKhaICBkEOkHuWRQ/LJ+rMGE6UpqebohA5cRkOzG8G1yT0Zzyta y1uChVwx8YTE627K52ry9bJRzyeDiCx/JxElBaqBeYo870Osw66SI+uL0CpEfCCyp328woQHGN/PeC AQ7sn3YG8R8jSkMaBy5jeskTNErnZjw+cc67ltaYlYLh6qA1fN+h7NzjEEGH2VIGVE2JBV+Uap6Cse xUoDTRiAchaiSlEhp+CReYFqsEXgQNezLntzmsGkjYSRowJhTOlYZzHsJTGp0yna5vpW4To1w7nQEl aXDoYn86ZH538WKLkX/lxmpPnTSS9ERMAatG8B3BtLpZgYpQu7InTzfwIjYypT7f3baF8M2m13pVhi CEw3cDwUmHb/nyRc+xBCC6YtT16dHfH+kJ05PEHRXXkgTSCSf7fJEyr98MOraI+M/lCoP28BYBeoDE h2bhM2bjKCUJcyJ0Y6lyCiNbcwe4TF/Vus9/2cvmvMwlr77AaoFvPei2PXoOobkr1nfCX5qBemSJDP CMbvgY524ktnbIrz7bcvVCTVyEESAMjaVGftjndxhF7eUVbvhS0PHiQcLhXJ98AGWOAL1TUjdkCKiw cfK3c11il/LI7k00NfS0hPI/oLPvPiPBPRoAOMngD9FgUwTp/EGUyyH2ZeoQ== X-Developer-Key: i=keescook@chromium.org; a=openpgp; fpr=A5C3F68F229DD60F723E6E138972F4DFDC6DC026 Precedence: bulk List-ID: X-Mailing-List: linux-hardening@vger.kernel.org From: Kees Cook 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 from buffer of size 1 Signed-off-by: Kees Cook --- include/linux/fortify-string.h | 60 +++++++++++++++++++--------------- lib/fortify_kunit.c | 4 +-- lib/string_helpers.c | 9 ++--- 3 files changed, 40 insertions(+), 33 deletions(-) diff --git a/include/linux/fortify-string.h b/include/linux/fortify-string.h index 2bbee7b28e71..d37f4597cf68 100644 --- a/include/linux/fortify-string.h +++ b/include/linux/fortify-string.h @@ -14,8 +14,8 @@ #ifdef FORTIFY_KUNIT_OVERRIDE # define fortify_panic kunit_fortify_panic #else -# 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 @@ -39,8 +39,8 @@ #define FORTIFY_FUNC_kmemdup 15 #define FORTIFY_FUNC_strcpy 16 -void __fortify_report(u8 reason); -void __fortify_panic(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()?"); @@ -178,7 +178,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); } @@ -209,7 +209,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; } @@ -245,7 +245,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; } @@ -286,8 +286,8 @@ __FORTIFY_INLINE size_t strlcpy(char * const POS p, const char * const POS q, si __write_overflow(); } if (size) { - if (len >= p_size) - fortify_panic(FORTIFY_FUNC_strlcpy, FORTIFY_WRITE, q_len); + if (p_size <= len) + fortify_panic(FORTIFY_FUNC_strlcpy, FORTIFY_WRITE, p_size, len + 1, q_len); __underlying_memcpy(p, q, len); p[len] = '\0'; } @@ -364,8 +364,8 @@ __FORTIFY_INLINE ssize_t strscpy(char * const POS p, const char * const POS q, s * 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: @@ -423,7 +423,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; @@ -432,7 +432,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'; @@ -459,9 +459,11 @@ __FORTIFY_INLINE __diagnose_as(__builtin_strcat, 1, 2) char *strcat(char * const POS p, const char *q) { size_t p_size = __member_size(p); + size_t wanted; - if (strlcat(p, q, p_size) >= p_size) - fortify_panic(FORTIFY_FUNC_strcat, FORTIFY_WRITE, p); + wanted = strlcat(p, q, p_size); + if (p_size <= wanted) + fortify_panic(FORTIFY_FUNC_strcat, FORTIFY_WRITE, p_size, wanted + 1, p); return p; } @@ -491,13 +493,15 @@ char *strncat(char * const POS p, const char * const POS q, __kernel_size_t coun size_t p_len, copy_len; size_t p_size = __member_size(p); size_t q_size = __member_size(q); + size_t 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; @@ -538,7 +542,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; } @@ -638,9 +642,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. @@ -740,7 +744,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); } @@ -756,8 +760,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); } @@ -769,7 +775,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); } @@ -781,7 +787,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); } @@ -794,7 +800,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); } @@ -831,7 +837,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 b7c884037629..b022797c9fe6 100644 --- a/lib/fortify_kunit.c +++ b/lib/fortify_kunit.c @@ -18,9 +18,9 @@ /* Call kunit_fortify_panic() instead of fortify_panic() */ #define FORTIFY_KUNIT_OVERRIDE void fortify_add_kunit_error(int write); -#define kunit_fortify_panic(func, write, retfail) \ +#define kunit_fortify_panic(func, write, avail, size, retfail) \ do { \ - __fortify_report(fortify_reason(func, write)); \ + __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 5bb65f623e40..cc15a25556fb 100644 --- a/lib/string_helpers.c +++ b/lib/string_helpers.c @@ -1023,7 +1023,7 @@ EXPORT_SYMBOL(__read_overflow2_field); void __write_overflow_field(size_t avail, size_t wanted) { } EXPORT_SYMBOL(__write_overflow_field); -void __fortify_report(u8 reason) +void __fortify_report(const u8 reason, const size_t avail, const size_t size) { const char *name; const bool write = !!(reason & 0x1); @@ -1083,13 +1083,14 @@ void __fortify_report(u8 reason) default: name = "unknown"; } - WARN(1, "%s: detected buffer %s overflow\n", name, write ? "write" : "read"); + WARN(1, "%s: detected buffer overflow: %zu byte %s buffer of size %zu\n", + name, size, write ? "write to" : "read from", 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);