Avoid undefined left shift of negatives
This commit is contained in:
parent
311b9cb0e0
commit
7056638935
1 changed files with 33 additions and 1 deletions
34
lib/capn.c
34
lib/capn.c
|
|
@ -481,7 +481,36 @@ err:
|
|||
}
|
||||
|
||||
static void write_ptr_tag(char *d, capn_ptr p, int off) {
|
||||
uint64_t val = U64(U32(I32(off/8) << 2));
|
||||
/*
|
||||
lsb struct pointer msb
|
||||
+-+-----------------------------+---------------+---------------+
|
||||
|A| B | C | D |
|
||||
+-+-----------------------------+---------------+---------------+
|
||||
|
||||
A (2 bits) = 0, to indicate that this is a struct pointer.
|
||||
B (30 bits) = Offset, in words, from the end of the pointer to the
|
||||
start of the struct's data section. Signed.
|
||||
C (16 bits) = Size of the struct's data section, in words.
|
||||
D (16 bits) = Size of the struct's pointer section, in words.
|
||||
|
||||
For B we can't simply left-shift by 2 bits since C11 6.5.7.4
|
||||
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
|
||||
says we can get undefined behavior when the left-shift exceeds
|
||||
the signed integer (ie. values run into the sign bit). The
|
||||
ASAN detector will rightly complain. So we do two's complement
|
||||
manually, and check bounds, to stay within unsigned arithmetic.
|
||||
*/
|
||||
const int off_words = off / 8;
|
||||
uint64_t val;
|
||||
if (off_words < 0) {
|
||||
if (off_words < -(2147483647 >> 2) - 1) {
|
||||
goto err;
|
||||
}
|
||||
uint32_t twos = 1 + ~(U32(-off_words) << 2);
|
||||
val = U64(twos);
|
||||
} else {
|
||||
val = U64(U32(off_words) << 2);
|
||||
}
|
||||
|
||||
switch (p.type) {
|
||||
case CAPN_STRUCT:
|
||||
|
|
@ -528,6 +557,9 @@ static void write_ptr_tag(char *d, capn_ptr p, int off) {
|
|||
}
|
||||
|
||||
*(uint64_t*) d = capn_flip64(val);
|
||||
|
||||
err:
|
||||
memset(&p, 0, sizeof(p));
|
||||
}
|
||||
|
||||
static void write_far_ptr(char *d, struct capn_segment *s, char *tgt) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue