Skip to content

Commit 88b0eba

Browse files
committed
Bug#35277407 InnoDB:trx hangs due to wrong trx->in_innodb value
This commit backports the fix to 8.0 This patch will solve the following duplicates of this bug: Bug #112425: trx_t might be Use-After-Free in innobase_commit_by_xid Bug #99643: innobase_commit_by_xid/innobase_rollback_by_xid is not thread safe Bug #105036: trx would be used after free in `innobase_commit_by_xid` and rollback Background: TrxInInnoDB is a RAII wrapper for trx_t object used to track if the transaction's thread is currently executing within InnoDB code. It is acquired on all entry points, and as Innodb can be entered "recursively", the trx->in_depth is used to track the balance of enters and exits. On the outermost enter, the thread additionally checks if trx->in_innodb has the TRX_FORCE_ROLLBACK (0x8000 0000) flag set, which means a high priority transaction is attempting an asynchronous rollback of this transaction, so to avoid races, this thread should wait for the rollback to complete. Issue: TrxInInnoDB's destructor calls exit which resets in_depth and in_innodb increased by enter. However innobase_commit_by_xid and innobase_rollback_by_xid calls trx_free_for_background which returns the trx back to the pool, before the destructor is called. If this trx is being reused by another thread, it can lead to data-race and corrupted value of in_depth and in_innodb. If in_depth gets the value of -1, subsequent calls to enter and exit will bump in_innodb by one. This can lead to indefinite wait if in_innodb reaches TRX_FORCE_ROLLBACK. Fix: Ensure that TrxInInnoDB calls exit before returning the trx object to the pool. Further add checks to catch corrupt values of in_depth when freeing trx. Trx state validation before free was missed in trx_free_prepared_or_active_recovered Thanks to Shaohua Wang (Alibaba, Ex-Innodb) for the contribution Change-Id: Ibf79bec85ffa0eaf65f565c169db61536bff10a2
1 parent 14f89c1 commit 88b0eba

File tree

3 files changed

+16
-6
lines changed

3 files changed

+16
-6
lines changed

storage/innobase/handler/ha_innodb.cc

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20073,9 +20073,11 @@ static xa_status_code innobase_commit_by_xid(
2007320073
trx_t *trx = trx_get_trx_by_xid(xid);
2007420074

2007520075
if (trx != nullptr) {
20076-
TrxInInnoDB trx_in_innodb(trx);
20076+
{
20077+
TrxInInnoDB trx_in_innodb(trx);
2007720078

20078-
innobase_commit_low(trx);
20079+
innobase_commit_low(trx);
20080+
}
2007920081
ut_ad(trx->mysql_thd == nullptr);
2008020082
/* use cases are: disconnected xa, slave xa, recovery */
2008120083
trx_deregister_from_2pc(trx);
@@ -20101,9 +20103,12 @@ static xa_status_code innobase_rollback_by_xid(
2010120103
trx_t *trx = trx_get_trx_by_xid(xid);
2010220104

2010320105
if (trx != nullptr) {
20104-
TrxInInnoDB trx_in_innodb(trx);
20106+
int ret;
20107+
{
20108+
TrxInInnoDB trx_in_innodb(trx);
2010520109

20106-
int ret = innobase_rollback_trx(trx);
20110+
ret = innobase_rollback_trx(trx);
20111+
}
2010720112

2010820113
trx_deregister_from_2pc(trx);
2010920114
ut_ad(!trx->will_lock);

storage/innobase/include/trx0types.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ static const uint32_t TRX_FORCE_ROLLBACK_DISABLE = 1 << 29;
6464
/** Mark the transaction for forced rollback */
6565
static const uint32_t TRX_FORCE_ROLLBACK = 1U << 31;
6666

67-
/** For masking out the above four flags */
67+
/** For masking out the above flags */
6868
static const uint32_t TRX_FORCE_ROLLBACK_MASK = 0x1FFFFFFF;
6969

7070
/** Transaction execution states when trx->state == TRX_STATE_ACTIVE */

storage/innobase/trx/trx0trx.cc

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,9 @@ struct TrxFactory {
265265

266266
trx->dict_operation_lock_mode = 0;
267267

268+
ut_a(trx->in_innodb == 0);
269+
ut_a(trx->in_depth == 0);
270+
268271
trx->xid = ut::new_withkey<xid_t>(UT_NEW_THIS_FILE_PSI_KEY);
269272

270273
trx->detailed_error = reinterpret_cast<char *>(
@@ -501,11 +504,12 @@ static void trx_free(trx_t *&trx) {
501504

502505
ut_ad(trx->read_view == nullptr);
503506
ut_ad(trx->is_dd_trx == false);
507+
ut_ad(trx->in_depth == 0);
504508

505509
/* trx locking state should have been reset before returning trx
506510
to pool */
507511
ut_ad(trx->will_lock == 0);
508-
512+
trx->in_innodb &= TRX_FORCE_ROLLBACK_MASK;
509513
trx_pools->mem_free(trx);
510514

511515
trx = nullptr;
@@ -620,6 +624,7 @@ void trx_free_prepared_or_active_recovered(trx_t *trx) {
620624
trx->state.store(TRX_STATE_NOT_STARTED, std::memory_order_relaxed);
621625
trx->will_lock = 0;
622626

627+
trx_validate_state_before_free(trx);
623628
trx_free(trx);
624629
}
625630

0 commit comments

Comments
 (0)