Repository: immunant/c2rust (language: rust)

Issue:
Ast exporter segfault
ast exporter is segfaulting on some inputs.

A short example to demonstrate the problem is shown below.

```c
typedef struct {
    char chars[16];
} my_struct;

_Static_assert(sizeof(((my_struct *)0)->chars) == 16, "expected size 16");

```



Discussion / hints:
It looks like this is enough to cause the segfault:

```C
_Static_assert(1, "segfault");
```

Backtrace:
```
Program received signal SIGSEGV, Segmentation fault.
clang::Expr::ClassifyImpl () at /usr/src/debug/clang/clang-18.1.8.src/lib/AST/ExprClassification.cpp:45
45	   if (TR->isFunctionType() || TR == Ctx.OverloadTy)
(gdb) bt
#0  clang::Expr::ClassifyImpl () at /usr/src/debug/clang/clang-18.1.8.src/lib/AST/ExprClassification.cpp:45
#1  0x0000555555cce8a2 in clang::Expr::Classify (this=0x5555563debb8, Ctx=...) at /usr/include/clang/AST/Expr.h:401
#2  0x0000555555ce925c in TranslateASTVisitor::encode_entry (this=0x7fffffff8810, ast=0x5555563debb8, tag=TagStringLiteral, childIds=std::vector of length 0, capacity 0, extra=...)
    at /c2rust-ast-exporter/src/AstExporter.cpp:695
#3  0x0000555555cf8d90 in TranslateASTVisitor::VisitStringLiteral (this=0x7fffffff8810, SL=0x5555563debb8) at /c2rust-ast-exporter/src/AstExporter.cpp:2297
#4  0x0000555555d65ed4 in clang::RecursiveASTVisitor<TranslateASTVisitor>::WalkUpFromStringLiteral (this=0x7fffffff8810, S=0x5555563debb8) at /usr/include/clang/AST/StmtNodes.inc:86
#5  0x0000555555d65d03 in clang::RecursiveASTVisitor<TranslateASTVisitor>::TraverseStringLiteral (this=0x7fffffff8810, S=0x5555563debb8, Queue=0x7fffffff8460) at /usr/include/clang/AST/RecursiveASTVisitor.h:2923
#6  0x0000555555d43128 in clang::RecursiveASTVisitor<TranslateASTVisitor>::dataTraverseNode (this=0x7fffffff8810, S=0x5555563debb8, Queue=0x7fffffff8460) at /usr/include/clang/AST/StmtNodes.inc:86
#7  0x0000555555d11e54 in clang::RecursiveASTVisitor<TranslateASTVisitor>::TraverseStmt (this=0x7fffffff8810, S=0x5555563debb8, Queue=0x0) at /usr/include/clang/AST/RecursiveASTVisitor.h:678
#8  0x0000555555d2c38b in clang::RecursiveASTVisitor<TranslateASTVisitor>::TraverseStaticAssertDecl (this=0x7fffffff8810, D=0x5555563dec18) at /usr/include/clang/AST/RecursiveASTVisitor.h:1575
#9  0x0000555555d0fa40 in clang::RecursiveASTVisitor<TranslateASTVisitor>::TraverseDecl (this=0x7fffffff8810, D=0x5555563dec18) at /usr/include/clang/AST/DeclNodes.inc:72
#10 0x0000555555d5736f in clang::RecursiveASTVisitor<TranslateASTVisitor>::TraverseDeclContextHelper (this=0x7fffffff8810, DC=0x55555637e800) at /usr/include/clang/AST/RecursiveASTVisitor.h:1480
#11 0x0000555555d2b2f9 in clang::RecursiveASTVisitor<TranslateASTVisitor>::TraverseTranslationUnitDecl (this=0x7fffffff8810, D=0x55555637e7d8) at /usr/include/clang/AST/RecursiveASTVisitor.h:1580
#12 0x0000555555d0f8a8 in clang::RecursiveASTVisitor<TranslateASTVisitor>::TraverseDecl (this=0x7fffffff8810, D=0x55555637e7d8) at /usr/include/clang/AST/DeclNodes.inc:24
#13 0x0000555555cf9f5f in TranslateConsumer::HandleTranslationUnit(clang::ASTContext&)::{lambda(unsigned char*, unsigned long)#1}::operator()(unsigned char*, unsigned long) const (__closure=0x7fffffff8a50, buffer=0x7fffe5e00010 "\206\237\237\033", 
    len=67108864) at /c2rust-ast-exporter/src/AstExporter.cpp:2565
#14 0x0000555555cfa7ee in TranslateConsumer::HandleTranslationUnit (this=0x55555635c7d0, Context=...) at /c2rust-ast-exporter/src/AstExporter.cpp:2672
#15 0x00007ffff4f8d013 in clang::ParseAST () at /usr/src/debug/clang/clang-18.1.8.src/lib/Parse/ParseAST.cpp:176
#16 0x00007ffff6a583d1 in clang::FrontendAction::Execute () at /usr/src/debug/clang/clang-18.1.8.src/lib/Frontend/FrontendAction.cpp:1069
#17 0x00007ffff69f9017 in clang::CompilerInstance::ExecuteAction () at /usr/src/debug/clang/clang-18.1.8.src/lib/Frontend/CompilerInstance.cpp:1057
#18 0x00007ffff6bc7e6e in clang::tooling::FrontendActionFactory::runInvocation () at /usr/src/debug/clang/clang-18.1.8.src/lib/Tooling/Tooling.cpp:465
#19 0x00007ffff6bc5ce1 in clang::tooling::ToolInvocation::runInvocation () at /usr/src/debug/clang/clang-18.1.8.src/lib/Tooling/Tooling.cpp:440
#20 0x00007ffff6bc6f83 in clang::tooling::ToolInvocation::run () at /usr/src/debug/clang/clang-18.1.8.src/lib/Tooling/Tooling.cpp:425
#21 0x00007ffff6bc9695 in clang::tooling::ClangTool::run () at /usr/src/debug/clang/clang-18.1.8.src/lib/Tooling/Tooling.cpp:623
#22 0x0000555555cfb80d in process[abi:cxx11](int, char const**, int*) (argc=4, argv=0x5555563491d0, result=0x7fffffff99cc) at /c2rust-ast-exporter/src/AstExporter.cpp:2836
#23 0x0000555555cfba4c in ast_exporter (argc=4, argv=0x5555563491d0, debug=0) at /c2rust-ast-exporter/src/AstExporter.cpp:2852
#24 0x0000555555cb9b18 in c2rust_ast_exporter::get_ast_cbors (file_path=..., cc_db=..., extra_args=&[&str](size=0), debug=false) at c2rust-ast-exporter/src/lib.rs:67
#25 0x0000555555cb8c66 in c2rust_ast_exporter::get_untyped_ast (file_path=..., cc_db=..., extra_args=&[&str](size=0), debug=false) at c2rust-ast-exporter/src/lib.rs:27
#26 0x00005555558c1855 in c2rust_transpile::transpile_single (tcfg=0x7fffffffdad0, input_path=..., ancestor_path=..., build_dir=..., cc_db=..., extra_clang_args=&[&str](size=0)) at c2rust-transpile/src/lib.rs:491
#27 0x000055555568575c in c2rust_transpile::transpile::{closure#5} (cmd=0x55555634a420) at c2rust-transpile/src/lib.rs:328
#28 0x00005555557038db in core::iter::adapters::map::map_fold::{closure#0}<&alloc::rc::Rc<c2rust_transpile::compile_cmds::CompileCmd, alloc::alloc::Global>, core::result::Result<(std::path::PathBuf, alloc::vec::Vec<(&str, alloc::vec::Vec<&str, alloc::alloc::Global>), alloc::alloc::Global>, indexmap::set::IndexSet<c2rust_transpile::ExternCrate, std::hash::random::RandomState>), ()>, (), c2rust_transpile::transpile::{closure_env#5}, core::iter::traits::iterator::Iterator::for_each::call::{closure_env#0}<core::result::Result<(std::path::PathBuf, alloc::vec::Vec<(&str, alloc::vec::Vec<&str, alloc::alloc::Global>), alloc::alloc::Global>, indexmap::set::IndexSet<c2rust_transpile::ExternCrate, std::hash::random::RandomState>), ()>, alloc::vec::{impl#19}::extend_trusted::{closure_env#0}<core::result::Result<(std::path::PathBuf, alloc::vec::Vec<(&str, alloc::vec::Vec<&str, alloc::alloc::Global>), alloc::alloc::Global>, indexmap::set::IndexSet<c2rust_transpile::ExternCrate, std::hash::random::RandomState>), ()>, alloc::alloc::Global, core::iter::adapters::map::Map<core::slice::iter::Iter<alloc::rc::Rc<c2rust_transpile::compile_cmds::CompileCmd, alloc::alloc::Global>>, c2rust_transpile::transpile::{closure_env#5}>>>> (acc=(), 
    elt=0x55555634a420) at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/core/src/iter/adapters/map.rs:89
#29 0x0000555555976819 in core::slice::iter::{impl#181}::fold<alloc::rc::Rc<c2rust_transpile::compile_cmds::CompileCmd, alloc::alloc::Global>, (), core::iter::adapters::map::map_fold::{closure_env#0}<&alloc::rc::Rc<c2rust_transpile::compile_cmds::CompileCmd, alloc::alloc::Global>, core::result::Result<(std::path::PathBuf, alloc::vec::Vec<(&str, alloc::vec::Vec<&str, alloc::alloc::Global>), alloc::alloc::Global>, indexmap::set::IndexSet<c2rust_transpile::ExternCrate, std::hash::random::RandomState>), ()>, (), c2rust_transpile::transpile::{closure_env#5}, core::iter::traits::iterator::Iterator::for_each::call::{closure_env#0}<core::result::Result<(std::path::PathBuf, alloc::vec::Vec<(&str, alloc::vec::Vec<&str, alloc::alloc::Global>), alloc::alloc::Global>, indexmap::set::IndexSet<c2rust_transpile::ExternCrate, std::hash::random::RandomState>), ()>, alloc::vec::{impl#19}::extend_trusted::{closure_env#0}<core::result::Result<(std::path::PathBuf, alloc::vec::Vec<(&str, alloc::vec::Vec<&str, alloc::alloc::Global>), alloc::alloc::Global>, indexmap::set::IndexSet<c2rust_transpile::ExternCrate, std::hash::random::RandomState>), ()>, alloc::alloc::Global, core::iter::adapters::map::Map<core::slice::iter::Iter<alloc::rc::Rc<c2rust_transpile::compile_cmds::CompileCmd, alloc::alloc::Global>>, c2rust_transpile::transpile::{closure_env#5}>>>>> (self=..., init=(), f=...) at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/core/src/slice/iter/macros.rs:232
#30 0x00005555556f7110 in core::iter::adapters::map::{impl#2}::fold<core::result::Result<(std::path::PathBuf, alloc::vec::Vec<(&str, alloc::vec::Vec<&str, alloc::alloc::Global>), alloc::alloc::Global>, indexmap::set::IndexSet<c2rust_transpile::ExternCrate, std::hash::random::RandomState>), ()>, core::slice::iter::Iter<alloc::rc::Rc<c2rust_transpile::compile_cmds::CompileCmd, alloc::alloc::Global>>, c2rust_transpile::transpile::{closure_env#5}, (), core::iter::traits::iterator::Iterator::for_each::call::{closure_env#0}<core::result::Result<(std::path::PathBuf, alloc::vec::Vec<(&str, alloc::vec::Vec<&str, alloc::alloc::Global>), alloc::alloc::Global>, indexmap::set::IndexSet<c2rust_transpile::ExternCrate, std::hash::random::RandomState>), ()>, alloc::vec::{impl#19}::extend_trusted::{closure_env#0}<core::result::Result<(std::path::PathBuf, alloc::vec::Vec<(&str, alloc::vec::Vec<&str, alloc::alloc::Global>), alloc::alloc::Global>, indexmap::set::IndexSet<c2rust_transpile::ExternCrate, std::hash::random::RandomState>), ()>, alloc::alloc::Global, core::iter::adapters::map::Map<core::slice::iter::Iter<alloc::rc::Rc<c2rust_transpile::compile_cmds::CompileCmd, alloc::alloc::Global>>, c2rust_transpile::transpile::{closure_env#5}>>>> (
    self=..., init=(), g=...) at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/core/src/iter/adapters/map.rs:129
#31 0x00005555556fc226 in core::iter::traits::iterator::Iterator::for_each<core::iter::adapters::map::Map<core::slice::iter::Iter<alloc::rc::Rc<c2rust_transpile::compile_cmds::CompileCmd, alloc::alloc::Global>>, c2rust_transpile::transpile::{closure_env#5}>, alloc::vec::{impl#19}::extend_trusted::{closure_env#0}<core::result::Result<(std::path::PathBuf, alloc::vec::Vec<(&str, alloc::vec::Vec<&str, alloc::alloc::Global>), alloc::alloc::Global>, indexmap::set::IndexSet<c2rust_transpile::ExternCrate, std::hash::random::RandomState>), ()>, alloc::alloc::Global, core::iter::adapters::map::Map<core::slice::iter::Iter<alloc::rc::Rc<c2rust_transpile::compile_cmds::CompileCmd, alloc::alloc::Global>>, c2rust_transpile::transpile::{closure_env#5}>>>
    (self=..., f=...) at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/core/src/iter/traits/iterator.rs:817
#32 0x0000555555824703 in alloc::vec::Vec<core::result::Result<(std::path::PathBuf, alloc::vec::Vec<(&str, alloc::vec::Vec<&str, alloc::alloc::Global>), alloc::alloc::Global>, indexmap::set::IndexSet<c2rust_transpile::ExternCrate, std::hash::random::RandomState>), ()>, alloc::alloc::Global>::extend_trusted<core::result::Result<(std::path::PathBuf, alloc::vec::Vec<(&str, alloc::vec::Vec<&str, alloc::alloc::Global>), alloc::alloc::Global>, indexmap::set::IndexSet<c2rust_transpile::ExternCrate, std::hash::random::RandomState>), ()>, alloc::alloc::Global, core::iter::adapters::map::Map<core::slice::iter::Iter<alloc::rc::Rc<c2rust_transpile::compile_cmds::CompileCmd, alloc::alloc::Global>>, c2rust_transpile::transpile::{closure_env#5}>> (
    self=0x7fffffffc3a0, iterator=...) at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/alloc/src/vec/mod.rs:3020
#33 0x000055555584bebb in alloc::vec::spec_extend::{impl#1}::spec_extend<core::result::Result<(std::path::PathBuf, alloc::vec::Vec<(&str, alloc::vec::Vec<&str, alloc::alloc::Global>), alloc::alloc::Global>, indexmap::set::IndexSet<c2rust_transpile::ExternCrate, std::hash::random::RandomState>), ()>, core::iter::adapters::map::Map<core::slice::iter::Iter<alloc::rc::Rc<c2rust_transpile::compile_cmds::CompileCmd, alloc::alloc::Global>>, c2rust_transpile::transpile::{closure_env#5}>, alloc::alloc::Global> (self=0x7fffffffc3a0, iterator=...) at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/alloc/src/vec/spec_extend.rs:26
#34 0x0000555555814231 in alloc::vec::spec_from_iter_nested::{impl#1}::from_iter<core::result::Result<(std::path::PathBuf, alloc::vec::Vec<(&str, alloc::vec::Vec<&str, alloc::alloc::Global>), alloc::alloc::Global>, indexmap::set::IndexSet<c2rust_transpile::ExternCrate, std::hash::random::RandomState>), ()>, core::iter::adapters::map::Map<core::slice::iter::Iter<alloc::rc::Rc<c2rust_transpile::compile_cmds::CompileCmd, alloc::alloc::Global>>, c2rust_transpile::transpile::{closure_env#5}>> (
    iterator=...) at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/alloc/src/vec/spec_from_iter_nested.rs:62
#35 0x000055555584c90e in alloc::vec::spec_from_iter::{impl#0}::from_iter<core::result::Result<(std::path::PathBuf, alloc::vec::Vec<(&str, alloc::vec::Vec<&str, alloc::alloc::Global>), alloc::alloc::Global>, indexmap::set::IndexSet<c2rust_transpile::ExternCrate, std::hash::random::RandomState>), ()>, core::iter::adapters::map::Map<core::slice::iter::Iter<alloc::rc::Rc<c2rust_transpile::compile_cmds::CompileCmd, alloc::alloc::Global>>, c2rust_transpile::transpile::{closure_env#5}>> (
    iterator=...) at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/alloc/src/vec/spec_from_iter.rs:33
#36 0x000055555584b379 in alloc::vec::{impl#14}::from_iter<core::result::Result<(std::path::PathBuf, alloc::vec::Vec<(&str, alloc::vec::Vec<&str, alloc::alloc::Global>), alloc::alloc::Global>, indexmap::set::IndexSet<c2rust_transpile::ExternCrate, std::hash::random::RandomState>), ()>, core::iter::adapters::map::Map<core::slice::iter::Iter<alloc::rc::Rc<c2rust_transpile::compile_cmds::CompileCmd, alloc::alloc::Global>>, c2rust_transpile::transpile::{closure_env#5}>> (iter=...)
    at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/alloc/src/vec/mod.rs:2894
#37 0x00005555556fb50e in core::iter::traits::iterator::Iterator::collect<core::iter::adapters::map::Map<core::slice::iter::Iter<alloc::rc::Rc<c2rust_transpile::compile_cmds::CompileCmd, alloc::alloc::Global>>, c2rust_transpile::transpile::{closure_env#5}>, alloc::vec::Vec<core::result::Result<(std::path::PathBuf, alloc::vec::Vec<(&str, alloc::vec::Vec<&str, alloc::alloc::Global>), alloc::alloc::Global>, indexmap::set::IndexSet<c2rust_transpile::ExternCrate, std::hash::random::RandomState>), ()>, alloc::alloc::Global>> (self=...) at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/core/src/iter/traits/iterator.rs:2003
--Type <RET> for more, q to quit, c to continue without paging--
#38 0x00005555558bf802 in c2rust_transpile::transpile (tcfg=..., cc_db=..., extra_clang_args=&[&str](size=0)) at c2rust-transpile/src/lib.rs:325
#39 0x000055555562cdd6 in c2rust_transpile::main () at c2rust/src/bin/c2rust-transpile.rs:259
```
After some debugging and code reading, I am not sure of the cause of this still (probably due to my lack of experience working on compilers), but reading what the code is trying to do, I decided to try this change:

