Skip to content

Clang segfault when using C++ modules and a precompiled header #105994

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
ghost opened this issue Aug 25, 2024 · 24 comments
Closed

Clang segfault when using C++ modules and a precompiled header #105994

ghost opened this issue Aug 25, 2024 · 24 comments
Labels
clang:modules C++20 modules and Clang Header Modules crash-on-valid

Comments

@ghost
Copy link

ghost commented Aug 25, 2024

Hello,
when compiling a project that uses 4 module partitions and a precompiled header with #include <iostream> , I get a segfault.

I've originally encountered this problem on MacOS with llvm clang-18.1.8 . I've also been able to reproduce it in an Ubuntu docker container (on the same machine) with: Ubuntu clang version 18.1.3 (1ubuntu1) Target: aarch64-unknown-linux-gnu.

The machine this happens on is Apple Silicon M2 Pro (mac mini). Host architecture is aarch64.

Reproduction instructions using docker:

  1. docker run --rm -it -v /local/path/to/repro/project:/src ubuntu bash
  2. apt-get update && apt-get install -y cmake clang clang-tools ninja-build
  3. cd /src
  4. PATH="/usr/lib/llvm-18/bin:$PATH" CXX=clang cmake -S . -B build -G Ninja
  5. cmake --build build

Should get the following output:

FAILED: CMakeFiles/pch-cppm-problem-repro.dir/Mod.cppm.o CMakeFiles/pch-cppm-problem-repro.dir/mod.pcm 
/usr/lib/llvm-18/bin/clang   -std=gnu++23 -Winvalid-pch -Xclang -include-pch -Xclang /src/build/CMakeFiles/pch-cppm-problem-repro.dir/cmake_pch.hxx.pch -Xclang -include -Xclang /src/build/CMakeFiles/pch-cppm-problem-repro.dir/cmake_pch.hxx -MD -MT CMakeFiles/pch-cppm-problem-repro.dir/Mod.cppm.o -MF CMakeFiles/pch-cppm-problem-repro.dir/Mod.cppm.o.d @CMakeFiles/pch-cppm-problem-repro.dir/Mod.cppm.o.modmap -o CMakeFiles/pch-cppm-problem-repro.dir/Mod.cppm.o -c /src/Mod.cppm
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace, preprocessed source, and associated run script.
Stack dump:
0.	Program arguments: /usr/lib/llvm-18/bin/clang -cc1 -triple aarch64-unknown-linux-gnu -emit-obj -mrelax-all -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name Mod.cppm -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=non-leaf -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu generic -target-feature +v8a -target-feature +fp-armv8 -target-feature +neon -target-abi aapcs -debugger-tuning=gdb -fdebug-compilation-dir=/src/build -fcoverage-compilation-dir=/src/build -resource-dir /usr/lib/llvm-18/lib/clang/18 -Winvalid-pch -std=gnu++23 -ferror-limit 19 -fno-signed-char -fgnuc-version=4.2.1 -fmodule-file=mod:part1=CMakeFiles/pch-cppm-problem-repro.dir/mod-part1.pcm -fmodule-file=mod:part2=CMakeFiles/pch-cppm-problem-repro.dir/mod-part2.pcm -fmodule-file=mod:part3=CMakeFiles/pch-cppm-problem-repro.dir/mod-part3.pcm -fmodule-file=mod:part4=CMakeFiles/pch-cppm-problem-repro.dir/mod-part4.pcm -fskip-odr-check-in-gmf -include-pch /src/build/CMakeFiles/pch-cppm-problem-repro.dir/cmake_pch.hxx.pch -include /src/build/CMakeFiles/pch-cppm-problem-repro.dir/cmake_pch.hxx -target-feature +outline-atomics -target-feature -fmv -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o CMakeFiles/pch-cppm-problem-repro.dir/Mod.cppm.o -x pcm CMakeFiles/pch-cppm-problem-repro.dir/mod.pcm
 #0 0x0000ffff7698d398 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/usr/lib/llvm-18/bin/../lib/libLLVM.so.18.1+0xd9d398)
 #1 0x0000ffff7698b5a8 llvm::sys::RunSignalHandlers() (/usr/lib/llvm-18/bin/../lib/libLLVM.so.18.1+0xd9b5a8)
 #2 0x0000ffff7698dafc (/usr/lib/llvm-18/bin/../lib/libLLVM.so.18.1+0xd9dafc)
 #3 0x0000ffff80cdf790 (linux-vdso.so.1+0x790)
 #4 0x0000ffff7f5465d0 clang::ASTReader::getLocalModuleFile(clang::serialization::ModuleFile&, unsigned int) (/usr/lib/llvm-18/bin/../lib/libclang-cpp.so.18.1+0x27065d0)
 #5 0x0000ffff7f5c6e90 (/usr/lib/llvm-18/bin/../lib/libclang-cpp.so.18.1+0x2786e90)
 #6 0x0000ffff7f5c5bc0 clang::ASTReader::loadDeclUpdateRecords(clang::ASTReader::PendingUpdateRecord&) (/usr/lib/llvm-18/bin/../lib/libclang-cpp.so.18.1+0x2785bc0)
 #7 0x0000ffff7f575f44 clang::ASTReader::finishPendingActions() (/usr/lib/llvm-18/bin/../lib/libclang-cpp.so.18.1+0x2735f44)
 #8 0x0000ffff7f578ed8 clang::ASTReader::FinishedDeserializing() (/usr/lib/llvm-18/bin/../lib/libclang-cpp.so.18.1+0x2738ed8)
 #9 0x0000ffff7f55f79c clang::ASTReader::ReadAST(llvm::StringRef, clang::serialization::ModuleKind, clang::SourceLocation, unsigned int, clang::serialization::ModuleFile**) (/usr/lib/llvm-18/bin/../lib/libclang-cpp.so.18.1+0x271f79c)
