Hello, some of my collegues found that orafce crashes with
postgresql compliled with dtrace.

=== The cause

The immediate cause was I think that it just did
LWLockNewTrancheId and forget to do LWLockRegisterTranche. But
this is hardly detectable by a module developer.


Typical tracepoint looks like the following.

> TRACE_POSTGRESQL_LWLOCK_WAIT_DONE(T_NAME(lock), mode);

Where T_NAME is (LWLockTrancheArray[(lock)->tranche]), as you see
T_NAME assumes that all tranches pointed from lwlocks are
available on the array but the tranches without
LWLockRegisterTranche are not. What is worse this doesn't harm so
much without dtrace (or LWLOCK_STATS or error in LWLockRelease)
. Even if dtrace is activated one or two unregistred tranches
(faultly) have their seat (filled with trash) at the end of the
array with the current code.

As my understanding there are two ways to use lwlocks in
extension.  One is using LWLockNewTrancheId and
LWLockRegisterTanche on shared memory provided by the module. The
other is using RequestNamedLWLockTranche in _PG_init and
GetNamedLWLockTranche to acquire locks provided by postgresql.

I couldn't find a documentation about lwlock and trance in
extentions, is there any?


=== How to fix this

The most straightforward way to fix this is chekcing the validity
of tranche id on initilization of a lwlock. Even though I think
that degradation won't be a matter here, NamedLWLockTrancheArray
makes the things very ineffective. After some consideration I
decided to remove NamedLWLockTrancheArray.


=== The patch

The attached patch (is for current master) is aming to fix all
the problem by doing the following two things.

- Remove NameLWLockTrancheArray and all tranches are registered
  in LWLockTrancheArray. This seems to work at least for
  !EXEC_BAKCEND environment but I haven't tested with EXEC_BACKEND.

- Check tranche id in LWLockInitialize.

The first one required refactoring of CreateLWLocks. It is
changed to register tranches first then initialize lwlokcs.

The problem is found with PG9.6 and this should be backpatched at
least to the version. I haven't tested PG9.5 and 9.4 but it seems
to need different amendment. 9.3 doesn't has tranche.

regards,

-- 
Kyotaro Horiguchi
NTT Open Source Software Center
>From e2c4619df6c51dd5b71630de7467b332f7449fc5 Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horiguchi.kyot...@lab.ntt.co.jp>
Date: Fri, 3 Mar 2017 15:47:19 +0900
Subject: [PATCH] Remove NamedLWLockTrancheArray

This patch fixes two pattern of server crash of postgresql compled
with --with-dtarce.

One occurs when an extension forgets to call LWLockRegisterTranche for
the tranche id given by LWLockNewTrancheId. For the case, trace point
code of lwlocks looks up to LWLockTrancheArray for tranche names but
it can be outside of valid(mapped) memory. This seems quite common
mistake but hardly detectable by ordinary tests. Range check of
tranche_id in LWLockInitialize can detect the case.

The other occurs when an extension uses RequestNamedfLWLockTranche and
GetNamedLWLockTrasnche. In this case the tranches are stored in
NamedLWLockTrancheArray separatley from LWLockTrancheArray. This also
may cause a crash when a tracepoint is performed. This patch merges
all tranches into LWLockTrancheArray and remove NameLWLockTrancheArray.
---
 src/backend/storage/lmgr/lwlock.c | 89 ++++++++++++++++++++++-----------------
 src/include/storage/lwlock.h      |  8 ----
 2 files changed, 51 insertions(+), 46 deletions(-)

diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index ab81d94..4d2a1fc 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -153,8 +153,6 @@ NamedLWLockTrancheRequest *NamedLWLockTrancheRequestArray = NULL;
 static int	NamedLWLockTrancheRequestsAllocated = 0;
 int			NamedLWLockTrancheRequests = 0;
 
-NamedLWLockTranche *NamedLWLockTrancheArray = NULL;
-
 static bool lock_named_request_allowed = true;
 
 static void InitializeLWLocks(void);
@@ -360,9 +358,6 @@ LWLockShmemSize(void)
 	/* Space for dynamic allocation counter, plus room for alignment. */
 	size = add_size(size, sizeof(int) + LWLOCK_PADDED_SIZE);
 
-	/* space for named tranches. */
-	size = add_size(size, mul_size(NamedLWLockTrancheRequests, sizeof(NamedLWLockTranche)));
-
 	/* space for name of each tranche. */
 	for (i = 0; i < NamedLWLockTrancheRequests; i++)
 		size = add_size(size, strlen(NamedLWLockTrancheRequestArray[i].tranche_name) + 1);
@@ -410,13 +405,16 @@ CreateLWLocks(void)
 		 */
 		LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 		*LWLockCounter = LWTRANCHE_FIRST_USER_DEFINED;
-
-		/* Initialize all LWLocks */
-		InitializeLWLocks();
 	}
 
-	/* Register all LWLock tranches */
+	/* Register all LWLock tranches on local memory */
 	RegisterLWLockTranches();
