Skip to content

Commit 8c62d9d

Browse files
committed
Make checksum_impl.h safe to compile with -fstrict-aliasing.
In general, Postgres requires -fno-strict-aliasing with compilers that implement C99 strict aliasing rules. There's little hope of getting rid of that overall. But it seems like it would be a good idea if storage/checksum_impl.h in particular didn't depend on it, because that header is explicitly intended to be included by external programs. We don't have a lot of control over the compiler switches that an external program might use, as shown by Michael Banck's report of failure in a privately-modified version of pg_verify_checksums. Hence, switch to using a union in place of willy-nilly pointer casting inside this file. I think this makes the code a bit more readable anyway. checksum_impl.h hasn't changed since it was introduced in 9.3, so back-patch all the way. Discussion: https://postgr.es/m/1535618100.1286.3.camel@credativ.de
1 parent 7cfdc77 commit 8c62d9d

File tree

1 file changed

+23
-15
lines changed

1 file changed

+23
-15
lines changed

src/include/storage/checksum_impl.h

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,13 @@
107107
/* prime multiplier of FNV-1a hash */
108108
#define FNV_PRIME 16777619
109109

110+
/* Use a union so that this code is valid under strict aliasing */
111+
typedef union
112+
{
113+
PageHeaderData phdr;
114+
uint32 data[BLCKSZ / (sizeof(uint32) * N_SUMS)][N_SUMS];
115+
} PGChecksummablePage;
116+
110117
/*
111118
* Base offsets to initialize each of the parallel FNV hashes into a
112119
* different initial state.
@@ -132,28 +139,27 @@ do { \
132139
} while (0)
133140

134141
/*
135-
* Block checksum algorithm. The data argument must be aligned on a 4-byte
136-
* boundary.
142+
* Block checksum algorithm. The page must be adequately aligned
143+
* (at least on 4-byte boundary).
137144
*/
138145
static uint32
139-
pg_checksum_block(char *data, uint32 size)
146+
pg_checksum_block(const PGChecksummablePage *page)
140147
{
141148
uint32 sums[N_SUMS];
142-
uint32 (*dataArr)[N_SUMS] = (uint32 (*)[N_SUMS]) data;
143149
uint32 result = 0;
144150
uint32 i,
145151
j;
146152

147153
/* ensure that the size is compatible with the algorithm */
148-
Assert((size % (sizeof(uint32) * N_SUMS)) == 0);
154+
Assert(sizeof(PGChecksummablePage) == BLCKSZ);
149155

150156
/* initialize partial checksums to their corresponding offsets */
151157
memcpy(sums, checksumBaseOffsets, sizeof(checksumBaseOffsets));
152158

153159
/* main checksum calculation */
154-
for (i = 0; i < size / sizeof(uint32) / N_SUMS; i++)
160+
for (i = 0; i < (uint32) (BLCKSZ / (sizeof(uint32) * N_SUMS)); i++)
155161
for (j = 0; j < N_SUMS; j++)
156-
CHECKSUM_COMP(sums[j], dataArr[i][j]);
162+
CHECKSUM_COMP(sums[j], page->data[i][j]);
157163

158164
/* finally add in two rounds of zeroes for additional mixing */
159165
for (i = 0; i < 2; i++)
@@ -168,8 +174,10 @@ pg_checksum_block(char *data, uint32 size)
168174
}
169175

170176
/*
171-
* Compute the checksum for a Postgres page. The page must be aligned on a
172-
* 4-byte boundary.
177+
* Compute the checksum for a Postgres page.
178+
*
179+
* The page must be adequately aligned (at least on a 4-byte boundary).
180+
* Beware also that the checksum field of the page is transiently zeroed.
173181
*
174182
* The checksum includes the block number (to detect the case where a page is
175183
* somehow moved to a different location), the page header (excluding the
@@ -178,23 +186,23 @@ pg_checksum_block(char *data, uint32 size)
178186
uint16
179187
pg_checksum_page(char *page, BlockNumber blkno)
180188
{
181-
PageHeader phdr = (PageHeader) page;
189+
PGChecksummablePage *cpage = (PGChecksummablePage *) page;
182190
uint16 save_checksum;
183191
uint32 checksum;
184192

185193
/* We only calculate the checksum for properly-initialized pages */
186-
Assert(!PageIsNew(page));
194+
Assert(!PageIsNew(&cpage->phdr));
187195

188196
/*
189197
* Save pd_checksum and temporarily set it to zero, so that the checksum
190198
* calculation isn't affected by the old checksum stored on the page.
191199
* Restore it after, because actually updating the checksum is NOT part of
192200
* the API of this function.
193201
*/
194-
save_checksum = phdr->pd_checksum;
195-
phdr->pd_checksum = 0;
196-
checksum = pg_checksum_block(page, BLCKSZ);
197-
phdr->pd_checksum = save_checksum;
202+
save_checksum = cpage->phdr.pd_checksum;
203+
cpage->phdr.pd_checksum = 0;
204+
checksum = pg_checksum_block(cpage);
205+
cpage->phdr.pd_checksum = save_checksum;
198206

199207
/* Mix in the block number to detect transposed pages */
200208
checksum ^= blkno;

0 commit comments

Comments
 (0)