#10 0x0000ffff7f6f9250 clang::ASTUnit::LoadFromASTFile(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, clang::PCHContainerReader const&, clang::ASTUnit::WhatToLoad, llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine>, clang::FileSystemOptions const&, std::shared_ptr<clang::HeaderSearchOptions>, bool, clang::CaptureDiagsKind, bool, bool, llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>) (/usr/lib/llvm-18/bin/../lib/libclang-cpp.so.18.1+0x28b9250)
#11 0x0000ffff7f77cc7c clang::FrontendAction::BeginSourceFile(clang::CompilerInstance&, clang::FrontendInputFile const&) (/usr/lib/llvm-18/bin/../lib/libclang-cpp.so.18.1+0x293cc7c)
#12 0x0000ffff7f710580 clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) (/usr/lib/llvm-18/bin/../lib/libclang-cpp.so.18.1+0x28d0580)
#13 0x0000ffff7f7f8e4c clang::ExecuteCompilerInvocation(clang::CompilerInstance*) (/usr/lib/llvm-18/bin/../lib/libclang-cpp.so.18.1+0x29b8e4c)
#14 0x0000aaaaacaf21c4 cc1_main(llvm::ArrayRef<char const*>, char const*, void*) (/usr/lib/llvm-18/bin/clang+0x121c4)
#15 0x0000aaaaacaefbac (/usr/lib/llvm-18/bin/clang+0xfbac)
#16 0x0000aaaaacaeed00 clang_main(int, char**, llvm::ToolContext const&) (/usr/lib/llvm-18/bin/clang+0xed00)
#17 0x0000aaaaacafae40 main (/usr/lib/llvm-18/bin/clang+0x1ae40)
#18 0x0000ffff757d84c4 (/lib/aarch64-linux-gnu/libc.so.6+0x284c4)
#19 0x0000ffff757d8598 __libc_start_main (/lib/aarch64-linux-gnu/libc.so.6+0x28598)
#20 0x0000aaaaacaec970 _start (/usr/lib/llvm-18/bin/clang+0xc970)
clang: error: unable to execute command: Segmentation fault
clang: error: clang frontend command failed due to signal (use -v to see invocation)
Ubuntu clang version 18.1.3 (1ubuntu1)
Target: aarch64-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/lib/llvm-18/bin
clang: note: diagnostic msg: 
********************

PLEASE ATTACH THE FOLLOWING FILES TO THE BUG REPORT:
Preprocessed source(s) and associated run script(s) are located at:
clang: note: diagnostic msg: /tmp/Mod-9d95fb.cppm
clang: note: diagnostic msg: /tmp/Mod-9d95fb.sh
clang: note: diagnostic msg: 

