[PATCH] JIT inlining SIGSEGV in MemoryBuffer::getFile with LLVM 17 / GCC 13

From: Grigorev Jurij <ju(dot)grigorev(at)ftdata(dot)ru>
To: PostgreSQL Hackers <pgsql-hackers(at)lists(dot)postgresql(dot)org>
Cc: "thomas(dot)munro(at)gmail(dot)com" <thomas(dot)munro(at)gmail(dot)com>, "andres(at)anarazel(dot)de" <andres(at)anarazel(dot)de>
Subject: [PATCH] JIT inlining SIGSEGV in MemoryBuffer::getFile with LLVM 17 / GCC 13
Date: 2026-08-19 15:59:04
Message-ID: dc9fb68c682c4eec9d18f1007bd568f8@localhost.localdomain
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi there, hackers!

I ran into a JIT crash on openSUSE Leap 15.6 (LLVM 17.0.6, clang 17.0.6, GCC 13, just default devel packages). With jit_above_cost and jit_inline_above_cost both 0, check-world loses about a hundred tests to signal 11, always starting in the same place:
```
LOG: executing LLVM IR with JIT
server closed the connection unexpectedly
...
LOG: server process (PID ...) was terminated by signal 11: Segmentation Fault
DETAIL: Failed process was running:
SELECT t1.b1, NOT t1.b1, t2.b1, NOT t2.b1 FROM BOOLTBL1 t1, BOOLTBL2 t2;
```

If I skip llvm_inline(), everything is green. Ubuntu with LLVM 18 is fine too. So this is inlining, and it is specific to that toolchain.
I spent a while looking at bitcode loading and IRMover before a backtrace made it obvious that it dies inside llvm::MemoryBuffer::getFile(), called from llvm_load_summary() on postgres.index.bc, before the file is even opened.
```
#0 llvm::MemoryBuffer::getFile(...)
#1 llvm_load_summary
#2 add_module_to_inline_search_path
#3 llvm_build_inline_plan
#4 llvm_inline
```

load_module() already reads the per-object .bc files through LLVMCreateMemoryBufferWithContentsOfFile() and never crashes. A tiny C++ program that just calls getFile("/etc/hostname") SIGSEGVs the same way; the C API on that path, and on the 2.4MB index, is fine, and getModuleSummaryIndex() then works. After switching llvm_load_summary() to the C API, the boolean join comes back with 4 rows and inlining actually happens:
```
DEBUG: time to inline: 21.186 ms, successfully inlined 6 functions
```

Andres, Thomas: I am copying you because you're knowing about this part more than anyone, and some already looked at this symbol in BUG #18254 [1]. That one was FreeBSD / LLVM 16 failing to load llvmjit.so:
```
Undefined symbol
"_ZN4llvm12MemoryBuffer7getFileERKNS_5TwineEbbbSt8optionalINS_5AlignEE"
```

I could not find anyone reporting a SIGSEGV on this path, think we are looking at the other face of the same ABI problem when the symbol resolves, then the call blows up.
Someone hit the same getFile() crash outside Postgres, on LLVM 16/17 - [2].

The overload takes std::optional<llvm::Align>, and the default arguments are built at the call site. A GCC 13-built llvmjit.so and a distro libLLVM.so.17 do not have to agree on that layout. Matching the compiler that built LLVM would avoid it, but that is not how packages are usually built: postgres often gets GCC, LLVM comes from the distro, and the C++ details of std::optional are not a stable contract between the two. The C API is. So I think this is worth fixing in tree, not treating as a local toolchain footgun.

The attached patch just does what load_module() already does. Hope to see your thoughts on this!

[1] https://postgr.es/m/18254-e303b33b2317c861@postgresql.org
[2] https://stackoverflow.com/questions/78537302/segmentation-fault-when-llvmmemorybuffergetfile-on-aarch64-with-llvm16-17

Regards,
Yuriy Grigoryev

Attachment Content-Type Size
v1-0001-jit-summary-c-api.patch application/octet-stream 2.7 KB

Browse pgsql-hackers by date

  From Date Subject
Next Message Andrey Borodin 2026-08-19 16:07:55 Re: Reduce WAL volume for heap tuple hint bits
Previous Message Fujii Masao 2026-08-19 15:58:33 Re: Failing assertion while taking a restartpoint during crash recovery