https://github.com/gamesh411 updated https://github.com/llvm/llvm-project/pull/168704
From dae8506bc6eafa1fcc10e03e3bf7678c17a0babe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Wed, 19 Nov 2025 10:47:40 +0100 Subject: [PATCH 01/13] [clang][analyzer] Add ReportInC99AndEarlier option to DeprecatedOrUnsafeBufferHandling checker The checker may report warnings for deprecated buffer handling functions (memcpy, memset, memmove, etc.) even when not compiling with C11 standard if the new option "ReportInC99AndEarlier" is set to true. These functions became deprecated in C11, but may still be problematic in earlier C standards. --- clang/docs/analyzer/checkers.rst | 7 +++ .../clang/StaticAnalyzer/Checkers/Checkers.td | 21 +++++--- .../Checkers/CheckSecuritySyntaxOnly.cpp | 22 ++++++++- .../Analysis/Inputs/system-header-simulator.h | 1 + clang/test/Analysis/analyzer-config.c | 1 + ...ecated-buffer-handling-allow-without-c11.c | 48 +++++++++++++++++++ 6 files changed, 92 insertions(+), 8 deletions(-) create mode 100644 clang/test/Analysis/security-deprecated-buffer-handling-allow-without-c11.c diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst index 31edf9e99dc7d..d93cfe5806ebb 100644 --- a/clang/docs/analyzer/checkers.rst +++ b/clang/docs/analyzer/checkers.rst @@ -1785,6 +1785,13 @@ security.insecureAPI.DeprecatedOrUnsafeBufferHandling (C) strncpy(buf, "a", 1); // warn } +The ``AllowWithoutC11`` option allows reporting warnings for these functions even when not compiling with C11 standard. These functions are deprecated in C11, but may still be problematic in earlier C standards. + +To enable this option, use: +``-analyzer-config security.insecureAPI.DeprecatedOrUnsafeBufferHandling:AllowWithoutC11=true``. + +By default, this option is set to *false*. + .. _security-MmapWriteExec: security.MmapWriteExec (C) diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td index ffae3b9310979..310dac5340a18 100644 --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -901,12 +901,21 @@ def UncheckedReturn : Checker<"UncheckedReturn">, Dependencies<[SecuritySyntaxChecker]>, Documentation<HasDocumentation>; -def DeprecatedOrUnsafeBufferHandling : - Checker<"DeprecatedOrUnsafeBufferHandling">, - HelpText<"Warn on uses of unsecure or deprecated buffer manipulating " - "functions">, - Dependencies<[SecuritySyntaxChecker]>, - Documentation<HasDocumentation>; +def DeprecatedOrUnsafeBufferHandling + : Checker<"DeprecatedOrUnsafeBufferHandling">, + HelpText<"Warn on uses of unsecure or deprecated buffer manipulating " + "functions">, + Dependencies<[SecuritySyntaxChecker]>, + CheckerOptions< + [CmdLineOption< + Boolean, "AllowWithoutC11", + "Allow reporting deprecated or unsafe buffer handling " + "functions even when not compiling with C11 standard. " + "These functions are deprecated in C11, but may still be " + "problematic in earlier C standards.", + "false", Released>, +]>, + Documentation<HasDocumentation>; def decodeValueOfObjCType : Checker<"decodeValueOfObjCType">, HelpText<"Warn on uses of the '-decodeValueOfObjCType:at:' method">, diff --git a/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp index 5e75c1c4a3abd..e07c9dcbad9fe 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp @@ -50,6 +50,8 @@ struct ChecksFilter { bool check_UncheckedReturn = false; bool check_decodeValueOfObjCType = false; + bool allowDeprecatedOrUnsafeBufferHandlingWithoutC11 = false; + CheckerNameRef checkName_bcmp; CheckerNameRef checkName_bcopy; CheckerNameRef checkName_bzero; @@ -754,7 +756,8 @@ void WalkAST::checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE, if (!filter.check_DeprecatedOrUnsafeBufferHandling) return; - if (!BR.getContext().getLangOpts().C11) + if (!(BR.getContext().getLangOpts().C11 || + filter.allowDeprecatedOrUnsafeBufferHandlingWithoutC11)) return; // Issue a warning. ArgIndex == -1: Deprecated but not unsafe (has size @@ -1113,5 +1116,20 @@ REGISTER_CHECKER(rand) REGISTER_CHECKER(vfork) REGISTER_CHECKER(FloatLoopCounter) REGISTER_CHECKER(UncheckedReturn) -REGISTER_CHECKER(DeprecatedOrUnsafeBufferHandling) + +void ento::registerDeprecatedOrUnsafeBufferHandling(CheckerManager &mgr) { + SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); + checker->filter.check_DeprecatedOrUnsafeBufferHandling = true; + checker->filter.checkName_DeprecatedOrUnsafeBufferHandling = + mgr.getCurrentCheckerName(); + checker->filter.allowDeprecatedOrUnsafeBufferHandlingWithoutC11 = + mgr.getAnalyzerOptions().getCheckerBooleanOption( + mgr.getCurrentCheckerName(), "AllowWithoutC11"); +} + +bool ento::shouldRegisterDeprecatedOrUnsafeBufferHandling( + const CheckerManager &mgr) { + return true; +} + REGISTER_CHECKER(decodeValueOfObjCType) diff --git a/clang/test/Analysis/Inputs/system-header-simulator.h b/clang/test/Analysis/Inputs/system-header-simulator.h index fadc09f65d536..e048a6a892c48 100644 --- a/clang/test/Analysis/Inputs/system-header-simulator.h +++ b/clang/test/Analysis/Inputs/system-header-simulator.h @@ -82,6 +82,7 @@ char *strcpy(char *restrict, const char *restrict); char *strncpy(char *restrict dst, const char *restrict src, size_t n); char *strsep(char **restrict stringp, const char *restrict delim); void *memcpy(void *restrict dst, const void *restrict src, size_t n); +void *memmove(void *dst, const void *src, size_t n); void *memset(void *s, int c, size_t n); typedef unsigned long __darwin_pthread_key_t; diff --git a/clang/test/Analysis/analyzer-config.c b/clang/test/Analysis/analyzer-config.c index 7936273415ad4..60ca162fb3f24 100644 --- a/clang/test/Analysis/analyzer-config.c +++ b/clang/test/Analysis/analyzer-config.c @@ -121,6 +121,7 @@ // CHECK-NEXT: region-store-small-struct-limit = 2 // CHECK-NEXT: report-in-main-source-file = false // CHECK-NEXT: security.cert.env.InvalidPtr:InvalidatingGetEnv = false +// CHECK-NEXT: security.insecureAPI.DeprecatedOrUnsafeBufferHandling:AllowWithoutC11 = false // CHECK-NEXT: serialize-stats = false // CHECK-NEXT: silence-checkers = "" // CHECK-NEXT: stable-report-filename = false diff --git a/clang/test/Analysis/security-deprecated-buffer-handling-allow-without-c11.c b/clang/test/Analysis/security-deprecated-buffer-handling-allow-without-c11.c new file mode 100644 index 0000000000000..880e4bbf81302 --- /dev/null +++ b/clang/test/Analysis/security-deprecated-buffer-handling-allow-without-c11.c @@ -0,0 +1,48 @@ +// Test 1: Without C11 and without flag - should NOT warn +// RUN: %clang_analyze_cc1 %s -verify -std=gnu99 \ +// RUN: -analyzer-checker=security.insecureAPI.DeprecatedOrUnsafeBufferHandling \ +// RUN: -DEXPECT_NO_WARNINGS + +// Test 2: Without C11 but with flag enabled - should warn +// RUN: %clang_analyze_cc1 %s -verify -std=gnu99 \ +// RUN: -analyzer-checker=security.insecureAPI.DeprecatedOrUnsafeBufferHandling \ +// RUN: -analyzer-config security.insecureAPI.DeprecatedOrUnsafeBufferHandling:AllowWithoutC11=true \ +// RUN: -DEXPECT_WARNINGS + +// Test 3: With C11 - should warn (existing behavior) +// RUN: %clang_analyze_cc1 %s -verify -std=gnu11 \ +// RUN: -analyzer-checker=security.insecureAPI.DeprecatedOrUnsafeBufferHandling \ +// RUN: -DEXPECT_WARNINGS + +#include "Inputs/system-header-simulator.h" + +extern char buf[128]; +extern char src[128]; + +void test_memcpy(void) { + memcpy(buf, src, 10); +#ifdef EXPECT_WARNINGS + // expected-warning@-2{{Call to function 'memcpy' is insecure as it does not provide security checks introduced in the C11 standard}} +#else + // expected-no-diagnostics +#endif +} + +void test_memset(void) { + memset(buf, 0, 10); +#ifdef EXPECT_WARNINGS + // expected-warning@-2{{Call to function 'memset' is insecure as it does not provide security checks introduced in the C11 standard}} +#else + // expected-no-diagnostics +#endif +} + +void test_memmove(void) { + memmove(buf, src, 10); +#ifdef EXPECT_WARNINGS + // expected-warning@-2{{Call to function 'memmove' is insecure as it does not provide security checks introduced in the C11 standard}} +#else + // expected-no-diagnostics +#endif +} + From c17348a68c5a9090bbf69068acb80a720f3cba8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Wed, 26 Nov 2025 13:25:41 +0100 Subject: [PATCH 02/13] eliminate macro-based test case separation not quite as suggested, but I think this is expressive now without being too complicated --- ...ecated-buffer-handling-allow-without-c11.c | 38 +++++++------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/clang/test/Analysis/security-deprecated-buffer-handling-allow-without-c11.c b/clang/test/Analysis/security-deprecated-buffer-handling-allow-without-c11.c index 880e4bbf81302..57cc05a7cf555 100644 --- a/clang/test/Analysis/security-deprecated-buffer-handling-allow-without-c11.c +++ b/clang/test/Analysis/security-deprecated-buffer-handling-allow-without-c11.c @@ -1,48 +1,38 @@ // Test 1: Without C11 and without flag - should NOT warn -// RUN: %clang_analyze_cc1 %s -verify -std=gnu99 \ -// RUN: -analyzer-checker=security.insecureAPI.DeprecatedOrUnsafeBufferHandling \ -// RUN: -DEXPECT_NO_WARNINGS +// RUN: %clang_analyze_cc1 %s -verify=c99-noflag -std=gnu99 \ +// RUN: -analyzer-checker=security.insecureAPI.DeprecatedOrUnsafeBufferHandling // Test 2: Without C11 but with flag enabled - should warn -// RUN: %clang_analyze_cc1 %s -verify -std=gnu99 \ +// RUN: %clang_analyze_cc1 %s -verify=c99-withflag -std=gnu99 \ // RUN: -analyzer-checker=security.insecureAPI.DeprecatedOrUnsafeBufferHandling \ -// RUN: -analyzer-config security.insecureAPI.DeprecatedOrUnsafeBufferHandling:AllowWithoutC11=true \ -// RUN: -DEXPECT_WARNINGS +// RUN: -analyzer-config security.insecureAPI.DeprecatedOrUnsafeBufferHandling:AllowWithoutC11=true // Test 3: With C11 - should warn (existing behavior) -// RUN: %clang_analyze_cc1 %s -verify -std=gnu11 \ -// RUN: -analyzer-checker=security.insecureAPI.DeprecatedOrUnsafeBufferHandling \ -// RUN: -DEXPECT_WARNINGS +// RUN: %clang_analyze_cc1 %s -verify=c11 -std=gnu11 \ +// RUN: -analyzer-checker=security.insecureAPI.DeprecatedOrUnsafeBufferHandling #include "Inputs/system-header-simulator.h" extern char buf[128]; extern char src[128]; +// c99-noflag-no-diagnostics + void test_memcpy(void) { memcpy(buf, src, 10); -#ifdef EXPECT_WARNINGS - // expected-warning@-2{{Call to function 'memcpy' is insecure as it does not provide security checks introduced in the C11 standard}} -#else - // expected-no-diagnostics -#endif + // c99-withflag-warning@-1{{Call to function 'memcpy' is insecure as it does not provide security checks introduced in the C11 standard}} + // c11-warning@-2{{Call to function 'memcpy' is insecure as it does not provide security checks introduced in the C11 standard}} } void test_memset(void) { memset(buf, 0, 10); -#ifdef EXPECT_WARNINGS - // expected-warning@-2{{Call to function 'memset' is insecure as it does not provide security checks introduced in the C11 standard}} -#else - // expected-no-diagnostics -#endif + // c99-withflag-warning@-1{{Call to function 'memset' is insecure as it does not provide security checks introduced in the C11 standard}} + // c11-warning@-2{{Call to function 'memset' is insecure as it does not provide security checks introduced in the C11 standard}} } void test_memmove(void) { memmove(buf, src, 10); -#ifdef EXPECT_WARNINGS - // expected-warning@-2{{Call to function 'memmove' is insecure as it does not provide security checks introduced in the C11 standard}} -#else - // expected-no-diagnostics -#endif + // c99-withflag-warning@-1{{Call to function 'memmove' is insecure as it does not provide security checks introduced in the C11 standard}} + // c11-warning@-2{{Call to function 'memmove' is insecure as it does not provide security checks introduced in the C11 standard}} } From 193c40f5dc005778a309b0a711206c2fc6a04bff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Wed, 26 Nov 2025 13:54:11 +0100 Subject: [PATCH 03/13] rename flag --- clang/docs/analyzer/checkers.rst | 4 ++-- clang/include/clang/StaticAnalyzer/Checkers/Checkers.td | 4 ++-- clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp | 2 +- clang/test/Analysis/analyzer-config.c | 2 +- .../security-deprecated-buffer-handling-allow-without-c11.c | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst index d93cfe5806ebb..244fdb62a162d 100644 --- a/clang/docs/analyzer/checkers.rst +++ b/clang/docs/analyzer/checkers.rst @@ -1785,10 +1785,10 @@ security.insecureAPI.DeprecatedOrUnsafeBufferHandling (C) strncpy(buf, "a", 1); // warn } -The ``AllowWithoutC11`` option allows reporting warnings for these functions even when not compiling with C11 standard. These functions are deprecated in C11, but may still be problematic in earlier C standards. +The ``ReportInC99AndEarlier`` option allows reporting warnings for these functions even when not compiling with C11 standard. These functions became deprecated in C11, but may still be problematic in earlier C standards. To enable this option, use: -``-analyzer-config security.insecureAPI.DeprecatedOrUnsafeBufferHandling:AllowWithoutC11=true``. +``-analyzer-config security.insecureAPI.DeprecatedOrUnsafeBufferHandling:ReportInC99AndEarlier=true``. By default, this option is set to *false*. diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td index 310dac5340a18..1b7751221fb7b 100644 --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -908,10 +908,10 @@ def DeprecatedOrUnsafeBufferHandling Dependencies<[SecuritySyntaxChecker]>, CheckerOptions< [CmdLineOption< - Boolean, "AllowWithoutC11", + Boolean, "ReportInC99AndEarlier", "Allow reporting deprecated or unsafe buffer handling " "functions even when not compiling with C11 standard. " - "These functions are deprecated in C11, but may still be " + "These functions became deprecated in C11, but may still be " "problematic in earlier C standards.", "false", Released>, ]>, diff --git a/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp index e07c9dcbad9fe..e6dbb8baa7e67 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp @@ -1124,7 +1124,7 @@ void ento::registerDeprecatedOrUnsafeBufferHandling(CheckerManager &mgr) { mgr.getCurrentCheckerName(); checker->filter.allowDeprecatedOrUnsafeBufferHandlingWithoutC11 = mgr.getAnalyzerOptions().getCheckerBooleanOption( - mgr.getCurrentCheckerName(), "AllowWithoutC11"); + mgr.getCurrentCheckerName(), "ReportInC99AndEarlier"); } bool ento::shouldRegisterDeprecatedOrUnsafeBufferHandling( diff --git a/clang/test/Analysis/analyzer-config.c b/clang/test/Analysis/analyzer-config.c index 60ca162fb3f24..adc90894557f6 100644 --- a/clang/test/Analysis/analyzer-config.c +++ b/clang/test/Analysis/analyzer-config.c @@ -121,7 +121,7 @@ // CHECK-NEXT: region-store-small-struct-limit = 2 // CHECK-NEXT: report-in-main-source-file = false // CHECK-NEXT: security.cert.env.InvalidPtr:InvalidatingGetEnv = false -// CHECK-NEXT: security.insecureAPI.DeprecatedOrUnsafeBufferHandling:AllowWithoutC11 = false +// CHECK-NEXT: security.insecureAPI.DeprecatedOrUnsafeBufferHandling:ReportInC99AndEarlier = false // CHECK-NEXT: serialize-stats = false // CHECK-NEXT: silence-checkers = "" // CHECK-NEXT: stable-report-filename = false diff --git a/clang/test/Analysis/security-deprecated-buffer-handling-allow-without-c11.c b/clang/test/Analysis/security-deprecated-buffer-handling-allow-without-c11.c index 57cc05a7cf555..fae3b498913ec 100644 --- a/clang/test/Analysis/security-deprecated-buffer-handling-allow-without-c11.c +++ b/clang/test/Analysis/security-deprecated-buffer-handling-allow-without-c11.c @@ -5,7 +5,7 @@ // Test 2: Without C11 but with flag enabled - should warn // RUN: %clang_analyze_cc1 %s -verify=c99-withflag -std=gnu99 \ // RUN: -analyzer-checker=security.insecureAPI.DeprecatedOrUnsafeBufferHandling \ -// RUN: -analyzer-config security.insecureAPI.DeprecatedOrUnsafeBufferHandling:AllowWithoutC11=true +// RUN: -analyzer-config security.insecureAPI.DeprecatedOrUnsafeBufferHandling:ReportInC99AndEarlier=true // Test 3: With C11 - should warn (existing behavior) // RUN: %clang_analyze_cc1 %s -verify=c11 -std=gnu11 \ From baf884d57661ac8626c90e154f9987f1736a5a73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Wed, 26 Nov 2025 14:53:34 +0100 Subject: [PATCH 04/13] remove redundancy --- ...y-deprecated-buffer-handling-allow-without-c11.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/clang/test/Analysis/security-deprecated-buffer-handling-allow-without-c11.c b/clang/test/Analysis/security-deprecated-buffer-handling-allow-without-c11.c index fae3b498913ec..f8686798f6819 100644 --- a/clang/test/Analysis/security-deprecated-buffer-handling-allow-without-c11.c +++ b/clang/test/Analysis/security-deprecated-buffer-handling-allow-without-c11.c @@ -3,12 +3,12 @@ // RUN: -analyzer-checker=security.insecureAPI.DeprecatedOrUnsafeBufferHandling // Test 2: Without C11 but with flag enabled - should warn -// RUN: %clang_analyze_cc1 %s -verify=c99-withflag -std=gnu99 \ +// RUN: %clang_analyze_cc1 %s -verify=common -std=gnu99 \ // RUN: -analyzer-checker=security.insecureAPI.DeprecatedOrUnsafeBufferHandling \ // RUN: -analyzer-config security.insecureAPI.DeprecatedOrUnsafeBufferHandling:ReportInC99AndEarlier=true // Test 3: With C11 - should warn (existing behavior) -// RUN: %clang_analyze_cc1 %s -verify=c11 -std=gnu11 \ +// RUN: %clang_analyze_cc1 %s -verify=common -std=gnu11 \ // RUN: -analyzer-checker=security.insecureAPI.DeprecatedOrUnsafeBufferHandling #include "Inputs/system-header-simulator.h" @@ -20,19 +20,16 @@ extern char src[128]; void test_memcpy(void) { memcpy(buf, src, 10); - // c99-withflag-warning@-1{{Call to function 'memcpy' is insecure as it does not provide security checks introduced in the C11 standard}} - // c11-warning@-2{{Call to function 'memcpy' is insecure as it does not provide security checks introduced in the C11 standard}} + // common-warning@-1{{Call to function 'memcpy' is insecure as it does not provide security checks introduced in the C11 standard}} } void test_memset(void) { memset(buf, 0, 10); - // c99-withflag-warning@-1{{Call to function 'memset' is insecure as it does not provide security checks introduced in the C11 standard}} - // c11-warning@-2{{Call to function 'memset' is insecure as it does not provide security checks introduced in the C11 standard}} + // common-warning@-1{{Call to function 'memset' is insecure as it does not provide security checks introduced in the C11 standard}} } void test_memmove(void) { memmove(buf, src, 10); - // c99-withflag-warning@-1{{Call to function 'memmove' is insecure as it does not provide security checks introduced in the C11 standard}} - // c11-warning@-2{{Call to function 'memmove' is insecure as it does not provide security checks introduced in the C11 standard}} + // common-warning@-1{{Call to function 'memmove' is insecure as it does not provide security checks introduced in the C11 standard}} } From 362cf7409cb5b3945898ca41bde57bf41573102f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Wed, 26 Nov 2025 23:28:00 +0100 Subject: [PATCH 05/13] rename flag --- .../lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp index e6dbb8baa7e67..a9c60e5bfba14 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp @@ -50,7 +50,7 @@ struct ChecksFilter { bool check_UncheckedReturn = false; bool check_decodeValueOfObjCType = false; - bool allowDeprecatedOrUnsafeBufferHandlingWithoutC11 = false; + bool reportDeprecatedOrUnsafeBufferHandlingInC99AndEarlier = false; CheckerNameRef checkName_bcmp; CheckerNameRef checkName_bcopy; @@ -757,7 +757,7 @@ void WalkAST::checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE, return; if (!(BR.getContext().getLangOpts().C11 || - filter.allowDeprecatedOrUnsafeBufferHandlingWithoutC11)) + filter.reportDeprecatedOrUnsafeBufferHandlingInC99AndEarlier)) return; // Issue a warning. ArgIndex == -1: Deprecated but not unsafe (has size @@ -1122,7 +1122,7 @@ void ento::registerDeprecatedOrUnsafeBufferHandling(CheckerManager &mgr) { checker->filter.check_DeprecatedOrUnsafeBufferHandling = true; checker->filter.checkName_DeprecatedOrUnsafeBufferHandling = mgr.getCurrentCheckerName(); - checker->filter.allowDeprecatedOrUnsafeBufferHandlingWithoutC11 = + checker->filter.reportDeprecatedOrUnsafeBufferHandlingInC99AndEarlier = mgr.getAnalyzerOptions().getCheckerBooleanOption( mgr.getCurrentCheckerName(), "ReportInC99AndEarlier"); } From 5d9175ef82ad2ef1b37684e9a26538fde4097079 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Wed, 26 Nov 2025 23:28:41 +0100 Subject: [PATCH 06/13] fix code style --- .../Checkers/CheckSecuritySyntaxOnly.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp index a9c60e5bfba14..d4b56418535e0 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp @@ -1117,18 +1117,18 @@ REGISTER_CHECKER(vfork) REGISTER_CHECKER(FloatLoopCounter) REGISTER_CHECKER(UncheckedReturn) -void ento::registerDeprecatedOrUnsafeBufferHandling(CheckerManager &mgr) { - SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); - checker->filter.check_DeprecatedOrUnsafeBufferHandling = true; - checker->filter.checkName_DeprecatedOrUnsafeBufferHandling = - mgr.getCurrentCheckerName(); - checker->filter.reportDeprecatedOrUnsafeBufferHandlingInC99AndEarlier = - mgr.getAnalyzerOptions().getCheckerBooleanOption( - mgr.getCurrentCheckerName(), "ReportInC99AndEarlier"); +void ento::registerDeprecatedOrUnsafeBufferHandling(CheckerManager &Mgr) { + SecuritySyntaxChecker *Checker = Mgr.getChecker<SecuritySyntaxChecker>(); + Checker->filter.check_DeprecatedOrUnsafeBufferHandling = true; + Checker->filter.checkName_DeprecatedOrUnsafeBufferHandling = + Mgr.getCurrentCheckerName(); + Checker->filter.reportDeprecatedOrUnsafeBufferHandlingInC99AndEarlier = + Mgr.getAnalyzerOptions().getCheckerBooleanOption( + Mgr.getCurrentCheckerName(), "ReportInC99AndEarlier"); } bool ento::shouldRegisterDeprecatedOrUnsafeBufferHandling( - const CheckerManager &mgr) { + const CheckerManager &) { return true; } From c21e2e5431c2e807701e34d69e5dacca59d04f4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Wed, 10 Dec 2025 10:39:19 +0100 Subject: [PATCH 07/13] factor out annexk detection --- .../bugprone/UnsafeFunctionsCheck.cpp | 22 +------ .../include/clang/Analysis/AnnexKDetection.h | 61 +++++++++++++++++++ 2 files changed, 63 insertions(+), 20 deletions(-) create mode 100644 clang/include/clang/Analysis/AnnexKDetection.h diff --git a/clang-tools-extra/clang-tidy/bugprone/UnsafeFunctionsCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UnsafeFunctionsCheck.cpp index 67d0931003c54..f0b325d3e1823 100644 --- a/clang-tools-extra/clang-tidy/bugprone/UnsafeFunctionsCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/UnsafeFunctionsCheck.cpp @@ -10,6 +10,7 @@ #include "../utils/OptionsUtils.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Analysis/AnnexKDetection.h" #include "clang/Lex/PPCallbacks.h" #include "clang/Lex/Preprocessor.h" #include <cassert> @@ -111,26 +112,7 @@ static bool isAnnexKAvailable(std::optional<bool> &CacheVar, Preprocessor *PP, if (CacheVar.has_value()) return *CacheVar; - if (!LO.C11) - // TODO: How is "Annex K" available in C++ mode? - return (CacheVar = false).value(); - - assert(PP && "No Preprocessor registered."); - - if (!PP->isMacroDefined("__STDC_LIB_EXT1__") || - !PP->isMacroDefined("__STDC_WANT_LIB_EXT1__")) - return (CacheVar = false).value(); - - const auto *MI = - PP->getMacroInfo(PP->getIdentifierInfo("__STDC_WANT_LIB_EXT1__")); - if (!MI || MI->tokens_empty()) - return (CacheVar = false).value(); - - const Token &T = MI->tokens().back(); - if (!T.isLiteral() || !T.getLiteralData()) - return (CacheVar = false).value(); - - CacheVar = StringRef(T.getLiteralData(), T.getLength()) == "1"; + CacheVar = analysis::isAnnexKAvailable(PP, LO); return CacheVar.value(); } diff --git a/clang/include/clang/Analysis/AnnexKDetection.h b/clang/include/clang/Analysis/AnnexKDetection.h new file mode 100644 index 0000000000000..c1cb89309f042 --- /dev/null +++ b/clang/include/clang/Analysis/AnnexKDetection.h @@ -0,0 +1,61 @@ +//==- AnnexKDetection.h - Annex K availability detection --------*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file provides utilities for detecting C11 Annex K (Bounds-checking +// interfaces) availability. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ANALYSIS_ANNEXKDETECTION_H +#define LLVM_CLANG_ANALYSIS_ANNEXKDETECTION_H + +#include "clang/Basic/LangOptions.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/StringRef.h" + +namespace clang { +namespace analysis { + +/// Calculates whether Annex K is available for the current translation unit +/// based on the macro definitions and the language options. +/// +/// Annex K (Bounds-checking interfaces) is available when: +/// 1. C11 standard is enabled +/// 2. __STDC_LIB_EXT1__ macro is defined (indicates library support) +/// 3. __STDC_WANT_LIB_EXT1__ macro is defined and equals "1" (indicates user +/// opt-in) +/// +/// \param PP The preprocessor instance to check macro definitions. +/// \param LO The language options to check C11 standard. +/// \returns true if Annex K is available, false otherwise. +inline bool isAnnexKAvailable(Preprocessor *PP, const LangOptions &LO) { + if (!LO.C11) + return false; + + assert(PP && "No Preprocessor registered."); + + if (!PP->isMacroDefined("__STDC_LIB_EXT1__") || + !PP->isMacroDefined("__STDC_WANT_LIB_EXT1__")) + return false; + + const auto *MI = + PP->getMacroInfo(PP->getIdentifierInfo("__STDC_WANT_LIB_EXT1__")); + if (!MI || MI->tokens_empty()) + return false; + + const Token &T = MI->tokens().back(); + if (!T.isLiteral() || !T.getLiteralData()) + return false; + + return StringRef(T.getLiteralData(), T.getLength()) == "1"; +} + +} // namespace analysis +} // namespace clang + +#endif // LLVM_CLANG_ANALYSIS_ANNEXKDETECTION_H From a1b6624df7e2c750431adb19513734921433db46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Wed, 10 Dec 2025 10:41:59 +0100 Subject: [PATCH 08/13] implement the results of the rfc-discussion A string option is introduced: ReportMode, which can have 3 values: "all", "actionable" and "c11-only". --- clang/docs/analyzer/checkers.rst | 14 +++-- .../clang/StaticAnalyzer/Checkers/Checkers.td | 16 +++--- .../Checkers/CheckSecuritySyntaxOnly.cpp | 44 ++++++++++++--- clang/test/Analysis/analyzer-config.c | 2 +- ...ecated-buffer-handling-allow-without-c11.c | 53 ++++++++++++++++--- 5 files changed, 104 insertions(+), 25 deletions(-) diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst index 244fdb62a162d..16643d4dd3841 100644 --- a/clang/docs/analyzer/checkers.rst +++ b/clang/docs/analyzer/checkers.rst @@ -1785,12 +1785,18 @@ security.insecureAPI.DeprecatedOrUnsafeBufferHandling (C) strncpy(buf, "a", 1); // warn } -The ``ReportInC99AndEarlier`` option allows reporting warnings for these functions even when not compiling with C11 standard. These functions became deprecated in C11, but may still be problematic in earlier C standards. +The ``ReportMode`` option controls when warnings are reported: -To enable this option, use: -``-analyzer-config security.insecureAPI.DeprecatedOrUnsafeBufferHandling:ReportInC99AndEarlier=true``. +* ``all``: Reports all unsafe functions regardless of C standard or Annex K availability. Useful for security auditing and vulnerability scanning. -By default, this option is set to *false*. +* ``actionable``: Only reports when Annex K is available (C11 with ``__STDC_LIB_EXT1__`` and ``__STDC_WANT_LIB_EXT1__=1``). + +* ``c11-only``: Reports when C11 standard is enabled (does not take Annex K availability into account). + +To set this option, use: +``-analyzer-config security.insecureAPI.DeprecatedOrUnsafeBufferHandling:ReportMode=all`` + +By default, this option is set to *c11-only*. .. _security-MmapWriteExec: diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td index 1b7751221fb7b..73dd85f4867bb 100644 --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -908,12 +908,16 @@ def DeprecatedOrUnsafeBufferHandling Dependencies<[SecuritySyntaxChecker]>, CheckerOptions< [CmdLineOption< - Boolean, "ReportInC99AndEarlier", - "Allow reporting deprecated or unsafe buffer handling " - "functions even when not compiling with C11 standard. " - "These functions became deprecated in C11, but may still be " - "problematic in earlier C standards.", - "false", Released>, + String, "ReportMode", + "Controls when warnings are reported. \"all\" reports all " + "unsafe " + "functions regardless of C standard or Annex K availability. " + "\"actionable\" only reports when Annex K is available (C11 " + "with " + "__STDC_LIB_EXT1__ and __STDC_WANT_LIB_EXT1__=1). \"c11-only\" " + "reports when C11 standard is enabled (does not take Annex K " + "availability into account).", + "c11-only", Released>, ]>, Documentation<HasDocumentation>; diff --git a/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp index d4b56418535e0..496be06998578 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp @@ -10,10 +10,11 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/AST/StmtVisitor.h" #include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Analysis/AnnexKDetection.h" #include "clang/Basic/TargetInfo.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" @@ -34,6 +35,9 @@ static bool isArc4RandomAvailable(const ASTContext &Ctx) { } namespace { + +enum class ReportPolicy { All, Actionable, C11Only }; + struct ChecksFilter { bool check_bcmp = false; bool check_bcopy = false; @@ -50,7 +54,7 @@ struct ChecksFilter { bool check_UncheckedReturn = false; bool check_decodeValueOfObjCType = false; - bool reportDeprecatedOrUnsafeBufferHandlingInC99AndEarlier = false; + ReportPolicy ReportMode = ReportPolicy::C11Only; CheckerNameRef checkName_bcmp; CheckerNameRef checkName_bcopy; @@ -756,8 +760,22 @@ void WalkAST::checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE, if (!filter.check_DeprecatedOrUnsafeBufferHandling) return; - if (!(BR.getContext().getLangOpts().C11 || - filter.reportDeprecatedOrUnsafeBufferHandlingInC99AndEarlier)) + const bool ShouldReport = [this] { + const bool IsAnnexKAvailable = analysis::isAnnexKAvailable( + &BR.getPreprocessor(), BR.getContext().getLangOpts()); + const bool IsC11OrLaterStandard = BR.getContext().getLangOpts().C11; + + switch (filter.ReportMode) { + case ReportPolicy::All: + return true; + case ReportPolicy::Actionable: + return IsAnnexKAvailable; + case ReportPolicy::C11Only: + return IsC11OrLaterStandard; + } + }(); + + if (!ShouldReport) return; // Issue a warning. ArgIndex == -1: Deprecated but not unsafe (has size @@ -1122,9 +1140,21 @@ void ento::registerDeprecatedOrUnsafeBufferHandling(CheckerManager &Mgr) { Checker->filter.check_DeprecatedOrUnsafeBufferHandling = true; Checker->filter.checkName_DeprecatedOrUnsafeBufferHandling = Mgr.getCurrentCheckerName(); - Checker->filter.reportDeprecatedOrUnsafeBufferHandlingInC99AndEarlier = - Mgr.getAnalyzerOptions().getCheckerBooleanOption( - Mgr.getCurrentCheckerName(), "ReportInC99AndEarlier"); + + // Parse ReportMode option (defaults to C11Only for backward compatibility) + StringRef ReportModeStr = Mgr.getAnalyzerOptions().getCheckerStringOption( + Mgr.getCurrentCheckerName(), "ReportMode"); + if (ReportModeStr == "all") + Checker->filter.ReportMode = ReportPolicy::All; + else if (ReportModeStr == "actionable") + Checker->filter.ReportMode = ReportPolicy::Actionable; + else if (ReportModeStr == "c11-only") + Checker->filter.ReportMode = ReportPolicy::C11Only; + else + Mgr.reportInvalidCheckerOptionValue( + Checker, "ReportMode", + "ReportMode should be one of the folliwing values: \"all\", " + "\"actionable\" or \"c11-only\" (the default)"); } bool ento::shouldRegisterDeprecatedOrUnsafeBufferHandling( diff --git a/clang/test/Analysis/analyzer-config.c b/clang/test/Analysis/analyzer-config.c index adc90894557f6..c12a2218ce6b8 100644 --- a/clang/test/Analysis/analyzer-config.c +++ b/clang/test/Analysis/analyzer-config.c @@ -121,7 +121,7 @@ // CHECK-NEXT: region-store-small-struct-limit = 2 // CHECK-NEXT: report-in-main-source-file = false // CHECK-NEXT: security.cert.env.InvalidPtr:InvalidatingGetEnv = false -// CHECK-NEXT: security.insecureAPI.DeprecatedOrUnsafeBufferHandling:ReportInC99AndEarlier = false +// CHECK-NEXT: security.insecureAPI.DeprecatedOrUnsafeBufferHandling:ReportMode = c11-only // CHECK-NEXT: serialize-stats = false // CHECK-NEXT: silence-checkers = "" // CHECK-NEXT: stable-report-filename = false diff --git a/clang/test/Analysis/security-deprecated-buffer-handling-allow-without-c11.c b/clang/test/Analysis/security-deprecated-buffer-handling-allow-without-c11.c index f8686798f6819..e92733f0c4445 100644 --- a/clang/test/Analysis/security-deprecated-buffer-handling-allow-without-c11.c +++ b/clang/test/Analysis/security-deprecated-buffer-handling-allow-without-c11.c @@ -1,22 +1,61 @@ -// Test 1: Without C11 and without flag - should NOT warn -// RUN: %clang_analyze_cc1 %s -verify=c99-noflag -std=gnu99 \ -// RUN: -analyzer-checker=security.insecureAPI.DeprecatedOrUnsafeBufferHandling +// These cases should not warn -// Test 2: Without C11 but with flag enabled - should warn +// C99 with "all" mode // RUN: %clang_analyze_cc1 %s -verify=common -std=gnu99 \ // RUN: -analyzer-checker=security.insecureAPI.DeprecatedOrUnsafeBufferHandling \ -// RUN: -analyzer-config security.insecureAPI.DeprecatedOrUnsafeBufferHandling:ReportInC99AndEarlier=true +// RUN: -analyzer-config security.insecureAPI.DeprecatedOrUnsafeBufferHandling:ReportMode=all + +// C11 with default mode +// RUN: %clang_analyze_cc1 %s -verify=common -std=gnu11 \ +// RUN: -analyzer-checker=security.insecureAPI.DeprecatedOrUnsafeBufferHandling + +// C11 with "all" mode +// RUN: %clang_analyze_cc1 %s -verify=common -std=gnu11 \ +// RUN: -analyzer-checker=security.insecureAPI.DeprecatedOrUnsafeBufferHandling \ +// RUN: -analyzer-config security.insecureAPI.DeprecatedOrUnsafeBufferHandling:ReportMode=all + +// C11 with "c11-only" mode +// RUN: %clang_analyze_cc1 %s -verify=common -std=gnu11 \ +// RUN: -analyzer-checker=security.insecureAPI.DeprecatedOrUnsafeBufferHandling \ +// RUN: -analyzer-config security.insecureAPI.DeprecatedOrUnsafeBufferHandling:ReportMode=c11-only -// Test 3: With C11 - should warn (existing behavior) +// C11 with "actionable" mode and Annex K available // RUN: %clang_analyze_cc1 %s -verify=common -std=gnu11 \ +// RUN: -D__STDC_LIB_EXT1__=200509L -D__STDC_WANT_LIB_EXT1__=1 \ +// RUN: -analyzer-checker=security.insecureAPI.DeprecatedOrUnsafeBufferHandling \ +// RUN: -analyzer-config security.insecureAPI.DeprecatedOrUnsafeBufferHandling:ReportMode=actionable + +// These cases should not warn + +// C99 with default mode +// RUN: %clang_analyze_cc1 %s -verify=c99-default -std=gnu99 \ // RUN: -analyzer-checker=security.insecureAPI.DeprecatedOrUnsafeBufferHandling +// C99 with "actionable" mode and no Annex K +// RUN: %clang_analyze_cc1 %s -verify=c99-actionable -std=gnu99 \ +// RUN: -analyzer-checker=security.insecureAPI.DeprecatedOrUnsafeBufferHandling \ +// RUN: -analyzer-config security.insecureAPI.DeprecatedOrUnsafeBufferHandling:ReportMode=actionable + +// C99 with "c11-only" mode +// RUN: %clang_analyze_cc1 %s -verify=c99-c11only -std=gnu99 \ +// RUN: -analyzer-checker=security.insecureAPI.DeprecatedOrUnsafeBufferHandling \ +// RUN: -analyzer-config security.insecureAPI.DeprecatedOrUnsafeBufferHandling:ReportMode=c11-only + +// C11 with "actionable" mode and no Annex K +// RUN: %clang_analyze_cc1 %s -verify=c11-actionable-noannex -std=gnu11 \ +// RUN: -analyzer-checker=security.insecureAPI.DeprecatedOrUnsafeBufferHandling \ +// RUN: -analyzer-config security.insecureAPI.DeprecatedOrUnsafeBufferHandling:ReportMode=actionable + + #include "Inputs/system-header-simulator.h" extern char buf[128]; extern char src[128]; -// c99-noflag-no-diagnostics +// c99-default-no-diagnostics +// c99-actionable-no-diagnostics +// c99-c11only-no-diagnostics +// c11-actionable-noannex-no-diagnostics void test_memcpy(void) { memcpy(buf, src, 10); From b419211140be5f1943ce9cc645f72c9d33cc89df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Wed, 10 Dec 2025 10:59:06 +0100 Subject: [PATCH 09/13] rename test file --- ...t-c11.c => security-deprecated-buffer-handling-report-modes.c} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename clang/test/Analysis/{security-deprecated-buffer-handling-allow-without-c11.c => security-deprecated-buffer-handling-report-modes.c} (100%) diff --git a/clang/test/Analysis/security-deprecated-buffer-handling-allow-without-c11.c b/clang/test/Analysis/security-deprecated-buffer-handling-report-modes.c similarity index 100% rename from clang/test/Analysis/security-deprecated-buffer-handling-allow-without-c11.c rename to clang/test/Analysis/security-deprecated-buffer-handling-report-modes.c From 2ef82c8318c3f430ad77b36b076efe1e0098f244 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Mon, 5 Jan 2026 16:11:11 +0100 Subject: [PATCH 10/13] put detection function in a separate cpp file --- .../include/clang/Analysis/AnnexKDetection.h | 35 +++------------ clang/lib/Analysis/AnnexKDetection.cpp | 44 +++++++++++++++++++ clang/lib/Analysis/CMakeLists.txt | 1 + 3 files changed, 52 insertions(+), 28 deletions(-) create mode 100644 clang/lib/Analysis/AnnexKDetection.cpp diff --git a/clang/include/clang/Analysis/AnnexKDetection.h b/clang/include/clang/Analysis/AnnexKDetection.h index c1cb89309f042..3d2097f83b22a 100644 --- a/clang/include/clang/Analysis/AnnexKDetection.h +++ b/clang/include/clang/Analysis/AnnexKDetection.h @@ -14,12 +14,12 @@ #ifndef LLVM_CLANG_ANALYSIS_ANNEXKDETECTION_H #define LLVM_CLANG_ANALYSIS_ANNEXKDETECTION_H -#include "clang/Basic/LangOptions.h" -#include "clang/Lex/Preprocessor.h" -#include "llvm/ADT/StringRef.h" - namespace clang { -namespace analysis { +class Preprocessor; +class LangOptions; +} // namespace clang + +namespace clang::analysis { /// Calculates whether Annex K is available for the current translation unit /// based on the macro definitions and the language options. @@ -33,29 +33,8 @@ namespace analysis { /// \param PP The preprocessor instance to check macro definitions. /// \param LO The language options to check C11 standard. /// \returns true if Annex K is available, false otherwise. -inline bool isAnnexKAvailable(Preprocessor *PP, const LangOptions &LO) { - if (!LO.C11) - return false; - - assert(PP && "No Preprocessor registered."); - - if (!PP->isMacroDefined("__STDC_LIB_EXT1__") || - !PP->isMacroDefined("__STDC_WANT_LIB_EXT1__")) - return false; +[[nodiscard]] bool isAnnexKAvailable(Preprocessor *PP, const LangOptions &LO); - const auto *MI = - PP->getMacroInfo(PP->getIdentifierInfo("__STDC_WANT_LIB_EXT1__")); - if (!MI || MI->tokens_empty()) - return false; - - const Token &T = MI->tokens().back(); - if (!T.isLiteral() || !T.getLiteralData()) - return false; - - return StringRef(T.getLiteralData(), T.getLength()) == "1"; -} - -} // namespace analysis -} // namespace clang +} // namespace clang::analysis #endif // LLVM_CLANG_ANALYSIS_ANNEXKDETECTION_H diff --git a/clang/lib/Analysis/AnnexKDetection.cpp b/clang/lib/Analysis/AnnexKDetection.cpp new file mode 100644 index 0000000000000..44c3f90847548 --- /dev/null +++ b/clang/lib/Analysis/AnnexKDetection.cpp @@ -0,0 +1,44 @@ +//==- AnnexKDetection.cpp - Annex K availability detection --------*- C++ +//-*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains the implementation of utilities for detecting C11 Annex K +// (Bounds-checking interfaces) availability. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/AnnexKDetection.h" + +#include "clang/Basic/LangOptions.h" +#include "clang/Lex/Preprocessor.h" + +namespace clang::analysis { + +[[nodiscard]] bool isAnnexKAvailable(Preprocessor *PP, const LangOptions &LO) { + if (!LO.C11) + return false; + + assert(PP && "No Preprocessor registered."); + + if (!PP->isMacroDefined("__STDC_LIB_EXT1__") || + !PP->isMacroDefined("__STDC_WANT_LIB_EXT1__")) + return false; + + const auto *MI = + PP->getMacroInfo(PP->getIdentifierInfo("__STDC_WANT_LIB_EXT1__")); + if (!MI || MI->tokens_empty()) + return false; + + const Token &T = MI->tokens().back(); + if (!T.isLiteral() || !T.getLiteralData()) + return false; + + return StringRef(T.getLiteralData(), T.getLength()) == "1"; +} + +} // namespace clang::analysis diff --git a/clang/lib/Analysis/CMakeLists.txt b/clang/lib/Analysis/CMakeLists.txt index 1dbd4153d856f..cc14bf1c2d882 100644 --- a/clang/lib/Analysis/CMakeLists.txt +++ b/clang/lib/Analysis/CMakeLists.txt @@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS add_clang_library(clangAnalysis AnalysisDeclContext.cpp + AnnexKDetection.cpp BodyFarm.cpp CalledOnceCheck.cpp CFG.cpp From 9003d1a26e9d6fb42941ad401fe9bae108835bca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Mon, 5 Jan 2026 16:11:56 +0100 Subject: [PATCH 11/13] reflow checkers.td description --- .../clang/StaticAnalyzer/Checkers/Checkers.td | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td index 73dd85f4867bb..ab62d22cf25e5 100644 --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -910,15 +910,15 @@ def DeprecatedOrUnsafeBufferHandling [CmdLineOption< String, "ReportMode", "Controls when warnings are reported. \"all\" reports all " - "unsafe " - "functions regardless of C standard or Annex K availability. " - "\"actionable\" only reports when Annex K is available (C11 " - "with " - "__STDC_LIB_EXT1__ and __STDC_WANT_LIB_EXT1__=1). \"c11-only\" " - "reports when C11 standard is enabled (does not take Annex K " - "availability into account).", - "c11-only", Released>, -]>, + "unsafe functions regardless of C standard or Annex K " + "availability. \"actionable\" only reports when Annex K is " + "available (C11 with __STDC_LIB_EXT1__ and " + "__STDC_WANT_LIB_EXT1__=1). \"c11-only\" reports when C11 " + "standard is enabled (does not take Annex K availability into " + "account).", + "c11-only", + Released>, + ]>, Documentation<HasDocumentation>; def decodeValueOfObjCType : Checker<"decodeValueOfObjCType">, From e7112a7e8f4674a1a9252db5947cf5ed7bc0bf88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Mon, 5 Jan 2026 16:12:41 +0100 Subject: [PATCH 12/13] cache the detection result per TU --- .../Checkers/CheckSecuritySyntaxOnly.cpp | 57 +++++++++++-------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp index 496be06998578..cba49767609b8 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp @@ -72,6 +72,26 @@ struct ChecksFilter { CheckerNameRef checkName_decodeValueOfObjCType; }; +// Determine whether to report Annex K related checks based on the +// reporting policy. +[[nodiscard]] bool shouldReportAnnexKRelated(BugReporter &BR, + const ChecksFilter &Filter) { + const bool IsAnnexKAvailable = analysis::isAnnexKAvailable( + &BR.getPreprocessor(), BR.getContext().getLangOpts()); + const bool IsC11OrLaterStandard = BR.getContext().getLangOpts().C11; + + switch (Filter.ReportMode) { + case ReportPolicy::All: + return true; + case ReportPolicy::Actionable: + return IsAnnexKAvailable; + case ReportPolicy::C11Only: + return IsC11OrLaterStandard; + default: + llvm_unreachable("Unknown ReportPolicy value"); + } +}; + class WalkAST : public StmtVisitor<WalkAST> { BugReporter &BR; AnalysisDeclContext* AC; @@ -79,14 +99,18 @@ class WalkAST : public StmtVisitor<WalkAST> { IdentifierInfo *II_setid[num_setids]; const bool CheckRand; + + // Cache the result of Annex K availability check. + const bool ShouldReportAnnexKRelated; + const ChecksFilter &filter; public: - WalkAST(BugReporter &br, AnalysisDeclContext* ac, - const ChecksFilter &f) - : BR(br), AC(ac), II_setid(), - CheckRand(isArc4RandomAvailable(BR.getContext())), - filter(f) {} + WalkAST(BugReporter &br, AnalysisDeclContext *ac, const ChecksFilter &f) + : BR(br), AC(ac), II_setid(), + CheckRand(isArc4RandomAvailable(BR.getContext())), + ShouldReportAnnexKRelated(shouldReportAnnexKRelated(br, f)), filter(f) { + } // Statement visitor methods. void VisitCallExpr(CallExpr *CE); @@ -757,25 +781,8 @@ void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) { void WalkAST::checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE, const FunctionDecl *FD) { - if (!filter.check_DeprecatedOrUnsafeBufferHandling) - return; - - const bool ShouldReport = [this] { - const bool IsAnnexKAvailable = analysis::isAnnexKAvailable( - &BR.getPreprocessor(), BR.getContext().getLangOpts()); - const bool IsC11OrLaterStandard = BR.getContext().getLangOpts().C11; - - switch (filter.ReportMode) { - case ReportPolicy::All: - return true; - case ReportPolicy::Actionable: - return IsAnnexKAvailable; - case ReportPolicy::C11Only: - return IsC11OrLaterStandard; - } - }(); - - if (!ShouldReport) + if (!filter.check_DeprecatedOrUnsafeBufferHandling || + !ShouldReportAnnexKRelated) return; // Issue a warning. ArgIndex == -1: Deprecated but not unsafe (has size @@ -1153,7 +1160,7 @@ void ento::registerDeprecatedOrUnsafeBufferHandling(CheckerManager &Mgr) { else Mgr.reportInvalidCheckerOptionValue( Checker, "ReportMode", - "ReportMode should be one of the folliwing values: \"all\", " + "ReportMode should be one of the following values: \"all\", " "\"actionable\" or \"c11-only\" (the default)"); } From 45f15d13aba841ef4c43eeb28338f4e874fbcad0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Mon, 5 Jan 2026 20:31:49 +0100 Subject: [PATCH 13/13] introduce defines to test case for more readability --- ...-deprecated-buffer-handling-report-modes.c | 100 +++++++++++++----- 1 file changed, 73 insertions(+), 27 deletions(-) diff --git a/clang/test/Analysis/security-deprecated-buffer-handling-report-modes.c b/clang/test/Analysis/security-deprecated-buffer-handling-report-modes.c index e92733f0c4445..2dffc50e2e8d3 100644 --- a/clang/test/Analysis/security-deprecated-buffer-handling-report-modes.c +++ b/clang/test/Analysis/security-deprecated-buffer-handling-report-modes.c @@ -1,50 +1,96 @@ -// These cases should not warn +// DEFINE: %{base_cmd} = %clang_analyze_cc1 %s \ +// DEFINE: -analyzer-checker=security.insecureAPI.DeprecatedOrUnsafeBufferHandling + +// DEFINE: %{verify_flag} = + +// DEFINE: %{config_flag_unset} = +// DEFINE: %{config_flag_all} = -analyzer-config security.insecureAPI.DeprecatedOrUnsafeBufferHandling:ReportMode=all +// DEFINE: %{config_flag_actionable} = -analyzer-config security.insecureAPI.DeprecatedOrUnsafeBufferHandling:ReportMode=actionable +// DEFINE: %{config_flag_c11_only} = -analyzer-config security.insecureAPI.DeprecatedOrUnsafeBufferHandling:ReportMode=c11-only +// DEFINE: %{config_flag} = %{config_flag_unset} + +// DEFINE: %{std_flag_c99} = -std=gnu99 +// DEFINE: %{std_flag_c11} = -std=gnu11 +// DEFINE: %{std_flag} = %{std_flag_c99} + +// DEFINE: %{annexk_defines_unset} = +// DEFINE: %{annexk_defines_set} = -D__STDC_LIB_EXT1__=200509L -D__STDC_WANT_LIB_EXT1__=1 +// DEFINE: %{annexk_flag} = %{annexk_defines_unset} + +// DEFINE: %{run_cmd} = %{base_cmd} %{verify_flag} %{std_flag} %{annexk_flag} %{config_flag} + +// These cases should warn + +// REDEFINE: %{verify_flag} = -verify=common +// REDEFINE: %{std_flag} = %{std_flag_c99} +// REDEFINE: %{annexk_flag} = %{annexk_defines_unset} +// REDEFINE: %{config_flag} = %{config_flag_all} +// RUN: %{run_cmd} // C99 with "all" mode -// RUN: %clang_analyze_cc1 %s -verify=common -std=gnu99 \ -// RUN: -analyzer-checker=security.insecureAPI.DeprecatedOrUnsafeBufferHandling \ -// RUN: -analyzer-config security.insecureAPI.DeprecatedOrUnsafeBufferHandling:ReportMode=all +// REDEFINE: %{verify_flag} = -verify=common +// REDEFINE: %{std_flag} = %{std_flag_c99} +// REDEFINE: %{annexk_flag} = %{annexk_defines_unset} +// REDEFINE: %{config_flag} = %{config_flag_all} +// RUN: %{run_cmd} // C11 with default mode -// RUN: %clang_analyze_cc1 %s -verify=common -std=gnu11 \ -// RUN: -analyzer-checker=security.insecureAPI.DeprecatedOrUnsafeBufferHandling +// REDEFINE: %{verify_flag} = -verify=common +// REDEFINE: %{std_flag} = %{std_flag_c11} +// REDEFINE: %{annexk_flag} = %{annexk_defines_unset} +// REDEFINE: %{config_flag} = %{config_flag_unset} +// RUN: %{run_cmd} // C11 with "all" mode -// RUN: %clang_analyze_cc1 %s -verify=common -std=gnu11 \ -// RUN: -analyzer-checker=security.insecureAPI.DeprecatedOrUnsafeBufferHandling \ -// RUN: -analyzer-config security.insecureAPI.DeprecatedOrUnsafeBufferHandling:ReportMode=all +// REDEFINE: %{verify_flag} = -verify=common +// REDEFINE: %{std_flag} = %{std_flag_c11} +// REDEFINE: %{annexk_flag} = %{annexk_defines_unset} +// REDEFINE: %{config_flag} = %{config_flag_all} +// RUN: %{run_cmd} // C11 with "c11-only" mode -// RUN: %clang_analyze_cc1 %s -verify=common -std=gnu11 \ -// RUN: -analyzer-checker=security.insecureAPI.DeprecatedOrUnsafeBufferHandling \ -// RUN: -analyzer-config security.insecureAPI.DeprecatedOrUnsafeBufferHandling:ReportMode=c11-only +// REDEFINE: %{verify_flag} = -verify=common +// REDEFINE: %{std_flag} = %{std_flag_c11} +// REDEFINE: %{annexk_flag} = %{annexk_defines_unset} +// REDEFINE: %{config_flag} = %{config_flag_c11_only} +// RUN: %{run_cmd} // C11 with "actionable" mode and Annex K available -// RUN: %clang_analyze_cc1 %s -verify=common -std=gnu11 \ -// RUN: -D__STDC_LIB_EXT1__=200509L -D__STDC_WANT_LIB_EXT1__=1 \ -// RUN: -analyzer-checker=security.insecureAPI.DeprecatedOrUnsafeBufferHandling \ -// RUN: -analyzer-config security.insecureAPI.DeprecatedOrUnsafeBufferHandling:ReportMode=actionable +// REDEFINE: %{verify_flag} = -verify=common +// REDEFINE: %{std_flag} = %{std_flag_c11} +// REDEFINE: %{annexk_flag} = %{annexk_defines_set} +// REDEFINE: %{config_flag} = %{config_flag_actionable} +// RUN: %{run_cmd} // These cases should not warn // C99 with default mode -// RUN: %clang_analyze_cc1 %s -verify=c99-default -std=gnu99 \ -// RUN: -analyzer-checker=security.insecureAPI.DeprecatedOrUnsafeBufferHandling +// REDEFINE: %{verify_flag} = -verify=c99-default +// REDEFINE: %{std_flag} = %{std_flag_c99} +// REDEFINE: %{annexk_flag} = %{annexk_defines_unset} +// REDEFINE: %{config_flag} = %{config_flag_unset} +// RUN: %{run_cmd} // C99 with "actionable" mode and no Annex K -// RUN: %clang_analyze_cc1 %s -verify=c99-actionable -std=gnu99 \ -// RUN: -analyzer-checker=security.insecureAPI.DeprecatedOrUnsafeBufferHandling \ -// RUN: -analyzer-config security.insecureAPI.DeprecatedOrUnsafeBufferHandling:ReportMode=actionable +// REDEFINE: %{verify_flag} = -verify=c99-actionable +// REDEFINE: %{std_flag} = %{std_flag_c99} +// REDEFINE: %{annexk_flag} = %{annexk_defines_unset} +// REDEFINE: %{config_flag} = %{config_flag_actionable} +// RUN: %{run_cmd} // C99 with "c11-only" mode -// RUN: %clang_analyze_cc1 %s -verify=c99-c11only -std=gnu99 \ -// RUN: -analyzer-checker=security.insecureAPI.DeprecatedOrUnsafeBufferHandling \ -// RUN: -analyzer-config security.insecureAPI.DeprecatedOrUnsafeBufferHandling:ReportMode=c11-only +// REDEFINE: %{verify_flag} = -verify=c99-c11only +// REDEFINE: %{std_flag} = %{std_flag_c99} +// REDEFINE: %{annexk_flag} = %{annexk_defines_unset} +// REDEFINE: %{config_flag} = %{config_flag_c11_only} +// RUN: %{run_cmd} // C11 with "actionable" mode and no Annex K -// RUN: %clang_analyze_cc1 %s -verify=c11-actionable-noannex -std=gnu11 \ -// RUN: -analyzer-checker=security.insecureAPI.DeprecatedOrUnsafeBufferHandling \ -// RUN: -analyzer-config security.insecureAPI.DeprecatedOrUnsafeBufferHandling:ReportMode=actionable +// REDEFINE: %{verify_flag} = -verify=c11-actionable-noannex +// REDEFINE: %{std_flag} = %{std_flag_c11} +// REDEFINE: %{annexk_flag} = %{annexk_defines_unset} +// REDEFINE: %{config_flag} = %{config_flag_actionable} +// RUN: %{run_cmd} #include "Inputs/system-header-simulator.h" _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
