In unwind_protect_mem_internal, we must make sure to allocate at least a full UNWIND_ELT, even if the required size for desired_setting is less than the remaining padding in UNWIND_ELT. Otherwise when we come to memset it with 0xdf in unwind_frame_discard_internal we will overflow the allocation.
On existing 32-bit and 64-bit architectures, this padding happens to be only 4 bytes, and no users of this function call it with a size smaller than 4 (unwind_protect_short and unwind_protect_string are both currently unused). However, we should not rely on this as this could change in future. Moreover on CHERI-RISC-V, pointers are replaced with capabilities, 16-byte fat pointers, and the padding now ends up being 12 bytes, violating this assumption, but also trapping on this detected buffer overflow by virtue of its fine-grained bounds. --- unwind_prot.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/unwind_prot.c b/unwind_prot.c index c9196dc1..37429924 100644 --- a/unwind_prot.c +++ b/unwind_prot.c @@ -349,6 +349,8 @@ unwind_protect_mem_internal (var, psize) size = *(int *) psize; allocated = size + offsetof (UNWIND_ELT, sv.v.desired_setting[0]); + if (allocated < sizeof (UNWIND_ELT)) + allocated = sizeof (UNWIND_ELT); elt = (UNWIND_ELT *)xmalloc (allocated); elt->head.next = unwind_protect_list; elt->head.cleanup = (Function *) restore_variable; -- 2.20.1