********************
ninja: build stopped: subcommand failed. ```

@github-actions github-actions bot added the clang Clang issues not falling into any other category label Aug 25, 2024
@ghost
Copy link
Author

ghost commented Aug 25, 2024

@ghost
Copy link
Author

ghost commented Aug 25, 2024

I've also just confirmed this on x86_64. Same repro instructions (inside docker). Compiler: Ubuntu clang version 18.1.3 (1ubuntu1)
Target: x86_64-pc-linux-gnu

@ghost
Copy link
Author

ghost commented Aug 25, 2024

Just so that people don't have to download a .zip file to see the sources, I'm also trying to include the files themselves directly:
CMakeLists.txt:

cmake_minimum_required(VERSION 3.28)
project(PCH-CPPM-problem-repro VERSION 0.0.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 23)
add_executable(pch-cppm-problem-repro main.cpp)
set_target_properties(pch-cppm-problem-repro PROPERTIES CXX_SCAN_FOR_MODULES 1)
target_precompile_headers(pch-cppm-problem-repro PRIVATE stdafx.h)
target_sources(pch-cppm-problem-repro PRIVATE FILE_SET CXX_MODULES FILES
    Mod.cppm Part1.cppm Part2.cppm Part3.cppm Part4.cppm
)

main.cpp:

import mod;

int main() {
}

Mod.cppm:

export module mod;
export import :part1;
export import :part2;
export import :part3;
export import :part4;

Part1.cppm:

export module mod:part1;

Part2.cppm:

export module mod:part2;

Part3.cppm:

export module mod:part3;

Part4.cppm:

export module mod:part4;

stdafx.h:

#pragma once
#include <iostream>

@ghost
Copy link
Author

ghost commented Aug 25, 2024

Having 4 partitions exported in Mod.cppm seems key, removing one of them and segfault stops happening.
Also not including iostream in the PCH fixes the segfault. Those 2 things seem significant.
Putting vector instead of iostream also removes the segfault.

And funny thing is, it has problems both with the Ubuntu iostream file and the MacOS one as well.

@Sirraide Sirraide added clang:modules C++20 modules and Clang Header Modules and removed clang Clang issues not falling into any other category labels Aug 25, 2024
@llvmbot
Copy link
Member

llvmbot commented Aug 25, 2024

@llvm/issue-subscribers-clang-modules

Author: None (ShaderKeeper)

Hello, when compiling a project that uses 4 module partitions and a precompiled header with ```#include <iostream>``` , I get a segfault.

I've originally encountered this problem on MacOS with llvm clang-18.1.8 . I've also been able to reproduce it in an Ubuntu docker container (on the same machine) with: Ubuntu clang version 18.1.3 (1ubuntu1) Target: aarch64-unknown-linux-gnu.

The machine this happens on is Apple Silicon M2 Pro (mac mini). Host architecture is aarch64.

Reproduction instructions using docker:

  1. docker run --rm -it -v /local/path/to/repro/project:/src ubuntu bash
  2. apt-get update && apt-get install -y cmake clang clang-tools ninja-build
  3. cd /src
  4. PATH="/usr/lib/llvm-18/bin:$PATH" CXX=clang cmake -S . -B build -G Ninja
  5. cmake --build build

Should get the following output:

