Avoid undefined behaviour after realloc()

Adding the offset between the realloc result and the old allocation to
update pointers into the new allocation is undefined behaviour: the
old pointers are no longer valid after realloc() according to the C
standard. While this works on almost all architectures and compilers,
it causes  problems on architectures that track pointer bounds (e.g.
CHERI or Arm's Morello): the value_list pointers will still have the
bounds of the previous allocation and therefore any dereference will
result in a run-time trap.

I found this due to a crash (dereferencing an invalid capability) while
trying to run `xev` over SSH on a CHERI-RISC-V system. With these two
realloc changes, and https://gitlab.freedesktop.org/xorg/proto/xorgproto/-/merge_requests/41
I am able to succesfully run `xev` compiled for CHERI-RISC-V.

Signed-off-by: Alex Richardson <Alexander.Richardson@cl.cam.ac.uk>
This commit is contained in:
Alex Richardson 2021-06-16 12:17:04 +01:00
parent 1c845834a3
commit d01d233741
2 changed files with 8 additions and 4 deletions

View file

@ -676,8 +676,10 @@ parseline(
goto error;
b->tree = new;
b->treesize = newsize;
/* Re-derive top after realloc() to avoid undefined behaviour
(and crashes on architectures that track pointer bounds). */
if (top >= (DTIndex *) old && top < (DTIndex *) &old[oldsize])
top = (DTIndex *) (((char *) top) + (((char *)b->tree)-(char *)old));
top = (DTIndex *) (((char *)new) + (((char *)top)-(char *)old));
}
p = &b->tree[b->treeused];
p->keysym = buf[i].keysym;

View file

@ -517,11 +517,13 @@ append_value_list (void)
}
if (value != *value_list) {
int i;
ssize_t delta;
delta = value - *value_list;
char *old_list;
old_list = *value_list;
*value_list = value;
/* Re-derive pointers from the new realloc() result to avoid undefined
behaviour (and crashes on architectures with pointer bounds). */
for (i = 1; i < value_num; ++i) {
value_list[i] += delta;
value_list[i] = value + (value_list[i] - old_list);
}
}