From e8c2c3682e22a6b8eb42caae86d191f01fb04df0 Mon Sep 17 00:00:00 2001
From: Piotr Balcer <piotr.balcer@intel.com>
Date: Fri, 22 Mar 2019 14:59:29 +0100
Subject: [PATCH] obj: fix crash after large undo log recovery

The 'operation_context' structure was being created twice, once as part
of lane initialization, and once as part of the recovery operation.
The first context is used for all operations, whereas the second context
lives only for the duration of the undo log recovery.

The problem was that the recovery process can change the persistent
state in such a way that the context runtime information becomes stale.
This is perfectly fine, and it's handled for the operation that
does the recovery - but at the same time, this makes the data of the
lane context invalid.

This bug was triggering ASSERTs in debug builds and might have led to
NULL dereference on release builds. Fortunately the crash happens
after recovery has been finished and simply restarting the application
gets rid of the problem.

The fix is quite simple: instead of duplicating the runtime state for
the purpose of recovery, simply use the one that already exists in the
lane.
---
 src/libpmemobj/lane.c                | 18 +--------
 src/test/obj_recovery/Makefile       |  3 +-
 src/test/obj_recovery/TEST9          | 56 ++++++++++++++++++++++++++++
 src/test/obj_recovery/obj_recovery.c | 33 ++++++++++++++--
 4 files changed, 90 insertions(+), 20 deletions(-)
 create mode 100755 src/test/obj_recovery/TEST9

diff --git a/src/libpmemobj/lane.c b/src/libpmemobj/lane.c
index 28e64fa85..1849e9858 100644
--- a/src/libpmemobj/lane.c
+++ b/src/libpmemobj/lane.c
@@ -1,5 +1,5 @@
 /*
- * Copyright 2015-2018, Intel Corporation
+ * Copyright 2015-2019, Intel Corporation
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -402,24 +402,10 @@ lane_recover_and_section_boot(PMEMobjpool *pop)
 	 * a undo recovery might require deallocation of the next ulogs.
 	 */
 	for (i = 0; i < pop->nlanes; ++i) {
-		layout = lane_get_layout(pop, i);
-
-		struct ulog *undo = (struct ulog *)&layout->undo;
-
-		struct operation_context *ctx = operation_new(
-			undo,
-			LANE_UNDO_SIZE,
-			lane_undo_extend, (ulog_free_fn)pfree, &pop->p_ops,
-			LOG_TYPE_UNDO);
-		if (ctx == NULL) {
-			LOG(2, "undo recovery failed %" PRIu64 " %d",
-				i, err);
-			return err;
-		}
+		struct operation_context *ctx = pop->lanes_desc.lane[i].undo;
 		operation_resume(ctx);
 		operation_process(ctx);
 		operation_finish(ctx);
-		operation_delete(ctx);
 	}
 
 	return 0;
diff --git a/src/test/obj_recovery/Makefile b/src/test/obj_recovery/Makefile
index 40fcd353d..d8afac8b2 100644
--- a/src/test/obj_recovery/Makefile
+++ b/src/test/obj_recovery/Makefile
@@ -1,5 +1,5 @@
 #
-# Copyright 2015-2018, Intel Corporation
+# Copyright 2015-2019, Intel Corporation
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions
@@ -39,5 +39,6 @@ OBJS = obj_recovery.o
 
 LIBPMEM=y
 LIBPMEMOBJ=y
+LIBPMEMCOMMON=y
 
 include ../Makefile.inc
diff --git a/src/test/obj_recovery/TEST9 b/src/test/obj_recovery/TEST9
new file mode 100755
index 000000000..a039777f9
--- /dev/null
+++ b/src/test/obj_recovery/TEST9
@@ -0,0 +1,56 @@
+#!/usr/bin/env bash
+#
+# Copyright 2019, Intel Corporation
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#
+#     * Neither the name of the copyright holder nor the names of its
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+#
+# src/test/obj_recovery/TEST9 -- unit test for pool recovery
+#
+
+# standard unit test setup
+. ../unittest/unittest.sh
+
+require_test_type medium
+require_no_asan
+
+setup
+
+# exits in the middle of transaction, so pool cannot be closed
+export MEMCHECK_DONT_CHECK_LEAKS=1
+
+create_holey_file 16M $DIR/testfile
+
+expect_normal_exit ./obj_recovery$EXESUFFIX $DIR/testfile n c l
+expect_normal_exit ./obj_recovery$EXESUFFIX $DIR/testfile n o l
+
+check
+
+pass
diff --git a/src/test/obj_recovery/obj_recovery.c b/src/test/obj_recovery/obj_recovery.c
index b53840771..348a843aa 100644
--- a/src/test/obj_recovery/obj_recovery.c
+++ b/src/test/obj_recovery/obj_recovery.c
@@ -1,5 +1,5 @@
 /*
- * Copyright 2015-2018, Intel Corporation
+ * Copyright 2015-2019, Intel Corporation
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -46,6 +46,8 @@ POBJ_LAYOUT_ROOT(recovery, struct root);
 POBJ_LAYOUT_TOID(recovery, struct foo);
 POBJ_LAYOUT_END(recovery);
 
+#define MB (1 << 20)
+
 struct foo {
 	int bar;
 };
@@ -53,6 +55,7 @@ struct foo {
 struct root {
 	PMEMmutex lock;
 	TOID(struct foo) foo;
+	char large_data[MB];
 };
 
 #define BAR_VALUE 5
@@ -67,14 +70,14 @@ main(int argc, char *argv[])
 
 	if (argc != 5)
 		UT_FATAL("usage: %s [file] [lock: y/n] "
-			"[cmd: c/o] [type: n/f/s]",
+			"[cmd: c/o] [type: n/f/s/l]",
 			argv[0]);
 
 	const char *path = argv[1];
 
 	PMEMobjpool *pop = NULL;
 	int exists = argv[3][0] == 'o';
-	enum { TEST_NEW, TEST_FREE, TEST_SET } type;
+	enum { TEST_NEW, TEST_FREE, TEST_SET, TEST_LARGE } type;
 
 	if (argv[4][0] == 'n')
 		type = TEST_NEW;
@@ -82,6 +85,8 @@ main(int argc, char *argv[])
 		type = TEST_FREE;
 	else if (argv[4][0] == 's')
 		type = TEST_SET;
+	else if (argv[4][0] == 'l')
+		type = TEST_LARGE;
 	else
 		UT_FATAL("invalid type");
 
@@ -143,6 +148,28 @@ main(int argc, char *argv[])
 		} else {
 			UT_ASSERT(D_RW(D_RW(root)->foo)->bar == BAR_VALUE);
 		}
+	} else if (type == TEST_LARGE) {
+		if (!exists) {
+			TX_BEGIN(pop) {
+				TX_MEMSET(D_RW(root)->large_data, 0xc, MB);
+				pmemobj_persist(pop,
+					D_RW(root)->large_data, MB);
+				VALGRIND_PMEMCHECK_END_TX;
+
+				exit(0);
+			} TX_END
+		} else {
+			UT_ASSERT(util_is_zeroed(D_RW(root)->large_data, MB));
+
+			TX_BEGIN(pop) { /* we should be able to start TX */
+				TX_MEMSET(D_RW(root)->large_data, 0xc, MB);
+				pmemobj_persist(pop,
+					D_RW(root)->large_data, MB);
+				VALGRIND_PMEMCHECK_END_TX;
+
+				pmemobj_tx_abort(0);
+			} TX_END
+		}
 	} else if (type == TEST_NEW) {
 		if (!exists) {
 			TX_BEGIN_PARAM(pop, lock_type, lock) {
-- 
GitLab