llvm / llvm/llvm-project

[clang][Sema] Segfault in Expr::getReferencedDeclOfCallee via CheckNoInlineAttr during template instantiation of variadic function with [[clang::noinline]] on return statement

Open
#208,705 4 comments 0 reactions 0 assignees View on GitHub
clang:frontend crash generated by fuzzer
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

The following code:

```cpp
template
template
int dependent(int x){ return x + D;}
[[clang::always_inline]]
int non_dependent(int x){return x;}
template
int variadic_qux(int x) {
[[msvc::noinline]] return non_dependent(x) + (dependent(x) + ...);
}
void use() {
variadic_baz<0, 1, 2>(0); // #VARIADIC_INST
```

Resulted in this output:
```
/tmp/test.cpp:7:3: warning: 'msvc::noinline' attribute only applies to functions and statements [-Wignored-attributes]
7 | [[msvc::noinline]] static int j = bar();
| ^
/tmp/test.cpp:7:35: error: use of undeclared identifier 'bar'
7 | [[msvc::noinline]] static int j = bar();
| ^~~
/tmp/test.cpp:10:3: warning: 'clang::noinline' attribute only applies to functions and statements [-Wignored-attributes]
10 | [[clang::noinline]] static int i = bar();
| ^
/tmp/test.cpp:10:36: error: use of undeclared identifier 'bar'
10 | [[clang::noinline]] static int i = bar();
| ^~~
/tmp/test.cpp:11:15: error: expected ';' after struct
11 | struct Tag1 {}
| ^
| ;
/tmp/test.cpp:12:15: error: expected ';' after struct
12 | struct Tag4 {}
| ^
| ;
/tmp/test.cpp:25:5: error: 'clang::noinline' attribute takes no arguments
25 | [[clang::noinline(0)]] bar(); // expected-error {{'clang::noinline' attribute takes no arguments}}
| ^
/tmp/test.cpp:27:23: warning: 'clang::noinline' attribute is ignored because there exists no call expression inside the statement [-Wignored-attributes]
27 | [[clang::noinline]] x = 0; // expected-warning {{'clang::noinline' attribute is ignored because there exists no call expression inside the statement}}
| ^
/tmp/test.cpp:28:23: warning: 'clang::noinline' attribute is ignored because there exists no call expression inside the statement [-Wignored-attributes]
28 | [[clang::noinline]] { asm("nop"); } // expected-warning {{'clang::noinline' attribute is ignored because there exists no call expression inside the statement}}
| ^
/tmp/test.cpp:29:5: warning: 'clang::noinline' attribute only applies to functions and statements [-Wignored-attributes]
29 | [[clang::noinline]] label: x = 1; // expected-warning {{'clang::noinline' attribute only applies to functions and statements}}
| ^
/tmp/test.cpp:32:23: warning: statement attribute 'clang::noinline' has higher precedence than function attribute 'always_inline' [-Wignored-attributes]
32 | [[clang::noinline]] always_inline_fn(); // expected-warning {{statement attribute 'clang::noinline' has higher precedence than function attribute 'always_inline'}}
| ^
/tmp/test.cpp:5:24: note: conflicting attribute is here
5 | [[gnu::always_inline]] void always_inline_fn(void) { }
| ^
/tmp/test.cpp:33:23: warning: statement attribute 'clang::noinline' has higher precedence than function attribute 'flatten' [-Wignored-attributes]
33 | [[clang::noinline]] flatten_fn(); // expected-warning {{statement attribute 'clang::noinline' has higher precedence than function attribute 'flatten'}}
| ^
/tmp/test.cpp:3:18: note: conflicting attribute is here
3 | [[gnu::flatten]] void flatten_fn(void) { }
| ^
/tmp/test.cpp:36:21: warning: attribute is ignored on this statement as it only applies to functions; use '[[clang::noinline]]' on statements [-Wignored-attributes]
36 | [[gnu::noinline]] bar(); // expected-warning {{attribute is ignored on this statement as it only applies to functions; use '[[clang::noinline]]' on statements}}
| ^
/tmp/test.cpp:37:29: warning: attribute is ignored on this statement as it only applies to functions; use '[[clang::noinline]]' on statements [-Wignored-attributes]
37 | __attribute__((noinline)) bar(); // expected-warning {{attribute is ignored on this statement as it only applies to functions; use '[[clang::noinline]]' on statements}}
| ^
/tmp/test.cpp:41:5: error: 'msvc::noinline' attribute takes no arguments
41 | [[msvc::noinline(0)]] bar(); // expected-error {{'msvc::noinline' attribute takes no arguments}}
| ^
/tmp/test.cpp:43:22: warning: 'msvc::noinline' attribute is ignored because there exists no call expression inside the statement [-Wignored-attributes]
43 | [[msvc::noinline]] x = 0; // expected-warning {{'msvc::noinline' attribute is ignored because there exists no call expression inside the statement}}
| ^
/tmp/test.cpp:44:22: warning: 'msvc::noinline' attribute is ignored because there exists no call expression inside the statement [-Wignored-attributes]
44 | [[msvc::noinline]] { asm("nop"); } // expected-warning {{'msvc::noinline' attribute is ignored because there exists no call expression inside the statement}}
| ^
/tmp/test.cpp:45:5: warning: 'msvc::noinline' attribute only applies to functions and statements [-Wignored-attributes]
45 | [[msvc::noinline]] label: x = 1; // expected-warning {{'msvc::noinline' attribute only applies to functions and statements}}
| ^
/tmp/test.cpp:47:22: warning: statement attribute 'msvc::noinline' has higher precedence than function attribute 'always_inline' [-Wignored-attributes]
47 | [[msvc::noinline]] always_inline_fn(); // expected-warning {{statement attribute 'msvc::noinline' has higher precedence than function attribute 'always_inline'}}
| ^
/tmp/test.cpp:5:24: note: conflicting attribute is here
5 | [[gnu::always_inline]] void always_inline_fn(void) { }
| ^
/tmp/test.cpp:48:22: warning: statement attribute 'msvc::noinline' has higher precedence than function attribute 'flatten' [-Wignored-attributes]
48 | [[msvc::noinline]] flatten_fn(); // expected-warning {{statement attribute 'msvc::noinline' has higher precedence than function attribute 'flatten'}}
| ^
/tmp/test.cpp:3:18: note: conflicting attribute is here
3 | [[gnu::flatten]] void flatten_fn(void) { }
| ^
/tmp/test.cpp:56:33: error: comparison between pointer and integer ('void (*)()' and 'int')
56 | [[clang::noinline]] return foo(x + 1);
| ~~~^~~~
/tmp/test.cpp:56:23: warning: 'clang::noinline' attribute is ignored because there exists no call expression inside the statement [-Wignored-attributes]
56 | [[clang::noinline]] return foo(x + 1);
| ^
/tmp/test.cpp:59:3: warning: attribute declaration must precede definition [-Wignored-attributes]
59 | [[clang::always_inline]]
| ^
/tmp/test.cpp:55:5: note: previous definition is here
55 | int dependent(int x) {
| ^
/tmp/test.cpp:60:5: error: redefinition of 'dependent'
60 | int dependent(int x){ return x + D;}
| ^
/tmp/test.cpp:55:5: note: previous definition is here
55 | int dependent(int x) {
| ^
/tmp/test.cpp:70:23: warning: statement attribute 'clang::noinline' has higher precedence than function attribute 'always_inline' [-Wignored-attributes]
70 | [[clang::noinline]] non_dependent(x);
| ^
/tmp/test.cpp:63:1: note: conflicting attribute is here
63 | int non_dependent(int x){return x;}
| ^
/tmp/test.cpp:78:25: warning: statement attribute 'clang::noinline' has higher precedence than function attribute 'always_inline' [-Wignored-attributes]
78 | [[clang::noinline]] return non_dependent(x), baz(x + 1);
| ^
/tmp/test.cpp:63:1: note: conflicting attribute is here
63 | int non_dependent(int x){return x;}
| ^
/tmp/test.cpp:92:23: warning: statement attribute 'clang::noinline' has higher precedence than function attribute 'always_inline' [-Wignored-attributes]
92 | [[clang::noinline]] return non_dependent(x) + (dependent(x) + ...);
| ^
/tmp/test.cpp:63:1: note: conflicting attribute is here
63 | int non_dependent(int x){return x;}
| ^
/tmp/test.cpp:98:22: warning: statement attribute 'msvc::noinline' has higher precedence than function attribute 'always_inline' [-Wignored-attributes]
98 | [[msvc::noinline]] non_dependent(x);
| ^
/tmp/test.cpp:63:1: note: conflicting attribute is here
63 | int non_dependent(int x){return x;}
| ^
/tmp/test.cpp:106:24: warning: statement attribute 'msvc::noinline' has higher precedence than function attribute 'always_inline' [-Wignored-attributes]
106 | [[msvc::noinline]] return non_dependent(x), qux(x + 1);
| ^
/tmp/test.cpp:63:1: note: conflicting attribute is here
63 | int non_dependent(int x){return x;}
| ^
/tmp/test.cpp:120:22: warning: statement attribute 'msvc::noinline' has higher precedence than function attribute 'always_inline' [-Wignored-attributes]
120 | [[msvc::noinline]] return non_dependent(x) + (dependent(x) + ...);
| ^
/tmp/test.cpp:63:1: note: conflicting attribute is here
63 | int non_dependent(int x){return x;}
| ^
/tmp/test.cpp:128:24: error: use of undeclared identifier 'get_line_constexpr'
128 | const int global_three(get_line_constexpr());
| ^~~~~~~~~~~~~~~~~~
/tmp/test.cpp:130:18: error: use of undeclared identifier 'get_line_constexpr'
130 | int global_two = get_line_constexpr();
| ^~~~~~~~~~~~~~~~~~
/tmp/test.cpp:165:2: error: expected ';' after struct
165 | }
| ^
| ;
/tmp/test.cpp:78:25: warning: statement attribute 'clang::noinline' has higher precedence than function attribute 'always_inline' [-Wignored-attributes]
78 | [[clang::noinline]] return non_dependent(x), baz(x + 1);
| ^
/tmp/test.cpp:123:3: note: in instantiation of function template specialization 'baz<3>' requested here
123 | baz<3>(0); // #BAZ_INST
| ^
/tmp/test.cpp:67:1: note: conflicting attribute is here
67 | int baz(int x) { // #BAZ
| ^
/tmp/test.cpp:78:25: warning: statement attribute 'clang::noinline' has higher precedence than function attribute 'always_inline' [-Wignored-attributes]
78 | [[clang::noinline]] return non_dependent(x), baz(x + 1);
| ^
/tmp/test.cpp:78:50: note: in instantiation of function template specialization 'baz<2>' requested here
78 | [[clang::noinline]] return non_dependent(x), baz(x + 1);
| ^
/tmp/test.cpp:123:3: note: in instantiation of function template specialization 'baz<3>' requested here
123 | baz<3>(0); // #BAZ_INST
| ^
/tmp/test.cpp:67:1: note: conflicting attribute is here
67 | int baz(int x) { // #BAZ
| ^
/tmp/test.cpp:78:25: warning: statement attribute 'clang::noinline' has higher precedence than function attribute 'always_inline' [-Wignored-attributes]
78 | [[clang::noinline]] return non_dependent(x), baz(x + 1);
| ^
/tmp/test.cpp:78:50: note: in instantiation of function template specialization 'baz<1>' requested here
78 | [[clang::noinline]] return non_dependent(x), baz(x + 1);
| ^
/tmp/test.cpp:78:50: note: in instantiation of function template specialization 'baz<2>' requested here
/tmp/test.cpp:123:3: note: in instantiation of function template specialization 'baz<3>' requested here
123 | baz<3>(0); // #BAZ_INST
| ^
/tmp/test.cpp:67:1: note: conflicting attribute is here
67 | int baz(int x) { // #BAZ
| ^
/tmp/test.cpp:92:50: error: no matching function for call to 'dependent'
92 | [[clang::noinline]] return non_dependent(x) + (dependent(x) + ...);
| ^~~~~~~~~~~~
/tmp/test.cpp:124:3: note: in instantiation of function template specialization 'variadic_baz<0, 1, 2>' requested here
124 | variadic_baz<0, 1, 2>(0); // #VARIADIC_INST
| ^
/tmp/test.cpp:60:5: note: candidate template ignored: substitution failure [with D = 2]
60 | int dependent(int x){ return x + D;}
| ^
/tmp/test.cpp:92:50: error: no matching function for call to 'dependent'
92 | [[clang::noinline]] return non_dependent(x) + (dependent(x) + ...);
| ^~~~~~~~~~~~
/tmp/test.cpp:60:5: note: candidate template ignored: substitution failure [with D = 1]
60 | int dependent(int x){ return x + D;}
| ^
/tmp/test.cpp:92:50: error: no matching function for call to 'dependent'
92 | [[clang::noinline]] return non_dependent(x) + (dependent(x) + ...);
| ^~~~~~~~~~~~
/tmp/test.cpp:60:5: note: candidate template ignored: substitution failure [with D = 0]
60 | int dependent(int x){ return x + D;}
| ^
/tmp/test.cpp:92:23: warning: statement attribute 'clang::noinline' has higher precedence than function attribute 'always_inline' [-Wignored-attributes]
92 | [[clang::noinline]] return non_dependent(x) + (dependent(x) + ...);
| ^
/tmp/test.cpp:63:1: note: conflicting attribute is here
63 | int non_dependent(int x){return x;}
| ^
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-22/bin/clang -cc1 -triple x86_64-pc-linux-gnu -emit-obj -dumpdir a- -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name test.cpp -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=all -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/home/fuzz/WorkSpace/fusion-fuzz/projects/clang -fcoverage-compilation-dir=/home/fuzz/WorkSpace/fusion-fuzz/projects/clang -resource-dir /usr/lib/llvm-22/lib/clang/22 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/15/../../../../include/c++/15 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/15/../../../../include/x86_64-linux-gnu/c++/15 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/15/../../../../include/c++/15/backward -internal-isystem /usr/lib/llvm-22/lib/clang/22/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/15/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fdeprecated-macro -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcxx-exceptions -fexceptions -faddrsig -fdwarf2-cfi-asm -o /tmp/test-24a7ef.o -x c++ /tmp/test.cpp
1. parser at end of file
2. /tmp/test.cpp:84:5: instantiating function definition 'variadic_baz<0, 1, 2>'
#0 0x00007fb7bf2c25ea llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/usr/lib/llvm-22/bin/../lib/libLLVM.so.22.1+0x4e9f5ea)
#1 0x00007fb7bf2bfdf7 llvm::sys::RunSignalHandlers() (/usr/lib/llvm-22/bin/../lib/libLLVM.so.22.1+0x4e9cdf7)
#2 0x00007fb7bf2c3334 (/usr/lib/llvm-22/bin/../lib/libLLVM.so.22.1+0x4ea0334)
#3 0x00007fb7b9e94970 (/usr/lib/x86_64-linux-gnu/libc.so.6+0x40970)
#4 0x00007fb7c4e55710 clang::Expr::getReferencedDeclOfCallee() (/usr/lib/llvm-22/bin/../lib/libclang-cpp.so.22.1+0x19e7710)
#5 0x00007fb7c5aa3bb4 clang::Sema::CheckNoInlineAttr(clang::Stmt const*, clang::Stmt const*, clang::AttributeCommonInfo const&) (/usr/lib/llvm-22/bin/../lib/libclang-cpp.so.22.1+0x2635bb4)
#6 0x00007fb7c5c0fb22 (/usr/lib/llvm-22/bin/../lib/libclang-cpp.so.22.1+0x27a1b22)
#7 0x00007fb7c5bfde2d (/usr/lib/llvm-22/bin/../lib/libclang-cpp.so.22.1+0x278fe2d)
#8 0x00007fb7c5bbc12f clang::Sema::SubstStmt(clang::Stmt*, clang::MultiLevelTemplateArgumentList const&) (/usr/lib/llvm-22/bin/../lib/libclang-cpp.so.22.1+0x274e12f)
#9 0x00007fb7c5c3c0f4 clang::Sema::InstantiateFunctionDefinition(clang::SourceLocation, clang::FunctionDecl*, bool, bool, bool) (/usr/lib/llvm-22/bin/../lib/libclang-cpp.so.22.1+0x27ce0f4)
#10 0x00007fb7c5c3f458 clang::Sema::PerformPendingInstantiations(bool, bool) (/usr/lib/llvm-22/bin/../lib/libclang-cpp.so.22.1+0x27d1458)
#11 0x00007fb7c5520a85 clang::Sema::ActOnEndOfTranslationUnitFragment(clang::TUFragmentKind) (/usr/lib/llvm-22/bin/../lib/libclang-cpp.so.22.1+0x20b2a85)
#12 0x00007fb7c5521081 clang::Sema::ActOnEndOfTranslationUnit() (/usr/lib/llvm-22/bin/../lib/libclang-cpp.so.22.1+0x20b3081)
#13 0x00007fb7c4b645bc clang::Parser::ParseTopLevelDecl(clang::OpaquePtr&, clang::Sema::ModuleImportState&) (/usr/lib/llvm-22/bin/../lib/libclang-cpp.so.22.1+0x16f65bc)
#14 0x00007fb7c4aa367e clang::ParseAST(clang::Sema&, bool, bool) (/usr/lib/llvm-22/bin/../lib/libclang-cpp.so.22.1+0x163567e)
#15 0x00007fb7c67ddbef clang::FrontendAction::Execute() (/usr/lib/llvm-22/bin/../lib/libclang-cpp.so.22.1+0x336fbef)
#16 0x00007fb7c6751044 clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) (/usr/lib/llvm-22/bin/../lib/libclang-cpp.so.22.1+0x32e3044)
#17 0x00007fb7c686bf9a clang::ExecuteCompilerInvocation(clang::CompilerInstance*) (/usr/lib/llvm-22/bin/../lib/libclang-cpp.so.22.1+0x33fdf9a)
#18 0x0000564ae5e1ccdd cc1_main(llvm::ArrayRef, char const*, void*) (/usr/lib/llvm-22/bin/clang+0x13cdd)
#19 0x0000564ae5e1953b (/usr/lib/llvm-22/bin/clang+0x1053b)
#20 0x0000564ae5e188a3 clang_main(int, char**, llvm::ToolContext const&) (/usr/lib/llvm-22/bin/clang+0xf8a3)
#21 0x0000564ae5e279aa main (/usr/lib/llvm-22/bin/clang+0x1e9aa)
#22 0x00007fb7b9e7df77 (/usr/lib/x86_64-linux-gnu/libc.so.6+0x29f77)
#23 0x00007fb7b9e7e027 __libc_start_main (/usr/lib/x86_64-linux-gnu/libc.so.6+0x2a027)
#24 0x0000564ae5e171d1 _start (/usr/lib/llvm-22/bin/clang+0xe1d1)
clang++: error: unable to execute command: Segmentation fault (core dumped)
clang++: error: clang frontend command failed due to signal (use -v to see invocation)
Debian clang version 22.1.8 (1+b1)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/lib/llvm-22/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/test-6d4705.cpp
clang++: note: diagnostic msg: /tmp/test-6d4705.sh
clang++: note: diagnostic msg:

********************
```

To reproduce:
```
clang++ ./test.cpp
```

Compiler version:
```
Debian clang version 22.1.8 (1+b1)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/lib/llvm-22/bin
```

*This bug was found by [fusion-fuzz](https://github.com/fusion-fuzz/fusion-fuzz)*

Contributor guide

Open the contributing guide

Research direction

Start by reproducing the reported template-instantiation crash and tracing Clang's Sema entry point CheckNoInlineAttr through Expr::getReferencedDeclOfCallee. The issue provides no source file or regression test path, so first reduce the supplied example to the failing variadic case. Done means the instantiation no longer segfaults and produces the intended diagnostics.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.