Skip to content

Commit 5401e70

Browse files
committed
Avoid crashing when a JIT-inlined backend function throws an error.
errfinish() assumes that the __FUNC__ and __FILE__ arguments it's passed are compile-time constant strings that can just be pointed to rather than physically copied. However, it's possible for LLVM to generate code in which those pointers point into a dynamically loaded code segment. If that segment gets unloaded before we're done with the ErrorData struct, we have dangling pointers that will lead to SIGSEGV. In simple cases that won't happen, because we won't unload LLVM code before end of transaction. But it's possible to happen if the error is thrown within end-of-transaction code run by _SPI_commit or _SPI_rollback, because since commit 2e51781 those functions clean up by ending the transaction and starting a new one. Rather than fixing this by adding pstrdup() overhead to every elog/ereport sequence, let's fix it by copying the risky pointers in CopyErrorData(). That solves it for _SPI_commit/_SPI_rollback because they use that function to preserve the error data across the transaction end/restart sequence; and it seems likely that any other code doing something similar would need to do that too. I'm suspicious that this behavior amounts to an LLVM bug (or a bug in our use of it?), because it implies that string constant references that should be pointer-equal according to a naive understanding of C semantics will sometimes not be equal. However, even if it is a bug and someday gets fixed, we'll have to cope with the current behavior for a long time to come. Report and patch by me. Back-patch to all supported branches. Discussion: https://postgr.es/m/1565654.1719425368@sss.pgh.pa.us
1 parent 0e2f3d7 commit 5401e70

File tree

1 file changed

+17
-1
lines changed

1 file changed

+17
-1
lines changed

src/backend/utils/error/elog.c

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1573,7 +1573,21 @@ CopyErrorData(void)
15731573
newedata = (ErrorData *) palloc(sizeof(ErrorData));
15741574
memcpy(newedata, edata, sizeof(ErrorData));
15751575

1576-
/* Make copies of separately-allocated fields */
1576+
/*
1577+
* Make copies of separately-allocated strings. Note that we copy even
1578+
* theoretically-constant strings such as filename. This is because those
1579+
* could point into JIT-created code segments that might get unloaded at
1580+
* transaction cleanup. In some cases we need the copied ErrorData to
1581+
* survive transaction boundaries, so we'd better copy those strings too.
1582+
*/
1583+
if (newedata->filename)
1584+
newedata->filename = pstrdup(newedata->filename);
1585+
if (newedata->funcname)
1586+
newedata->funcname = pstrdup(newedata->funcname);
1587+
if (newedata->domain)
1588+
newedata->domain = pstrdup(newedata->domain);
1589+
if (newedata->context_domain)
1590+
newedata->context_domain = pstrdup(newedata->context_domain);
15771591
if (newedata->message)
15781592
newedata->message = pstrdup(newedata->message);
15791593
if (newedata->detail)
@@ -1586,6 +1600,8 @@ CopyErrorData(void)
15861600
newedata->context = pstrdup(newedata->context);
15871601
if (newedata->backtrace)
15881602
newedata->backtrace = pstrdup(newedata->backtrace);
1603+
if (newedata->message_id)
1604+
newedata->message_id = pstrdup(newedata->message_id);
15891605
if (newedata->schema_name)
15901606
newedata->schema_name = pstrdup(newedata->schema_name);
15911607
if (newedata->table_name)

0 commit comments

Comments
 (0)