[MDEV-27924] page_zip_copy_recs() corrupts ROW_FORMAT=COMPRESSED block descriptor Created: 2022-02-23  Updated: 2022-02-23  Resolved: 2022-02-23

Status: Closed
Project: MariaDB Server
Component/s: Storage Engine - InnoDB
Affects Version/s: 10.6.6, 10.7.2, 10.8.1, 10.6.7, 10.7.3, 10.8.2
Fix Version/s: 10.6.8, 10.7.4, 10.8.3

Type: Bug Priority: Blocker
Reporter: Marko Mäkelä Assignee: Marko Mäkelä
Resolution: Fixed Votes: 0
Labels: corruption, crash, regression-10.6

Issue Links:
Problem/Incident
is caused by MDEV-27058 Buffer page descriptors are too large Closed

 Description   

A number of innodb_zip tests crash if the preprocessor symbol UNIV_ZIP_COPY is defined. (There currently is no cmake option for enabling that.)
Here is a simple example:

10.6 92f79a22e63a3fef71106c64dfd8559ee9bdda4a

innodb_zip.wl6344_compress_level '16k,innodb' [ pass ]    573
***Warnings generated in error logs during shutdown after running tests: innodb_zip.wl6344_compress_level
 
mariadbd: /mariadb/10.6/storage/innobase/buf/buf0flu.cc:1212: void buf_flush_discard_page(buf_page_t*): Assertion `state == buf_page_t::FREED || state == buf_page_t::UNFIXED || state == buf_page_t::IBUF_EXIST || state == buf_page_t::REINIT' failed.

The cause is that page_zip_copy_recs() is invoking the default copy constructor of page_zip_des_t, which will also overwrite the fix data member, which we only stored in the page_zip_des_t object to avoid alignment loss.

The following fixes this block descriptor corruption that affects ROW_FORMAT=COMPRESSED pages in the event of a rather rare compression overflow:

diff --git a/storage/innobase/include/page0types.h b/storage/innobase/include/page0types.h
index 885d2290f7c..192e572543d 100644
--- a/storage/innobase/include/page0types.h
+++ b/storage/innobase/include/page0types.h
@@ -119,6 +119,16 @@ struct page_zip_des_t
 		       - reinterpret_cast<char*>(this));
 	}
 
+	page_zip_des_t() = default;
+	page_zip_des_t(const page_zip_des_t&) = default;
+
+	/* Initialize everything except the member "fix". */
+	page_zip_des_t(const page_zip_des_t& old, bool) {
+		memcpy((void*) this, (void*) &old,
+		       reinterpret_cast<char*>(&fix)
+		       - reinterpret_cast<char*>(this));
+	}
+
 private:
 	friend buf_pool_t;
 	friend buf_page_t;
diff --git a/storage/innobase/page/page0zip.cc b/storage/innobase/page/page0zip.cc
index ec90d73e765..73124b902f2 100644
--- a/storage/innobase/page/page0zip.cc
+++ b/storage/innobase/page/page0zip.cc
@@ -4562,7 +4562,7 @@ page_zip_copy_recs(
 	to the compressed data page. */
 	{
 		page_zip_t*	data = page_zip->data;
-		new (page_zip) page_zip_des_t(*src_zip);
+		new (page_zip) page_zip_des_t(*src_zip, false);
 		page_zip->data = data;
 	}
 	ut_ad(page_zip_get_trailer_len(page_zip, dict_index_is_clust(index))

With this patch (and UNIV_ZIP_COPY), only 2 tests fail with an expected result difference (MDEV-20752):

Failing test(s): innodb_zip.cmp_per_index innodb_zip.wl6347_comp_indx_stat


Generated at Thu Feb 08 09:56:40 UTC 2024 using Jira 8.20.16#820016-sha1:9d11dbea5f4be3d4cc21f03a88dd11d8c8687422.