Skip to content

Commit 7f8356b

Browse files
committed
Initialize TransactionState and user ID consistently at transaction start
If a failure happens when a transaction is starting between the moment the transaction status is changed from TRANS_DEFAULT to TRANS_START and the moment the current user ID and security context flags are fetched via GetUserIdAndSecContext(), or before initializing its basic fields, then those may get reset to incorrect values when the transaction aborts, leaving the session in an inconsistent state. One problem reported is that failing a starting transaction at the first query of a session could cause several kinds of system crashes on the follow-up queries. In order to solve that, move the initialization of the transaction state fields and the call of GetUserIdAndSecContext() in charge of fetching the current user ID close to the point where the transaction status is switched to TRANS_START, where there cannot be any error triggered in-between, per an idea of Tom Lane. This properly ensures that the current user ID, the security context flags and that the basic fields of TransactionState remain consistent even if the transaction fails while starting. Reported-by: Richard Guo Diagnosed-By: Richard Guo Author: Michael Paquier Reviewed-by: Tom Lane Discussion: https://postgr.es/m/CAN_9JTxECSb=pEPcb0a8d+6J+bDcOZ4=DgRo_B7Y5gRHJUM=Rw@mail.gmail.com Backpatch-through: 9.4
1 parent 32060f6 commit 7f8356b

File tree

1 file changed

+26
-22
lines changed
  • src/backend/access/transam

1 file changed

+26
-22
lines changed

src/backend/access/transam/xact.c

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1834,20 +1834,38 @@ StartTransaction(void)
18341834

18351835
Assert(XactTopTransactionId == InvalidTransactionId);
18361836

1837-
/*
1838-
* check the current transaction state
1839-
*/
1840-
if (s->state != TRANS_DEFAULT)
1841-
elog(WARNING, "StartTransaction while in %s state",
1842-
TransStateAsString(s->state));
1837+
/* check the current transaction state */
1838+
Assert(s->state == TRANS_DEFAULT);
18431839

18441840
/*
1845-
* set the current transaction state information appropriately during
1846-
* start processing
1841+
* Set the current transaction state information appropriately during
1842+
* start processing. Note that once the transaction status is switched
1843+
* this process cannot fail until the user ID and the security context
1844+
* flags are fetched below.
18471845
*/
18481846
s->state = TRANS_START;
18491847
s->transactionId = InvalidTransactionId; /* until assigned */
18501848

1849+
/*
1850+
* initialize current transaction state fields
1851+
*
1852+
* note: prevXactReadOnly is not used at the outermost level
1853+
*/
1854+
s->nestingLevel = 1;
1855+
s->gucNestLevel = 1;
1856+
s->childXids = NULL;
1857+
s->nChildXids = 0;
1858+
s->maxChildXids = 0;
1859+
1860+
/*
1861+
* Once the current user ID and the security context flags are fetched,
1862+
* both will be properly reset even if transaction startup fails.
1863+
*/
1864+
GetUserIdAndSecContext(&s->prevUser, &s->prevSecContext);
1865+
1866+
/* SecurityRestrictionContext should never be set outside a transaction */
1867+
Assert(s->prevSecContext == 0);
1868+
18511869
/*
18521870
* Make sure we've reset xact state variables
18531871
*
@@ -1928,20 +1946,6 @@ StartTransaction(void)
19281946
xactStopTimestamp = 0;
19291947
pgstat_report_xact_timestamp(xactStartTimestamp);
19301948

1931-
/*
1932-
* initialize current transaction state fields
1933-
*
1934-
* note: prevXactReadOnly is not used at the outermost level
1935-
*/
1936-
s->nestingLevel = 1;
1937-
s->gucNestLevel = 1;
1938-
s->childXids = NULL;
1939-
s->nChildXids = 0;
1940-
s->maxChildXids = 0;
1941-
GetUserIdAndSecContext(&s->prevUser, &s->prevSecContext);
1942-
/* SecurityRestrictionContext should never be set outside a transaction */
1943-
Assert(s->prevSecContext == 0);
1944-
19451949
/*
19461950
* initialize other subsystems for new transaction
19471951
*/

0 commit comments

Comments
 (0)