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 <stdlib.h>
#include <stdio.h>
#include <stdint.h>

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
```
This commit is contained in:
Motiejus Jakštys
2023-06-23 12:16:35 +03:00
committed by GitHub
parent 4c98eaa7dc
commit 5d77298e88

View File

@@ -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];
}