FAILED: CMakeFiles/pch-cppm-problem-repro.dir/Mod.cppm.o CMakeFiles/pch-cppm-problem-repro.dir/mod.pcm 
/usr/lib/llvm-18/bin/clang   -std=gnu++23 -Winvalid-pch -Xclang -include-pch -Xclang /src/build/CMakeFiles/pch-cppm-problem-repro.dir/cmake_pch.hxx.pch -Xclang -include -Xclang /src/build/CMakeFiles/pch-cppm-problem-repro.dir/cmake_pch.hxx -MD -MT CMakeFiles/pch-cppm-problem-repro.dir/Mod.cppm.o -MF CMakeFiles/pch-cppm-problem-repro.dir/Mod.cppm.o.d @<!-- -->CMakeFiles/pch-cppm-problem-repro.dir/Mod.cppm.o.modmap -o CMakeFiles/pch-cppm-problem-repro.dir/Mod.cppm.o -c /src/Mod.cppm
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace, preprocessed source, and associated run script.
Stack dump:
0.	Program arguments: /usr/lib/llvm-18/bin/clang -cc1 -triple aarch64-unknown-linux-gnu -emit-obj -mrelax-all -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name Mod.cppm -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=non-leaf -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu generic -target-feature +v8a -target-feature +fp-armv8 -target-feature +neon -target-abi aapcs -debugger-tuning=gdb -fdebug-compilation-dir=/src/build -fcoverage-compilation-dir=/src/build -resource-dir /usr/lib/llvm-18/lib/clang/18 -Winvalid-pch -std=gnu++23 -ferror-limit 19 -fno-signed-char -fgnuc-version=4.2.1 -fmodule-file=mod:part1=CMakeFiles/pch-cppm-problem-repro.dir/mod-part1.pcm -fmodule-file=mod:part2=CMakeFiles/pch-cppm-problem-repro.dir/mod-part2.pcm -fmodule-file=mod:part3=CMakeFiles/pch-cppm-problem-repro.dir/mod-part3.pcm -fmodule-file=mod:part4=CMakeFiles/pch-cppm-problem-repro.dir/mod-part4.pcm -fskip-odr-check-in-gmf -include-pch /src/build/CMakeFiles/pch-cppm-problem-repro.dir/cmake_pch.hxx.pch -include /src/build/CMakeFiles/pch-cppm-problem-repro.dir/cmake_pch.hxx -target-feature +outline-atomics -target-feature -fmv -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o CMakeFiles/pch-cppm-problem-repro.dir/Mod.cppm.o -x pcm CMakeFiles/pch-cppm-problem-repro.dir/mod.pcm
 #<!-- -->0 0x0000ffff7698d398 llvm::sys::PrintStackTrace(llvm::raw_ostream&amp;, int) (/usr/lib/llvm-18/bin/../lib/libLLVM.so.18.1+0xd9d398)
 #<!-- -->1 0x0000ffff7698b5a8 llvm::sys::RunSignalHandlers() (/usr/lib/llvm-18/bin/../lib/libLLVM.so.18.1+0xd9b5a8)
 #<!-- -->2 0x0000ffff7698dafc (/usr/lib/llvm-18/bin/../lib/libLLVM.so.18.1+0xd9dafc)
 #<!-- -->3 0x0000ffff80cdf790 (linux-vdso.so.1+0x790)
 #<!-- -->4 0x0000ffff7f5465d0 clang::ASTReader::getLocalModuleFile(clang::serialization::ModuleFile&amp;, unsigned int) (/usr/lib/llvm-18/bin/../lib/libclang-cpp.so.18.1+0x27065d0)
 #<!-- -->5 0x0000ffff7f5c6e90 (/usr/lib/llvm-18/bin/../lib/libclang-cpp.so.18.1+0x2786e90)
 #<!-- -->6 0x0000ffff7f5c5bc0 clang::ASTReader::loadDeclUpdateRecords(clang::ASTReader::PendingUpdateRecord&amp;) (/usr/lib/llvm-18/bin/../lib/libclang-cpp.so.18.1+0x2785bc0)
 #<!-- -->7 0x0000ffff7f575f44 clang::ASTReader::finishPendingActions() (/usr/lib/llvm-18/bin/../lib/libclang-cpp.so.18.1+0x2735f44)
 #<!-- -->8 0x0000ffff7f578ed8 clang::ASTReader::FinishedDeserializing() (/usr/lib/llvm-18/bin/../lib/libclang-cpp.so.18.1+0x2738ed8)
 #<!-- -->9 0x0000ffff7f55f79c clang::ASTReader::ReadAST(llvm::StringRef, clang::serialization::ModuleKind, clang::SourceLocation, unsigned int, clang::serialization::ModuleFile**) (/usr/lib/llvm-18/bin/../lib/libclang-cpp.so.18.1+0x271f79c)