```
diff --git a/c2rust-ast-exporter/src/AstExporter.cpp b/c2rust-ast-exporter/src/AstExporter.cpp
index a9183cdf..07934293 100644
--- a/c2rust-ast-exporter/src/AstExporter.cpp
+++ b/c2rust-ast-exporter/src/AstExporter.cpp
@@ -691,8 +691,7 @@ class TranslateASTVisitor final
 #if CLANG_VERSION_MAJOR < 13
         bool isRValue = ast->isRValue();
 #else
-        assert(Context && "Expected Context to be non-NULL");
-        bool isRValue = ast->Classify(*Context).isRValue();
+        bool isRValue = !ast->isLValue();
 #endif
         encode_entry_raw(ast, tag, ast->getSourceRange(), ty, isRValue, isVaList,
                          encodeMacroExpansions, childIds, extra);

```

Internally, the existing `.isRValue()` is simply checking `Kind >= CL_XValue;` and looking at the variants of Kind, this seems equivalent to `!= CL_LValue`.

I have not made a PR because I'm not certain about the correctness of this change, but it fixes the crash and seems to work for me now.

It's also possible that `!ast->isLValue()` is correct for `CLANG_VERSION_MAJOR < 13` too, so perhaps the whole check can be removed?

If someone who knows this better can provide feedback, I'll happily make a PR if this change seems reasonable.
Is there anything I can do to help with this?
~~I can't reproduce by running `target/debug/c2rust-transpile test.c` on a x86 host with `Ubuntu clang version 14.0.0-1ubuntu1.1`. c2rust.com doesn't repro either; just produces an empty output. Which compiler versions reproduce this?~~

Update: reproduces on LLVM 19.1.2.

Fix patch:
diff --git a/c2rust-ast-exporter/src/AstExporter.cpp b/c2rust-ast-exporter/src/AstExporter.cpp
index a9183cdfd8..d3089826d4 100644
--- a/c2rust-ast-exporter/src/AstExporter.cpp
+++ b/c2rust-ast-exporter/src/AstExporter.cpp
@@ -691,8 +691,12 @@ class TranslateASTVisitor final
 #if CLANG_VERSION_MAJOR < 13
         bool isRValue = ast->isRValue();
 #else
-        assert(Context && "Expected Context to be non-NULL");
-        bool isRValue = ast->Classify(*Context).isRValue();
+        // prvalues are equivalent to rvalues in C++03.
+        //
+        // NOTE: We used to call ast->Classify(*Context).isRValue() but that may
+        // result in a segfault on LLVM 18 and 19 for certain string literals.
+        // See https://github.com/immunant/c2rust/issues/1124
+        bool isRValue = ast->getValueKind() == VK_PRValue;
 #endif
         encode_entry_raw(ast, tag, ast->getSourceRange(), ty, isRValue, isVaList,
                          encodeMacroExpansions, childIds, extra);


Test patch: