From 5d77298e88ebdd0ba5c20be2191cc67eff444c0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Fri, 23 Jun 2023 12:16:35 +0300 Subject: [PATCH] avoid UB in bit shifts unsigned char* gets promoted to `int`, which cannot always be shifted by 24 bits. Justine Tunney blogs about it here: https://justine.lol/endian.html Example: ```deserialize.c #include #include #include uint32_t file_deserialize_uint32_ok(unsigned char *buf) { return ((uint32_t)buf[0] << 24) | ((uint32_t)buf[1] << 16) | ((uint32_t)buf[2] << 8) | (uint32_t)buf[3]; } uint32_t file_deserialize_uint32(unsigned char *buf) { return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; } int main() { unsigned char arr[4] = {0xaa, 0xaa, 0xaa, 0xaa}; printf("%d\n", file_deserialize_uint32_ok((unsigned char*)arr)); printf("%d\n", file_deserialize_uint32((unsigned char*)arr)); } ``` Output: ``` $ clang-16 -fsanitize=undefined ./deserialize.c -o deserialize && ./deserialize -1431655766 deserialize.c:10:20: runtime error: left shift of 170 by 24 places cannot be represented in type 'int' SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior deserialize.c:10:20 in -1431655766 ``` --- src/file_utils.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/file_utils.c b/src/file_utils.c index f25e5ee6..5fc2dfbe 100644 --- a/src/file_utils.c +++ b/src/file_utils.c @@ -198,7 +198,7 @@ bool file_write_float(FILE *file, float value) { } inline uint32_t file_deserialize_uint32(unsigned char *buf) { - return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; + return ((uint32_t)buf[0] << 24) | ((uint32_t)buf[1] << 16) | ((uint32_t)buf[2] << 8) | (uint32_t)buf[3]; } bool file_read_uint32(FILE *file, uint32_t *value) { @@ -243,7 +243,7 @@ bool file_write_uint32(FILE *file, uint32_t value) { inline uint16_t file_deserialize_uint16(unsigned char *buf) { - return (buf[0] << 8) | buf[1]; + return ((uint16_t)buf[0] << 8) | buf[1]; }