#<!-- -->10 0x0000ffff7f6f9250 clang::ASTUnit::LoadFromASTFile(std::__cxx11::basic_string&lt;char, std::char_traits&lt;char&gt;, std::allocator&lt;char&gt;&gt; const&amp;, clang::PCHContainerReader const&amp;, clang::ASTUnit::WhatToLoad, llvm::IntrusiveRefCntPtr&lt;clang::DiagnosticsEngine&gt;, clang::FileSystemOptions const&amp;, std::shared_ptr&lt;clang::HeaderSearchOptions&gt;, bool, clang::CaptureDiagsKind, bool, bool, llvm::IntrusiveRefCntPtr&lt;llvm::vfs::FileSystem&gt;) (/usr/lib/llvm-18/bin/../lib/libclang-cpp.so.18.1+0x28b9250)
#<!-- -->11 0x0000ffff7f77cc7c clang::FrontendAction::BeginSourceFile(clang::CompilerInstance&amp;, clang::FrontendInputFile const&amp;) (/usr/lib/llvm-18/bin/../lib/libclang-cpp.so.18.1+0x293cc7c)
#<!-- -->12 0x0000ffff7f710580 clang::CompilerInstance::ExecuteAction(clang::FrontendAction&amp;) (/usr/lib/llvm-18/bin/../lib/libclang-cpp.so.18.1+0x28d0580)
#<!-- -->13 0x0000ffff7f7f8e4c clang::ExecuteCompilerInvocation(clang::CompilerInstance*) (/usr/lib/llvm-18/bin/../lib/libclang-cpp.so.18.1+0x29b8e4c)
#<!-- -->14 0x0000aaaaacaf21c4 cc1_main(llvm::ArrayRef&lt;char const*&gt;, char const*, void*) (/usr/lib/llvm-18/bin/clang+0x121c4)
#<!-- -->15 0x0000aaaaacaefbac (/usr/lib/llvm-18/bin/clang+0xfbac)
#<!-- -->16 0x0000aaaaacaeed00 clang_main(int, char**, llvm::ToolContext const&amp;) (/usr/lib/llvm-18/bin/clang+0xed00)
#<!-- -->17 0x0000aaaaacafae40 main (/usr/lib/llvm-18/bin/clang+0x1ae40)
#<!-- -->18 0x0000ffff757d84c4 (/lib/aarch64-linux-gnu/libc.so.6+0x284c4)
#<!-- -->19 0x0000ffff757d8598 __libc_start_main (/lib/aarch64-linux-gnu/libc.so.6+0x28598)
#<!-- -->20 0x0000aaaaacaec970 _start (/usr/lib/llvm-18/bin/clang+0xc970)
clang: error: unable to execute command: Segmentation fault
clang: error: clang frontend command failed due to signal (use -v to see invocation)
Ubuntu clang version 18.1.3 (1ubuntu1)
Target: aarch64-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/lib/llvm-18/bin
clang: note: diagnostic msg: 
********************

PLEASE ATTACH THE FOLLOWING FILES TO THE BUG REPORT:
Preprocessed source(s) and associated run script(s) are located at:
clang: note: diagnostic msg: /tmp/Mod-9d95fb.cppm
clang: note: diagnostic msg: /tmp/Mod-9d95fb.sh
clang: note: diagnostic msg: 

