1
0
mirror of https://github.com/MariaDB/server.git synced 2025-04-18 21:44:20 +03:00

MDEV-36152 mariadb-backup --backup crash during innodb_undo_log_truncate=ON, innodb_encrypt_log=ON

recv_sys_t::parse(): Allocate decrypt_buf also for storing==BACKUP
but limit its size to correspond to 1 byte of record payload.
Ensure that last_offset=0 for storing==BACKUP.
When parsing INIT_PAGE or FREE_PAGE, avoid an unnecessary
l.copy_if_needed() for storing!=YES.
When parsing EXTENDED in storing==BACKUP, properly invoke
l.copy_if_needed() on a large enough decrypt_buf.
When parsing WRITE, MEMMOVE, MEMSET in storing==BACKUP,
skip further validation (and potential overflow of the tiny decrypt_buf),
like we used to do before commit 46aaf328ce424aededdb61e59a48db05630563d5
(MDEV-35830).

Reviewed by: Debarun Banerjee
This commit is contained in:
Marko Mäkelä 2025-02-25 11:41:49 +02:00
parent 0c204bfb87
commit 809a0cebdc

View File

@ -2507,9 +2507,11 @@ restart:
ut_ad(log_sys.is_latest());
alignas(8) byte iv[MY_AES_BLOCK_SIZE];
byte *decrypt_buf= storing != BACKUP
? static_cast<byte*>(alloca(srv_page_size)) : nullptr;
byte *decrypt_buf=
static_cast<byte*>(alloca(storing == BACKUP
? 1/*type,length*/ + 5/*space_id*/ +
5/*page_no*/ + 1/*rlen*/
: srv_page_size));
const lsn_t start_lsn{lsn};
/* Check that the entire mini-transaction is included within the buffer */
@ -2599,7 +2601,10 @@ restart:
ut_d(std::set<page_id_t> modified);
#endif
uint32_t space_id= 0, page_no= 0, last_offset= 0;
uint32_t space_id= 0, page_no= 0;
/* The end offset the last write (always 0 in storing==BACKUP).
The value 1 means that no "same page" record is allowed. */
uint last_offset= 0;
bool got_page_op= false;
for (l= begin;; l+= rlen)
@ -2712,8 +2717,7 @@ restart:
{
mach_write_to_4(iv + 8, space_id);
mach_write_to_4(iv + 12, page_no);
byte eb[1/*type,length*/ + 5/*space_id*/ + 5/*page_no*/ + 1/*rlen*/];
if (*l.copy_if_needed(iv, eb, recs, 1) == TRIM_PAGES)
if (*l.copy_if_needed(iv, decrypt_buf, recs, 1) == TRIM_PAGES)
undo_space_trunc(space_id);
}
continue;
@ -2762,10 +2766,10 @@ restart:
case FREE_PAGE:
ut_ad(freed.emplace(id).second);
/* the next record must not be same_page */
last_offset= 1;
if (storing != BACKUP) last_offset= 1;
goto free_or_init_page;
case INIT_PAGE:
last_offset= FIL_PAGE_TYPE;
if (storing != BACKUP) last_offset= FIL_PAGE_TYPE;
free_or_init_page:
if (UNIV_UNLIKELY(rlen != 0))
goto record_corrupted;
@ -2797,7 +2801,8 @@ restart:
erase(r);
continue;
}
cl= l.copy_if_needed(iv, decrypt_buf, recs, rlen);
if (storing == YES)
cl= l.copy_if_needed(iv, decrypt_buf, recs, rlen);
break;
case EXTENDED:
if (storing == NO)
@ -2811,7 +2816,8 @@ restart:
continue;
if (UNIV_UNLIKELY(!rlen))
goto record_corrupted;
cl= l.copy_if_needed(iv, decrypt_buf, recs, rlen);
if (storing == YES || rlen == 1)
cl= l.copy_if_needed(iv, decrypt_buf, recs, rlen);
if (rlen == 1 && *cl == TRIM_PAGES)
{
if (!srv_is_undo_tablespace(space_id) ||
@ -2825,7 +2831,7 @@ restart:
truncated_undo_spaces[space_id - srv_undo_space_id_start]=
{ start_lsn, page_no };
/* the next record must not be same_page */
last_offset= 1;
if (storing != BACKUP) last_offset= 1;
if (undo_space_trunc)
undo_space_trunc(space_id);
continue;
@ -2833,7 +2839,7 @@ restart:
/* This record applies to an undo log or index page, and it
may be followed by subsequent WRITE or similar records for the
same page in the same mini-transaction. */
last_offset= FIL_PAGE_TYPE;
if (storing != BACKUP) last_offset= FIL_PAGE_TYPE;
break;
case OPTION:
/* OPTION records can be safely ignored in recovery */
@ -2850,6 +2856,8 @@ restart:
case WRITE:
case MEMMOVE:
case MEMSET:
if (storing == BACKUP)
continue;
if (storing == NO && UNIV_LIKELY(page_no != 0))
/* fil_space_set_recv_size_and_flags() is mandatory for storing==NO.
It is only applicable to page_no == 0. Other than that, we can just