+
+	/* Initialize all LWLocks */
+	if (!IsUnderPostmaster)
+		InitializeLWLocks();
+
+
 }
 
 /*
@@ -425,7 +423,6 @@ CreateLWLocks(void)
 static void
 InitializeLWLocks(void)
 {
-	int			numNamedLocks = NumLWLocksByNamedTranches();
 	int			id;
 	int			i;
 	int			j;
@@ -451,35 +448,22 @@ InitializeLWLocks(void)
 	for (id = 0; id < NUM_PREDICATELOCK_PARTITIONS; id++, lock++)
 		LWLockInitialize(&lock->lock, LWTRANCHE_PREDICATE_LOCK_MANAGER);
 
-	/* Initialize named tranches. */
+	/*
+	 * Initialize LWLokcs in user tranches defined using
+	 * RequestNamedLWLockTranche
+	 */
 	if (NamedLWLockTrancheRequests > 0)
 	{
-		char	   *trancheNames;
+		LWLockPadded   *lock = &MainLWLockArray[NUM_FIXED_LWLOCKS];
 
-		NamedLWLockTrancheArray = (NamedLWLockTranche *)
-			&MainLWLockArray[NUM_FIXED_LWLOCKS + numNamedLocks];
-
-		trancheNames = (char *) NamedLWLockTrancheArray +
-			(NamedLWLockTrancheRequests * sizeof(NamedLWLockTranche));
-		lock = &MainLWLockArray[NUM_FIXED_LWLOCKS];
-
-		for (i = 0; i < NamedLWLockTrancheRequests; i++)
+		for (i = 0 ; i < NamedLWLockTrancheRequests ; i++)
 		{
-			NamedLWLockTrancheRequest *request;
-			NamedLWLockTranche *tranche;
-			char	   *name;
+			NamedLWLockTrancheRequest *request =
+				&NamedLWLockTrancheRequestArray[i];
+			int tranche_id = LWTRANCHE_FIRST_USER_DEFINED + i;
 
-			request = &NamedLWLockTrancheRequestArray[i];
-			tranche = &NamedLWLockTrancheArray[i];
-
-			name = trancheNames;
-			trancheNames += strlen(request->tranche_name) + 1;
-			strcpy(name, request->tranche_name);
-			tranche->trancheId = LWLockNewTrancheId();
-			tranche->trancheName = name;
-
-			for (j = 0; j < request->num_lwlocks; j++, lock++)
-				LWLockInitialize(&lock->lock, tranche->trancheId);
+			for (j = 0 ; j < request->num_lwlocks ; j++, lock++)
+				LWLockInitialize(&lock->lock, tranche_id);
 		}
 	}
 }
@@ -495,6 +479,10 @@ RegisterLWLockTranches(void)
 	if (LWLockTrancheArray == NULL)
 	{
 		LWLockTranchesAllocated = 64;
+		while(LWTRANCHE_FIRST_USER_DEFINED + NamedLWLockTrancheRequests
+			  > LWLockTranchesAllocated)
+			LWLockTranchesAllocated *= 2;
+
 		LWLockTrancheArray = (char **)
 			MemoryContextAllocZero(TopMemoryContext,
 						  LWLockTranchesAllocated * sizeof(char *));
@@ -511,10 +499,29 @@ RegisterLWLockTranches(void)
 	LWLockRegisterTranche(LWTRANCHE_PARALLEL_QUERY_DSA,
 						  "parallel_query_dsa");
 
-	/* Register named tranches. */
-	for (i = 0; i < NamedLWLockTrancheRequests; i++)
-		LWLockRegisterTranche(NamedLWLockTrancheArray[i].trancheId,
-							  NamedLWLockTrancheArray[i].trancheName);
+	if (NamedLWLockTrancheRequests > 0)
+	{
+		char   *trancheNames =
+			(char *) &MainLWLockArray[NUM_FIXED_LWLOCKS +
+									  NumLWLocksByNamedTranches()];
+
+		for (i = 0; i < NamedLWLockTrancheRequests; i++)
+		{
+			char	   *name;
+			int			tranche_id;
+			NamedLWLockTrancheRequest *request;
+
+			tranche_id = LWLockNewTrancheId();
+			request = &NamedLWLockTrancheRequestArray[i];
+
+			/* Copy the name in request to shared memory */
+			name = trancheNames;
+			trancheNames += strlen(request->tranche_name) + 1;
+			strcpy(name, request->tranche_name);
+
+			LWLockRegisterTranche(tranche_id, name);
+		}
+	}
 }
 
 /*
@@ -665,6 +672,12 @@ RequestNamedLWLockTranche(const char *tranche_name, int num_lwlocks)
 void
 LWLockInitialize(LWLock *lock, int tranche_id)
 {
+	if (tranche_id >= LWTRANCHE_FIRST_USER_DEFINED &&
+		tranche_id >= LWLockTranchesAllocated)
+		ereport(ERROR,
+				(errmsg ("tranche for this lock not available"),
+				 errdetail("LWLockRegisterTranche for this tranche required")));
+
 	pg_atomic_init_u32(&lock->state, LW_FLAG_RELEASE_OK);
 #ifdef LOCK_DEBUG
 	pg_atomic_init_u32(&lock->nwaiters, 0);
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index 8bd93c3..4645414 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -90,14 +90,6 @@ typedef union LWLockMinimallyPadded
 extern PGDLLIMPORT LWLockPadded *MainLWLockArray;
 extern char *MainLWLockNames[];
 
-/* struct for storing named tranche information */
-typedef struct NamedLWLockTranche
-{
-	int			trancheId;
-	char	   *trancheName;
-} NamedLWLockTranche;
-
-extern PGDLLIMPORT NamedLWLockTranche *NamedLWLockTrancheArray;
 extern PGDLLIMPORT int NamedLWLockTrancheRequests;
 
 /* Names for fixed lwlocks */
-- 
2.9.2

-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to