********************
ninja: build stopped: subcommand failed. ```


</details>

@ghost
Copy link
Author

ghost commented Aug 25, 2024

Link for reproduction in compiler explorer: https://godbolt.org/z/cMhdaTszb
Compiler: x86-64 clang assertions trunk

@ghost
Copy link
Author

ghost commented Aug 25, 2024

I'm trying to look at this. Notable, this happens during processing of the first (and only) input file which is a .pcm file. Even trying to dump info about this .pcm file with -module-file-info segfaults clang.

Command line:
clang -g -std=gnu++23 -arch arm64 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.5.sdk -fcolor-diagnostics -Winvalid-pch -Xarch_arm64 -include/Users/shader/CLionProjects/llvm-project/pch-cppm-problem-repro/cmake-build-debug/CMakeFiles/pch-cppm-problem-repro.dir/cmake_pch_arm64.hxx @CMakeFiles/pch-cppm-problem-repro.dir/Mod.cppm.o.modmap -x pcm -module-file-info CMakeFiles/pch-cppm-problem-repro.dir/mod.pcm

Offending .pcm file attached
mod.pcm.zip
(from a different compilation pass): module-file-info-mod-pcm.txt

@ghost
Copy link
Author

ghost commented Aug 25, 2024

When in Mod.cppm I remove the last export import :part4; suddenly the build succeeds. The mod.pcm file looks like this in that situation:
mod.pcm-successful.zip
module-file-info-mod-successful-pcm.txt

@ghost
Copy link
Author

ghost commented Aug 25, 2024

I have no idea if this actually fixes it, but this makes the build finish successfully in the test project:

Subject: [PATCH] Try to prevent segfault (#105994)
---
Index: clang/lib/Serialization/ASTReader.cpp
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
--- a/clang/lib/Serialization/ASTReader.cpp	(revision 33f3ebc86e7d3afcb65c551feba5bbc2421b42ed)
+++ b/clang/lib/Serialization/ASTReader.cpp	(revision 2d7041961e94ed641bb372f1281f433dd5b72262)
@@ -9187,6 +9187,9 @@
     auto I = GlobalSubmoduleMap.find(getGlobalSubmoduleID(M, ID >> 1));
     return I == GlobalSubmoduleMap.end() ? nullptr : I->second;
   } else {
+    if(ID == (std::numeric_limits<decltype(ID)>::max()-1)) {
+      return nullptr;
+    }
     // It's a prefix (preamble, PCH, ...). Look it up by index.
     unsigned IndexFromEnd = ID >> 1;
     assert(IndexFromEnd && "got reference to unknown module file");

Try_to_prevent_segfault_#105994.patch

@ghost
Copy link
Author

ghost commented Aug 25, 2024

-module-file-info for mod.pcm after applying this patch gets outputted successfully. It would probably be good to somehow check if this doesn't still contain unwanted garbage
module-file-info-mod-successful-pcm.txt

@ghost
Copy link
Author

ghost commented Aug 25, 2024

Well, all 4 partitions are exported. But previously, the PCH was mentioned here. Now it isn't. But maybe that's desirable? Because a PCH is not a module partition (I would think)

Information for module file 'CMakeFiles/pch-cppm-problem-repro.dir/mod.pcm':
  Module format: raw
  ====== C++20 Module structure ======
  Interface Unit 'mod' is the Primary Module at index #5
   Exports:
    Partition Interface 'mod:part1' is at index #1
    Partition Interface 'mod:part2' is at index #2
    Partition Interface 'mod:part3' is at index #3
    Partition Interface 'mod:part4' is at index #4

@ghost
Copy link
Author

ghost commented Aug 25, 2024

In my fork, I've force pushed 9d6010beef7539cfbc2ee935c90a82f20eed298e , this is rebased against the last commit in the upstream main branch that passed github actions checks.

I've compiled this commit in Release mode and tried it on the project where I discovered this problem (with ~80 CPP files, Vulkan, Objective C++, etc.) and it seems to build and run fine for me

@shafik
Copy link
Collaborator

shafik commented Aug 25, 2024

CC @ChuanqiXu9

@ChuanqiXu9
Copy link
Member

I have no idea if this actually fixes it, but this makes the build finish successfully in the test project:

Subject: [PATCH] Try to prevent segfault (#105994)
---
Index: clang/lib/Serialization/ASTReader.cpp
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
--- a/clang/lib/Serialization/ASTReader.cpp	(revision 33f3ebc86e7d3afcb65c551feba5bbc2421b42ed)
+++ b/clang/lib/Serialization/ASTReader.cpp	(revision 2d7041961e94ed641bb372f1281f433dd5b72262)
@@ -9187,6 +9187,9 @@
     auto I = GlobalSubmoduleMap.find(getGlobalSubmoduleID(M, ID >> 1));
     return I == GlobalSubmoduleMap.end() ? nullptr : I->second;
   } else {
+    if(ID == (std::numeric_limits<decltype(ID)>::max()-1)) {
+      return nullptr;
+    }
     // It's a prefix (preamble, PCH, ...). Look it up by index.
     unsigned IndexFromEnd = ID >> 1;
     assert(IndexFromEnd && "got reference to unknown module file");

Try_to_prevent_segfault_#105994.patch

Thanks for the analysis. And the "fix" shows there are some overflow for the ID. The ID is emitted in ASTWrtier.cpp ASTDeclContextNameLookupTrait::EmitFileRef. And its value is actually from ASTReader::getModuleFileID(). Maybe you can try to debug there.

@ghost
Copy link
Author

ghost commented Aug 26, 2024

@ChuanqiXu9 hello, thank you for the hint. I'll try to get some time after work (probably again over the weekend) and try to debug and find out what's actually happening.

@ghost
Copy link
Author

ghost commented Aug 31, 2024

Hello, I have a progress report. I looked into this and found that during the compilation, clang++ runs 2 separate invocations of clang-20 . The first one is the one that generates the module with the garbage reference.

I'll continue trying to find why this is there but it will probably take some time again.

I've placed a breakpoint where you've said @ChuanqiXu9 and found that ASTDeclContextNameLookupTrait::EmitFileRef runs 4 times.
The very first time when it's emitting a reference to a PCH module file:
(contents of F->FileName): "/Users/shader/CLionProjects/llvm-project/pch-cppm-problem-repro/cmake-build-debug/CMakeFiles/pch-cppm-problem-repro.dir/cmake_pch_arm64.hxx.pch"

The structure seems to me to be suspiciously empty? And also importantly Writer.getChain()->getModuleFileID(F) during this function call evaluates to the previously mentioned canary value.

Screenshot 2024-08-31 at 11 41 31 PM

Here you can see it well to comparison of one of the C++ module references. (the blue values are changed since one of the other calls). You can see this structure seems to have more reasonable values now.

Screenshot 2024-08-31 at 11 42 53 PM

@ghost
Copy link
Author

ghost commented Aug 31, 2024

I think I've found the problem. This function only gets called when MultiOnDiskHashTable is getting 'merged' for performance reasons (which is triggered by adding a 4th submodule). Otherwise the 'file ref' doesn't ever get emitted.

So when it is getting 'merged', this gets triggered:

Screenshot 2024-09-01 at 12 53 47 AM

It tries to set the 'base module' of submodules to the first module. Which might be true when you don't use PCH. When you use PCH then the first module(s) might be PCH.

I don't still understand this 100% but I think this explains the behavior that is seen.
I'll try to get 'ID' of the first module file and skip over PCH.

@ghost
Copy link
Author

ghost commented Sep 2, 2024

I believe this finally fixes it. When the main module .pcm file is being written, the sides of the subtraction to generate an offset from the end were swapped while generating the 'index from end' of the PCH module in ModuleManager.

During de-serialization on the other hand, the wrong operator[] was used that only supports unsigned int as argument, despite being passed a negative value.

The only way to trigger this bug as far as I can tell is:

  1. Have at least one PCH module added
  2. Trigger this condition to be true inside of MultiOnDiskHashTableGenerator::Emit().
if (auto *Merged = Base ? Base->getMergedTable() : nullptr) {
        // Write list of overridden files.
        Writer.write<uint32_t>(Merged->Files.size());
        for (const auto &F : Merged->Files)
          Info.EmitFileRef(OutStream, F);

        // Add all merged entries from Base to the generator.
        for (auto &KV : Merged->Data) {
          if (!Gen.contains(KV.first, Info))
            Gen.insert(KV.first, Info.ImportData(KV.second), Info);
        }
      } else {
        Writer.write<uint32_t>(0);
      }

I don't understand how this gets triggered. It seems to be when there is an 'UPDATE_VISIBLE' block being written, which I can't find what it is. (ASTWriter::WriteDeclContextVisibleUpdate). The test setup in this issue triggers it.

Fix patch:

Subject: [PATCH] [clang][modules] Fix serialization and de-serialization of PCH module file refs  (#105994)
---
Index: clang/lib/Serialization/ASTReader.cpp
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
--- a/clang/lib/Serialization/ASTReader.cpp	(revision 357bd61744bb8cc2b9b07447294fa977e5758550)
+++ b/clang/lib/Serialization/ASTReader.cpp	(revision 36c965fb4feed7b8392ff7d2a60b682a6d4234fc)
@@ -9217,7 +9217,7 @@
     // It's a prefix (preamble, PCH, ...). Look it up by index.
     unsigned IndexFromEnd = ID >> 1;
     assert(IndexFromEnd && "got reference to unknown module file");
-    return getModuleManager().pch_modules().end()[-IndexFromEnd];
+    return getModuleManager().pch_modules().end()[-static_cast<int>(IndexFromEnd)];
   }
 }
 
@@ -9235,7 +9235,7 @@
   auto PCHModules = getModuleManager().pch_modules();
   auto I = llvm::find(PCHModules, M);
   assert(I != PCHModules.end() && "emitting reference to unknown file");
-  return (I - PCHModules.end()) << 1;
+  return std::distance(I, PCHModules.end()) << 1;
 }
 
 std::optional<ASTSourceDescriptor> ASTReader::getSourceDescriptor(unsigned ID) {

@ChuanqiXu9
Copy link
Member

@ShaderKeeper thanks for your work and the analysis! You only need to get a test to make this a patch.

Just so that people don't have to download a .zip file to see the sources, I'm also trying to include the files themselves directly: CMakeLists.txt:

cmake_minimum_required(VERSION 3.28)
project(PCH-CPPM-problem-repro VERSION 0.0.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 23)
add_executable(pch-cppm-problem-repro main.cpp)
set_target_properties(pch-cppm-problem-repro PROPERTIES CXX_SCAN_FOR_MODULES 1)
target_precompile_headers(pch-cppm-problem-repro PRIVATE stdafx.h)
target_sources(pch-cppm-problem-repro PRIVATE FILE_SET CXX_MODULES FILES
    Mod.cppm Part1.cppm Part2.cppm Part3.cppm Part4.cppm
)

main.cpp:

import mod;

int main() {
}

Mod.cppm:

export module mod;
export import :part1;
export import :part2;
export import :part3;
export import :part4;

Part1.cppm:

export module mod:part1;

Part2.cppm:

export module mod:part2;

Part3.cppm:

export module mod:part3;

Part4.cppm:

export module mod:part4;

stdafx.h:

#pragma once
#include <iostream>

And from this message, it looks you "only" need to replace #include <iostream> with a reduced header to make it. This is not easy but relatively easy for modules. You can preprocess the last header and remove the unrelated parts step by step. After you make it, you can look at clang/test/Modules for example to add a test for it. Then try to make it a PR.

I don't understand how this gets triggered.

It is triggered by the last line of ASTWriter::GenerateNameLookupTable, where is may be called by (ASTWriter::WriteDeclContextVisibleUpdate) or ASTWriter::WriteDeclContextVisibleBlock. Its job is to generate the look up table, which is the core ability for modules.

@koplas
Copy link
Contributor

koplas commented Mar 24, 2025

Here is a reduced test case: https://godbolt.org/z/7741TzTqT

stdafx.h now looks like this:

#pragma once

class a {
  virtual ~a();
  a() {}
};

@ShaderKeeper Do you want to create a PR with this test code?

@ghost
Copy link
Author

ghost commented Mar 24, 2025

Here is a reduced test case: https://godbolt.org/z/7741TzTqT

stdafx.h now looks like this:

#pragma once

class a {
virtual ~a();
a() {}
};
@ShaderKeeper Do you want to create a PR with this test code?

Hello, thank you for looking into this. I didn't have time to continue. I've also never done a unit test in LLVM, so if you already know how, please feel free to open a PR and take full credit for my part of the patch.

koplas added a commit to koplas/llvm-project that referenced this issue Mar 24, 2025
…H module file refs (llvm#105994)

Co-authored-by: ShaderKeeper <no-reply@shaderkeeper.com>
@koplas
Copy link
Contributor

koplas commented Mar 24, 2025

Hello, thank you for looking into this. I didn't have time to continue. I've also never done a unit test in LLVM, so if you already know how, please feel free to open a PR and take full credit for my part of the patch.

It is also my first LLVM test, but here is it: #132802.

koplas added a commit to koplas/llvm-project that referenced this issue Mar 24, 2025
…H module file refs (llvm#105994)

Co-authored-by: ShaderKeeper <no-reply@shaderkeeper.com>
ChuanqiXu9 added a commit that referenced this issue Mar 25, 2025
…H module file refs (#105994) (#132802)

The File ID is incorrectly calculated, resulting in an out-of-bounds
access. The test code is more complex because the File fetching only
happens in specific scenarios.

---------

Co-authored-by: ShaderKeeper <no-reply@shaderkeeper.com>
Co-authored-by: Chuanqi Xu <yedeng.yd@linux.alibaba.com>
swift-ci pushed a commit to swiftlang/llvm-project that referenced this issue Apr 1, 2025
…H module file refs (llvm#105994) (llvm#132802)

The File ID is incorrectly calculated, resulting in an out-of-bounds
access. The test code is more complex because the File fetching only
happens in specific scenarios.

---------

Co-authored-by: ShaderKeeper <no-reply@shaderkeeper.com>
Co-authored-by: Chuanqi Xu <yedeng.yd@linux.alibaba.com>
(cherry picked from commit cca0f81)
@shafik
Copy link
Collaborator

shafik commented Apr 8, 2025

Looks like this is fixed in trunk: https://godbolt.org/z/hYh7hKMhn

@ChuanqiXu9
Copy link
Member

I think we can close this. In case, there are still problems, we can reopen it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:modules C++20 modules and Clang Header Modules crash-on-valid
Projects
None yet
Development

No branches or pull requests

5 participants