I've been poking around in the PHJ code trying to identify the reason
why there are still so many buildfarm failures.  I've not nailed it
down yet, but one thing I did notice is that there's an entirely
undocumented assumption that offsetof(HashMemoryChunkData, data) is
maxalign'ed.  If it isn't, the allocation code will give back non-
maxaligned pointers, which will appear to work as long as you only
test on alignment-lax processors.

Now, the existing definition of the struct seems safe on all
architectures we support, but it would not take much to break it.
I think we ought to do what we did recently in the memory-context
code: insert an explicit padding calculation and a static assertion
that it's right.  Hence, the attached proposed patch (in which
I also failed to resist the temptation to make the two code paths
in dense_alloc() look more alike).  Any objections?

                        regards, tom lane

diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 4e1a280..6aad152 100644
*** a/src/backend/executor/nodeHash.c
--- b/src/backend/executor/nodeHash.c
*************** dense_alloc(HashJoinTable hashtable, Siz
*** 2651,2667 ****
  	size = MAXALIGN(size);
  
  	/*
! 	 * If tuple size is larger than of 1/4 of chunk size, allocate a separate
! 	 * chunk.
  	 */
  	if (size > HASH_CHUNK_THRESHOLD)
  	{
  		/* allocate new chunk and put it at the beginning of the list */
  		newChunk = (HashMemoryChunk) MemoryContextAlloc(hashtable->batchCxt,
! 														offsetof(HashMemoryChunkData, data) + size);
  		newChunk->maxlen = size;
! 		newChunk->used = 0;
! 		newChunk->ntuples = 0;
  
  		/*
  		 * Add this chunk to the list after the first existing chunk, so that
--- 2651,2673 ----
  	size = MAXALIGN(size);
  
  	/*
! 	 * If the data[] field of HashMemoryChunkData has a non-maxaligned offset,
! 	 * we'll return non-maxaligned allocations, which is bad.
! 	 */
! 	StaticAssertStmt(HASH_CHUNK_HEADER_SIZE == MAXALIGN(HASH_CHUNK_HEADER_SIZE),
! 					 "sizeof(HashMemoryChunkData) is not maxaligned");
! 
! 	/*
! 	 * If tuple size is larger than threshold, allocate a separate chunk.
  	 */
  	if (size > HASH_CHUNK_THRESHOLD)
  	{
  		/* allocate new chunk and put it at the beginning of the list */
  		newChunk = (HashMemoryChunk) MemoryContextAlloc(hashtable->batchCxt,
! 														HASH_CHUNK_HEADER_SIZE + size);
  		newChunk->maxlen = size;
! 		newChunk->used = size;
! 		newChunk->ntuples = 1;
  
  		/*
  		 * Add this chunk to the list after the first existing chunk, so that
*************** dense_alloc(HashJoinTable hashtable, Siz
*** 2678,2686 ****
  			hashtable->chunks = newChunk;
  		}
  
- 		newChunk->used += size;
- 		newChunk->ntuples += 1;
- 
  		return newChunk->data;
  	}
  
--- 2684,2689 ----
*************** dense_alloc(HashJoinTable hashtable, Siz
*** 2693,2699 ****
  	{
  		/* allocate new chunk and put it at the beginning of the list */
  		newChunk = (HashMemoryChunk) MemoryContextAlloc(hashtable->batchCxt,
! 														offsetof(HashMemoryChunkData, data) + HASH_CHUNK_SIZE);
  
  		newChunk->maxlen = HASH_CHUNK_SIZE;
  		newChunk->used = size;
--- 2696,2702 ----
  	{
  		/* allocate new chunk and put it at the beginning of the list */
  		newChunk = (HashMemoryChunk) MemoryContextAlloc(hashtable->batchCxt,
! 														HASH_CHUNK_HEADER_SIZE + HASH_CHUNK_SIZE);
  
  		newChunk->maxlen = HASH_CHUNK_SIZE;
  		newChunk->used = size;
diff --git a/src/include/executor/hashjoin.h b/src/include/executor/hashjoin.h
index d8c82d4..cb91f79 100644
*** a/src/include/executor/hashjoin.h
--- b/src/include/executor/hashjoin.h
*************** typedef struct HashMemoryChunkData
*** 127,139 ****
  		dsa_pointer shared;
  	}			next;
  
  	char		data[FLEXIBLE_ARRAY_MEMBER];	/* buffer allocated at the end */
  }			HashMemoryChunkData;
  
  typedef struct HashMemoryChunkData *HashMemoryChunk;
  
  #define HASH_CHUNK_SIZE			(32 * 1024L)
! #define HASH_CHUNK_HEADER_SIZE (offsetof(HashMemoryChunkData, data))
  #define HASH_CHUNK_THRESHOLD	(HASH_CHUNK_SIZE / 4)
  
  /*
--- 127,152 ----
  		dsa_pointer shared;
  	}			next;
  
+ 	/*
+ 	 * It's required that data[] start at a maxaligned offset.  This padding
+ 	 * calculation is correct as long as int is not wider than size_t and
+ 	 * dsa_pointer is not wider than a regular pointer, but there's a static
+ 	 * assertion to check things in nodeHash.c.
+ 	 */
+ #define HASHMEMORYCHUNKDATA_RAWSIZE (SIZEOF_SIZE_T * 3 + SIZEOF_VOID_P)
+ 
+ 	/* ensure proper alignment by adding padding if needed */
+ #if (HASHMEMORYCHUNKDATA_RAWSIZE % MAXIMUM_ALIGNOF) != 0
+ 	char		padding[MAXIMUM_ALIGNOF - HASHMEMORYCHUNKDATA_RAWSIZE % MAXIMUM_ALIGNOF];
+ #endif
+ 
  	char		data[FLEXIBLE_ARRAY_MEMBER];	/* buffer allocated at the end */
  }			HashMemoryChunkData;
  
  typedef struct HashMemoryChunkData *HashMemoryChunk;
  
  #define HASH_CHUNK_SIZE			(32 * 1024L)
! #define HASH_CHUNK_HEADER_SIZE	offsetof(HashMemoryChunkData, data)
  #define HASH_CHUNK_THRESHOLD	(HASH_CHUNK_SIZE / 4)
  
  /*

Reply via email to