https://github.com/necto updated https://github.com/llvm/llvm-project/pull/196298
>From fc1e8b138b8f7a826eb2d79789cb44c518be727a Mon Sep 17 00:00:00 2001 From: Arseniy Zaostrovnykh <[email protected]> Date: Thu, 7 May 2026 14:01:31 +0200 Subject: [PATCH 01/11] Demonstrate CTU import failure when mtime is advanced --- clang/test/Analysis/ctu/reusable-pch.c | 42 ++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 clang/test/Analysis/ctu/reusable-pch.c diff --git a/clang/test/Analysis/ctu/reusable-pch.c b/clang/test/Analysis/ctu/reusable-pch.c new file mode 100644 index 0000000000000..6f42cd3c90097 --- /dev/null +++ b/clang/test/Analysis/ctu/reusable-pch.c @@ -0,0 +1,42 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t + +// Step 1: Build PCH and defmap. +// RUN: %clang_cc1 -x c -emit-pch -o %t/other.c.ast %t/other.c +// RUN: %clang_extdef_map %t/other.c -- -c -x c > %t/externalDefMap.tmp.txt +// RUN: sed 's| .*other\.c| other.c.ast|' %t/externalDefMap.tmp.txt > %t/externalDefMap.txt + +// Step 2: Run CTU using the PCH - the division by zero is found via inlining. +// RUN: %clang_cc1 -analyze \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ +// RUN: -analyzer-config ctu-dir=%t \ +// RUN: -verify %t/main.c + +// Step 3: Advance mtime of the source from which PCH was built. +// RUN: %python -c "import os, sys, time; os.utime(sys.argv[1], (time.time() + 120, time.time() + 120))" %t/other.c + +// Step 4: Run CTU using the now-stale PCH - it breaks. +// RUN: not %clang_cc1 -analyze \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ +// RUN: -analyzer-config ctu-dir=%t \ +// RUN: %t/main.c 2>&1 | FileCheck --check-prefix=BREAKS %s + +// BREAKS: file '{{.*}}other.c' has been modified since the precompiled file '{{.*}}other.c.ast' was built + +//--- main.c +// Without CTU, always_zero() has an unknown return value so no bug is found. +// With CTU, always_zero() is inlined and its return value (0) is known, +// exposing the division by zero. + +int always_zero(void); + +void f(void) { + int x = always_zero(); + (void)(1 / x); // expected-warning{{Division by zero}} +} + +//--- other.c +int always_zero(void) { return 0; } >From f7ddef412640dff8f054e8b786d0181a8699a6d6 Mon Sep 17 00:00:00 2001 From: Arseniy Zaostrovnykh <[email protected]> Date: Thu, 7 May 2026 14:08:29 +0200 Subject: [PATCH 02/11] Forward ValidateASTInputFilesContent when importing AST dumps --- clang/lib/Frontend/ASTUnit.cpp | 7 ++++++- clang/test/Analysis/ctu/reusable-pch.c | 12 ++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/clang/lib/Frontend/ASTUnit.cpp b/clang/lib/Frontend/ASTUnit.cpp index 05ae1f348f920..df6f85560227f 100644 --- a/clang/lib/Frontend/ASTUnit.cpp +++ b/clang/lib/Frontend/ASTUnit.cpp @@ -780,11 +780,16 @@ std::unique_ptr<ASTUnit> ASTUnit::LoadFromASTFile( DisableValidationForModuleKind::None; if (::getenv("LIBCLANG_DISABLE_PCH_VALIDATION")) disableValid = DisableValidationForModuleKind::All; + bool ValidateASTInputFilesContent = HSOpts.ValidateASTInputFilesContent; + AST->Reader = llvm::makeIntrusiveRefCnt<ASTReader>( *AST->PP, *AST->ModCache, AST->Ctx.get(), PCHContainerRdr, *AST->CodeGenOpts, ArrayRef<std::shared_ptr<ModuleFileExtension>>(), /*isysroot=*/"", - /*DisableValidationKind=*/disableValid, AllowASTWithCompilerErrors); + /*DisableValidationKind=*/disableValid, AllowASTWithCompilerErrors, + /*AllowConfigurationMismatch=*/false, + /*ValidateSystemInputs=*/false, + /*ForceValidateUserInputs=*/true, ValidateASTInputFilesContent); // Attach the AST reader to the AST context as an external AST source, so that // declarations will be deserialized from the AST file as needed. diff --git a/clang/test/Analysis/ctu/reusable-pch.c b/clang/test/Analysis/ctu/reusable-pch.c index 6f42cd3c90097..1490894f061a1 100644 --- a/clang/test/Analysis/ctu/reusable-pch.c +++ b/clang/test/Analysis/ctu/reusable-pch.c @@ -3,12 +3,13 @@ // RUN: split-file %s %t // Step 1: Build PCH and defmap. -// RUN: %clang_cc1 -x c -emit-pch -o %t/other.c.ast %t/other.c +// RUN: %clang_cc1 -x c -emit-pch -fvalidate-ast-input-files-content -o %t/other.c.ast %t/other.c // RUN: %clang_extdef_map %t/other.c -- -c -x c > %t/externalDefMap.tmp.txt // RUN: sed 's| .*other\.c| other.c.ast|' %t/externalDefMap.tmp.txt > %t/externalDefMap.txt // Step 2: Run CTU using the PCH - the division by zero is found via inlining. // RUN: %clang_cc1 -analyze \ +// RUN: -fvalidate-ast-input-files-content \ // RUN: -analyzer-checker=core \ // RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ // RUN: -analyzer-config ctu-dir=%t \ @@ -17,14 +18,13 @@ // Step 3: Advance mtime of the source from which PCH was built. // RUN: %python -c "import os, sys, time; os.utime(sys.argv[1], (time.time() + 120, time.time() + 120))" %t/other.c -// Step 4: Run CTU using the now-stale PCH - it breaks. -// RUN: not %clang_cc1 -analyze \ +// Step 4: Run CTU using the "stale" PCH +// RUN: %clang_cc1 -analyze \ +// RUN: -fvalidate-ast-input-files-content \ // RUN: -analyzer-checker=core \ // RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ // RUN: -analyzer-config ctu-dir=%t \ -// RUN: %t/main.c 2>&1 | FileCheck --check-prefix=BREAKS %s - -// BREAKS: file '{{.*}}other.c' has been modified since the precompiled file '{{.*}}other.c.ast' was built +// RUN: -verify %t/main.c //--- main.c // Without CTU, always_zero() has an unknown return value so no bug is found. >From 2103ae99e36b37080a7a4ef3b70d1a4d2514c14d Mon Sep 17 00:00:00 2001 From: Arseniy Zaostrovnykh <[email protected]> Date: Mon, 11 May 2026 12:26:53 +0200 Subject: [PATCH 03/11] Update clang/test/Analysis/ctu/reusable-pch.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Balázs Benics <[email protected]> --- clang/test/Analysis/ctu/reusable-pch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/test/Analysis/ctu/reusable-pch.c b/clang/test/Analysis/ctu/reusable-pch.c index 1490894f061a1..98d6f2c8129cc 100644 --- a/clang/test/Analysis/ctu/reusable-pch.c +++ b/clang/test/Analysis/ctu/reusable-pch.c @@ -18,7 +18,7 @@ // Step 3: Advance mtime of the source from which PCH was built. // RUN: %python -c "import os, sys, time; os.utime(sys.argv[1], (time.time() + 120, time.time() + 120))" %t/other.c -// Step 4: Run CTU using the "stale" PCH +// Step 4: Run CTU using the "stale" PCH, and it should still load it and find the division by zero bug. // RUN: %clang_cc1 -analyze \ // RUN: -fvalidate-ast-input-files-content \ // RUN: -analyzer-checker=core \ >From 50654dcfef54806eee91488bb14e4cc678f55562 Mon Sep 17 00:00:00 2001 From: Arseniy Zaostrovnykh <[email protected]> Date: Mon, 11 May 2026 13:33:35 +0200 Subject: [PATCH 04/11] [NFC] Inline the option --- clang/lib/Frontend/ASTUnit.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clang/lib/Frontend/ASTUnit.cpp b/clang/lib/Frontend/ASTUnit.cpp index df6f85560227f..73f6319f324ee 100644 --- a/clang/lib/Frontend/ASTUnit.cpp +++ b/clang/lib/Frontend/ASTUnit.cpp @@ -780,7 +780,6 @@ std::unique_ptr<ASTUnit> ASTUnit::LoadFromASTFile( DisableValidationForModuleKind::None; if (::getenv("LIBCLANG_DISABLE_PCH_VALIDATION")) disableValid = DisableValidationForModuleKind::All; - bool ValidateASTInputFilesContent = HSOpts.ValidateASTInputFilesContent; AST->Reader = llvm::makeIntrusiveRefCnt<ASTReader>( *AST->PP, *AST->ModCache, AST->Ctx.get(), PCHContainerRdr, @@ -789,7 +788,7 @@ std::unique_ptr<ASTUnit> ASTUnit::LoadFromASTFile( /*DisableValidationKind=*/disableValid, AllowASTWithCompilerErrors, /*AllowConfigurationMismatch=*/false, /*ValidateSystemInputs=*/false, - /*ForceValidateUserInputs=*/true, ValidateASTInputFilesContent); + /*ForceValidateUserInputs=*/true, HSOpts.ValidateASTInputFilesContent); // Attach the AST reader to the AST context as an external AST source, so that // declarations will be deserialized from the AST file as needed. >From 67c7da349552c38cc2c5d69362d0f1c7bc4a4f17 Mon Sep 17 00:00:00 2001 From: Arseniy Zaostrovnykh <[email protected]> Date: Mon, 11 May 2026 13:33:48 +0200 Subject: [PATCH 05/11] [NFC] Use clang_analyze_cc1 --- clang/test/Analysis/ctu/reusable-pch.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clang/test/Analysis/ctu/reusable-pch.c b/clang/test/Analysis/ctu/reusable-pch.c index 98d6f2c8129cc..2d68cb1924ae7 100644 --- a/clang/test/Analysis/ctu/reusable-pch.c +++ b/clang/test/Analysis/ctu/reusable-pch.c @@ -8,7 +8,7 @@ // RUN: sed 's| .*other\.c| other.c.ast|' %t/externalDefMap.tmp.txt > %t/externalDefMap.txt // Step 2: Run CTU using the PCH - the division by zero is found via inlining. -// RUN: %clang_cc1 -analyze \ +// RUN: %clang_analyze_cc1 \ // RUN: -fvalidate-ast-input-files-content \ // RUN: -analyzer-checker=core \ // RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ @@ -18,8 +18,9 @@ // Step 3: Advance mtime of the source from which PCH was built. // RUN: %python -c "import os, sys, time; os.utime(sys.argv[1], (time.time() + 120, time.time() + 120))" %t/other.c +// Step 4: Run CTU using the "stale" PCH // Step 4: Run CTU using the "stale" PCH, and it should still load it and find the division by zero bug. -// RUN: %clang_cc1 -analyze \ +// RUN: %clang_analyze_cc1 \ // RUN: -fvalidate-ast-input-files-content \ // RUN: -analyzer-checker=core \ // RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ >From 932842760a1744a002cf32fe7e6e120c0276ec73 Mon Sep 17 00:00:00 2001 From: Arseniy Zaostrovnykh <[email protected]> Date: Mon, 11 May 2026 13:42:01 +0200 Subject: [PATCH 06/11] Use touch instead of a python script --- clang/test/Analysis/ctu/reusable-pch.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/test/Analysis/ctu/reusable-pch.c b/clang/test/Analysis/ctu/reusable-pch.c index 2d68cb1924ae7..bad15a99887eb 100644 --- a/clang/test/Analysis/ctu/reusable-pch.c +++ b/clang/test/Analysis/ctu/reusable-pch.c @@ -15,8 +15,8 @@ // RUN: -analyzer-config ctu-dir=%t \ // RUN: -verify %t/main.c -// Step 3: Advance mtime of the source from which PCH was built. -// RUN: %python -c "import os, sys, time; os.utime(sys.argv[1], (time.time() + 120, time.time() + 120))" %t/other.c +// Step 3: Set mtime of the source from which PCH was built to the year 3000 (way in the future). +// RUN: touch -t 300001010000 %t/other.c // Step 4: Run CTU using the "stale" PCH // Step 4: Run CTU using the "stale" PCH, and it should still load it and find the division by zero bug. >From 0da55dc73c71440272bea37436f2207e48082403 Mon Sep 17 00:00:00 2001 From: Arseniy Zaostrovnykh <[email protected]> Date: Mon, 11 May 2026 13:48:22 +0200 Subject: [PATCH 07/11] [NFC] use `sed -e` explicitly --- clang/test/Analysis/ctu/reusable-pch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/test/Analysis/ctu/reusable-pch.c b/clang/test/Analysis/ctu/reusable-pch.c index bad15a99887eb..5a7aaeb69ff04 100644 --- a/clang/test/Analysis/ctu/reusable-pch.c +++ b/clang/test/Analysis/ctu/reusable-pch.c @@ -5,7 +5,7 @@ // Step 1: Build PCH and defmap. // RUN: %clang_cc1 -x c -emit-pch -fvalidate-ast-input-files-content -o %t/other.c.ast %t/other.c // RUN: %clang_extdef_map %t/other.c -- -c -x c > %t/externalDefMap.tmp.txt -// RUN: sed 's| .*other\.c| other.c.ast|' %t/externalDefMap.tmp.txt > %t/externalDefMap.txt +// RUN: sed -e 's| .*other\.c| other.c.ast|' %t/externalDefMap.tmp.txt > %t/externalDefMap.txt // Step 2: Run CTU using the PCH - the division by zero is found via inlining. // RUN: %clang_analyze_cc1 \ >From 8dfb808ae52c7471f6a703d5c5b0374bfd203513 Mon Sep 17 00:00:00 2001 From: Arseniy Zaostrovnykh <[email protected]> Date: Mon, 11 May 2026 13:53:02 +0200 Subject: [PATCH 08/11] Update clang/test/Analysis/ctu/reusable-pch.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Balázs Benics <[email protected]> --- clang/test/Analysis/ctu/reusable-pch.c | 1 - 1 file changed, 1 deletion(-) diff --git a/clang/test/Analysis/ctu/reusable-pch.c b/clang/test/Analysis/ctu/reusable-pch.c index 5a7aaeb69ff04..d01fb7df2b7cd 100644 --- a/clang/test/Analysis/ctu/reusable-pch.c +++ b/clang/test/Analysis/ctu/reusable-pch.c @@ -18,7 +18,6 @@ // Step 3: Set mtime of the source from which PCH was built to the year 3000 (way in the future). // RUN: touch -t 300001010000 %t/other.c -// Step 4: Run CTU using the "stale" PCH // Step 4: Run CTU using the "stale" PCH, and it should still load it and find the division by zero bug. // RUN: %clang_analyze_cc1 \ // RUN: -fvalidate-ast-input-files-content \ >From 4abf54520d484131ad01216ee95074344527c8af Mon Sep 17 00:00:00 2001 From: Arseniy Zaostrovnykh <[email protected]> Date: Mon, 11 May 2026 13:53:48 +0200 Subject: [PATCH 09/11] [NFC] Remove uncalled-for empty line --- clang/lib/Frontend/ASTUnit.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/clang/lib/Frontend/ASTUnit.cpp b/clang/lib/Frontend/ASTUnit.cpp index 73f6319f324ee..83fe82365b008 100644 --- a/clang/lib/Frontend/ASTUnit.cpp +++ b/clang/lib/Frontend/ASTUnit.cpp @@ -780,7 +780,6 @@ std::unique_ptr<ASTUnit> ASTUnit::LoadFromASTFile( DisableValidationForModuleKind::None; if (::getenv("LIBCLANG_DISABLE_PCH_VALIDATION")) disableValid = DisableValidationForModuleKind::All; - AST->Reader = llvm::makeIntrusiveRefCnt<ASTReader>( *AST->PP, *AST->ModCache, AST->Ctx.get(), PCHContainerRdr, *AST->CodeGenOpts, ArrayRef<std::shared_ptr<ModuleFileExtension>>(), >From 191bb9fd03c3910de8ffae2a4537425cdeb3bb0c Mon Sep 17 00:00:00 2001 From: Arseniy Zaostrovnykh <[email protected]> Date: Mon, 11 May 2026 14:06:37 +0200 Subject: [PATCH 10/11] Add a negative test case --- clang/test/Analysis/ctu/reusable-pch.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/clang/test/Analysis/ctu/reusable-pch.c b/clang/test/Analysis/ctu/reusable-pch.c index d01fb7df2b7cd..87ad3845317af 100644 --- a/clang/test/Analysis/ctu/reusable-pch.c +++ b/clang/test/Analysis/ctu/reusable-pch.c @@ -26,11 +26,23 @@ // RUN: -analyzer-config ctu-dir=%t \ // RUN: -verify %t/main.c +// Step 4': Run without content validation: CTU import failure +// RUN: not %clang_analyze_cc1 \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ +// RUN: -analyzer-config ctu-dir=%t \ +// RUN: -verify %t/main.c 2>&1 | FileCheck %s + //--- main.c // Without CTU, always_zero() has an unknown return value so no bug is found. // With CTU, always_zero() is inlined and its return value (0) is known, // exposing the division by zero. +// CHECK: fatal error: file '{{.*}}/other.c' has been modified since the precompiled file '{{.*}}/other.c.ast' was built +// CHECK: note: mtime changed from expected +// CHECK: note: earlier input file validation has covered only user files +// CHECK: import of an external symbol for CTU failed: Failed to load external AST source. + int always_zero(void); void f(void) { >From 6483fec571ae2fedd8e4c52ade27ebe772096357 Mon Sep 17 00:00:00 2001 From: Arseniy Zaostrovnykh <[email protected]> Date: Mon, 11 May 2026 14:21:05 +0200 Subject: [PATCH 11/11] Factor out common run parameters into ctu_analysis --- clang/test/Analysis/ctu/reusable-pch.c | 37 +++++++++++--------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/clang/test/Analysis/ctu/reusable-pch.c b/clang/test/Analysis/ctu/reusable-pch.c index 87ad3845317af..bf92775143825 100644 --- a/clang/test/Analysis/ctu/reusable-pch.c +++ b/clang/test/Analysis/ctu/reusable-pch.c @@ -1,37 +1,32 @@ // RUN: rm -rf %t // RUN: mkdir -p %t // RUN: split-file %s %t +// +// DEFINE: %{ctu_analysis} = %clang_analyze_cc1 \ +// DEFINE: -analyzer-checker=core \ +// DEFINE: -analyzer-config experimental-enable-naive-ctu-analysis=true \ +// DEFINE: -analyzer-config ctu-dir=%t \ +// DEFINE: -verify // Step 1: Build PCH and defmap. // RUN: %clang_cc1 -x c -emit-pch -fvalidate-ast-input-files-content -o %t/other.c.ast %t/other.c // RUN: %clang_extdef_map %t/other.c -- -c -x c > %t/externalDefMap.tmp.txt // RUN: sed -e 's| .*other\.c| other.c.ast|' %t/externalDefMap.tmp.txt > %t/externalDefMap.txt -// Step 2: Run CTU using the PCH - the division by zero is found via inlining. -// RUN: %clang_analyze_cc1 \ -// RUN: -fvalidate-ast-input-files-content \ -// RUN: -analyzer-checker=core \ -// RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ -// RUN: -analyzer-config ctu-dir=%t \ -// RUN: -verify %t/main.c +// Step 2a: Run CTU using the PCH - the division by zero is found via inlining. +// RUN: %{ctu_analysis} %t/main.c + +// Step 2b: Run with content validation - no difference. +// RUN: %{ctu_analysis} %t/main.c -fvalidate-ast-input-files-content // Step 3: Set mtime of the source from which PCH was built to the year 3000 (way in the future). // RUN: touch -t 300001010000 %t/other.c -// Step 4: Run CTU using the "stale" PCH, and it should still load it and find the division by zero bug. -// RUN: %clang_analyze_cc1 \ -// RUN: -fvalidate-ast-input-files-content \ -// RUN: -analyzer-checker=core \ -// RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ -// RUN: -analyzer-config ctu-dir=%t \ -// RUN: -verify %t/main.c - -// Step 4': Run without content validation: CTU import failure -// RUN: not %clang_analyze_cc1 \ -// RUN: -analyzer-checker=core \ -// RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ -// RUN: -analyzer-config ctu-dir=%t \ -// RUN: -verify %t/main.c 2>&1 | FileCheck %s +// Step 4a: Run CTU using the "stale" PCH, and it should still load it and find the division by zero bug. +// RUN: %{ctu_analysis} -fvalidate-ast-input-files-content %t/main.c + +// Step 4b: Run without content validation: CTU import failure +// RUN: not %{ctu_analysis} %t/main.c 2>&1 | FileCheck %s //--- main.c // Without CTU, always_zero() has an unknown return value so no bug is found. _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
