| From: | Henson Choi <assam258(at)gmail(dot)com> |
|---|---|
| To: | Matheus Alcantara <matheusssilv97(at)gmail(dot)com> |
| Cc: | pgsql-hackers <pgsql-hackers(at)postgresql(dot)org>, Tatsuo Ishii <ishii(at)postgresql(dot)org>, Andres Freund <andres(at)anarazel(dot)de>, jian he <jian(dot)universality(at)gmail(dot)com> |
| Subject: | Re: LLVM JIT: any JIT-compiled query crashes (SIGILL) on a libLLVM 19 + ASAN build |
| Date: | 2026-10-26 02:15:46 |
| Message-ID: | CAAAe_zA49cHXJ7J4hmeuSa75s+eSgCOnK_9Uvq6wUEJ2Bgvdfg@mail.gmail.com |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-hackers |
Hi hackers,
On an AddressSanitizer build, every JIT-compiled query crashes or
hangs the backend while the JIT generates code for it, even "SELECT 1"
with jit = on and jit_above_cost = 0. On Linux the backend dies of
SIGILL; on macOS it loops forever inside LLVM.
The JIT takes the LLVM types of PostgreSQL's structs and scalars from
the global variables in llvmjit_types.c, which is compiled to bitcode
with the same flags. To add a redzone after each variable,
AddressSanitizer wraps it in a struct of type { T, [N x i8] }: the
declared type T at offset zero, followed by the redzone. So
llvm_pg_var_type() hands that struct type to the JIT instead of T,
and, for example, a GEP for a field of ExprContext indexes a struct
with two elements, which makes the IR invalid.
v4, attached, leaves the build systems alone and fixes this where the
types are read:
0001 The jit regression test. It turns jit on and sets
jit_above_cost to zero, so that "SELECT 1" is JIT-compiled.
0002 llvm_pg_var_type() raises an error for a type that is not of
the expected form (a scalar or a named struct) instead of
handing it to the JIT.
0003 When llvm_pg_var_type() finds a struct whose second element
is a redzone, it returns the type of the first element, T.
0004 An alternative to 0003: exclude the variables in
llvmjit_types.c from AddressSanitizer instrumentation with a
clang attribute pragma, so that they are never wrapped.
Only one of 0003 and 0004 is needed; whichever is better can be
picked. The trade-off is:
0003 Leaves llvmjit_types.c alone and changes only llvmjit.c, but
relies on the shape of the struct that AddressSanitizer
builds ({ T, [N x i8] }). If that shape changes, it falls
back to the error from 0002.
0004 Keeps the wrapping from happening at all, so it does not
depend on how AddressSanitizer works inside, but adds a
clang-specific pragma to llvmjit_types.c.
0001 comes first only to show what each of the following patches
changes. Committed alone, it breaks CI's AddressSanitizer build, so
it should go in together with 0002 and 0003 (or 0004), or after them.
Testing (the jit test, run by pg_regress against a running server):
macOS arm64 Rocky Linux 10 x86_64
Apple clang ASan gcc 14 ASan
LLVM 21.1.7, meson LLVM 21.1.8, meson
0001 hangs inside LLVM SIGILL
0001+0002 ERROR ERROR
0001+0002+0003 ok ok
0001+0002+0004 ok ok
It of course works with both 0003 and 0004 applied as well: the
variables are then never wrapped, so the code in 0003 is just not
reached.
Best regards,
Henson
| Attachment | Content-Type | Size |
|---|---|---|
| v4-0001-Add-a-regression-test-that-runs-a-query-through-t.patch | application/octet-stream | 2.7 KB |
| v4-0002-Raise-an-error-for-a-JIT-type-variable-of-an-unex.patch | application/octet-stream | 1.6 KB |
| v4-0003-Make-the-LLVM-JIT-work-on-an-AddressSanitizer-bui.patch | application/octet-stream | 2.0 KB |
| v4-0004-Exclude-the-variables-in-llvmjit_types.c-from-Add.patch | application/octet-stream | 1.9 KB |
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Tom Lane | 2026-10-26 02:20:04 | Let's drop support for named POSIX semaphores |
| Previous Message | Paul Kim | 2026-10-26 01:34:58 | Re: Be strict when request to flush past end of WAL in WaitXLogInsertionsToFinish |