[MDEV-15772] Potential list overrun during XA recovery Created: 2018-04-04 Updated: 2019-05-08 Resolved: 2019-04-24 |
|
| Status: | Closed |
| Project: | MariaDB Server |
| Component/s: | XA |
| Affects Version/s: | 5.5, 10.0, 10.1, 10.2, 10.3 |
| Fix Version/s: | 10.2.24, 5.5.64, 10.1.39, 10.3.15, 10.4.5 |
| Type: | Bug | Priority: | Critical |
| Reporter: | Sergey Vojtovich | Assignee: | Marko Mäkelä |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Issue Links: |
|
||||||||
| Description |
|
In ha_recover() array of XA transactions is allocated as following:
Then each storage engine fills this array. However at least InnoDB (trx_recover_for_mysql()) doesn't check for boundaries and may overrun this array. |
| Comments |
| Comment by Thirunarayanan Balathandayuthapani [ 2018-06-07 ] | ||||||||||||||||||||||||||||||||||||||||||||
|
MAX_XID_LIST_SIZE is 128 *1024. It is because 128 rollback segment and each 1024 slots for 16k page size. 128 rollback segment is constant irrespective of page size.
For page size 64k, there is a possibility of overrun the list of XA transaction. | ||||||||||||||||||||||||||||||||||||||||||||
| Comment by Sergey Vojtovich [ 2018-06-26 ] | ||||||||||||||||||||||||||||||||||||||||||||
|
The idea of recovery is such that it doesn't recover all transactions at once, but rather in batches of info.len size. And in fact there is a check for boundaries:
So, no overrun and no bug here, right? | ||||||||||||||||||||||||||||||||||||||||||||
| Comment by Thirunarayanan Balathandayuthapani [ 2018-06-26 ] | ||||||||||||||||||||||||||||||||||||||||||||
|
There is no bug for binlog driven recovery. InnoDB can return the same list again and again if the xa transaction given from external transaction manager.
I think, we can fix it inside innodb itself by identifying whether transaction is already part of XA_list. | ||||||||||||||||||||||||||||||||||||||||||||
| Comment by Sergey Vojtovich [ 2018-06-26 ] | ||||||||||||||||||||||||||||||||||||||||||||
|
Our current API goes along with XA standard. And it defines that RM must advance it's cursor past last returned transaction. In other words InnoDB is responsible for not returning same transactions multiple times. E.g. see https://www.ibm.com/support/knowledgecenter/en/SSB23S_1.1.0.14/gtpc2/cpp_xa_recover.html | ||||||||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2018-07-25 ] | ||||||||||||||||||||||||||||||||||||||||||||
|
svoj, do you mean that multiple successive invocations of XA RECOVER should not keep returning the same data? Currently, it should; only XA COMMIT or XA ROLLBACK will ‘consume’ recovered XIDs. | ||||||||||||||||||||||||||||||||||||||||||||
| Comment by Sergey Vojtovich [ 2018-07-25 ] | ||||||||||||||||||||||||||||||||||||||||||||
|
marko, not XA RECOVER, which is user interface and iterates xid_cache and returns full list. But rather xa_recover(), which is handler::recover() in our case, that is internal interface. | ||||||||||||||||||||||||||||||||||||||||||||
| Comment by Thirunarayanan Balathandayuthapani [ 2019-04-22 ] | ||||||||||||||||||||||||||||||||||||||||||||
|
Following patch sets the recover list length to the minimum value.
Below test case hangs with the above patch:
Basically, InnoDB returns the same transaction again and again. So it hangs during the restart of the server. | ||||||||||||||||||||||||||||||||||||||||||||
| Comment by Thirunarayanan Balathandayuthapani [ 2019-04-22 ] | ||||||||||||||||||||||||||||||||||||||||||||
|
Patch is in bb-5.5-thiru | ||||||||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2019-04-24 ] | ||||||||||||||||||||||||||||||||||||||||||||
|
I revised thiru’s fix, splitting the PREPARED state into two, and not introducing a Boolean flag. This should ease the merge to 10.2, which allocates transaction objects from a pool. svoj, the intention was that InnoDB would return the list of XA PREPARE transactions only once. But, it turns out that init_server_components() is invoking ha_recover() both directly and via tc_log->open(). To ensure that the second traversal will get a list of all transactions instead of getting an empty list, we will reset the state of each transaction at the end of the traversal. It would be nice to remove this work-around. |