Fix bogus casting in BlockIdGetBlockNumber().
authorTom Lane
Fri, 4 Mar 2022 00:03:17 +0000 (19:03 -0500)
committerTom Lane
Fri, 4 Mar 2022 00:03:35 +0000 (19:03 -0500)
This macro cast the result to BlockNumber after shifting, not before,
which is the wrong thing.  Per the C spec, the uint16 fields would
promote to int not unsigned int, so that (for 32-bit int) the shift
potentially shifts a nonzero bit into the sign position.  I doubt
there are any production systems where this would actually end with
the wrong answer, but it is undefined behavior per the C spec, and
clang's -fsanitize=undefined option reputedly warns about it on some
platforms.  (I can't reproduce that right now, but the code is
undeniably wrong per spec.)  It's easy to fix by casting to
BlockNumber (uint32) in the proper places.

It's been wrong for ages, so back-patch to all supported branches.

Report and patch by Zhihong Yu (cosmetic tweaking by me)

Discussion: https://postgr.es/m/CALNJ-vT9r0DSsAOw9OXVJFxLENoVS_68kJ5x0p44atoYH+H4dg@mail.gmail.com

src/include/storage/block.h

index 4a5c476d2d0a0d380b5681e98d6a3e0fe731bae7..e7d86e5fe60d143fc5dceb78b1a2f90cefa08e09 100644 (file)
@@ -115,7 +115,7 @@ typedef BlockIdData *BlockId;   /* block identifier */
 #define BlockIdGetBlockNumber(blockId) \
 ( \
    AssertMacro(BlockIdIsValid(blockId)), \
-   (BlockNumber) (((blockId)->bi_hi << 16) | ((uint16) (blockId)->bi_lo)) \
+   ((((BlockNumber) (blockId)->bi_hi) << 16) | ((BlockNumber) (blockId)->bi_lo)) \
 )
 
 #endif                         /* BLOCK_H */