From eb21049b4b904b072679ece60e73c6b0dc0d1ebf Mon Sep 17 00:00:00 2001 From: Alexander Richardson Date: Wed, 11 Oct 2023 11:46:09 -0700 Subject: [PATCH] [libunwind] Avoid reading OOB for non-existent .eh_frame_hdr (#68815) I was running the tests with baremetal picolibc which has a linker script that __eh_frame_start==__eh_frame_end (not equal to zero) in case there is no .eh_frame_hdr. I noticed that libunwind was trying to read nonsense data because it was printing messages such as `libunwind: unsupported .eh_frame_hdr version: 20 at https://github.com/llvm/llvm-project/commit/8000d308146ebf49cb364cb600e28a0a42e22c83` This change adds a ehHdr size check to avoid reading this out-of-bounds data and potentially crashing. --- libunwind/src/EHHeaderParser.hpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/libunwind/src/EHHeaderParser.hpp b/libunwind/src/EHHeaderParser.hpp index ed4317c89055..0662a1321e2c 100644 --- a/libunwind/src/EHHeaderParser.hpp +++ b/libunwind/src/EHHeaderParser.hpp @@ -55,6 +55,19 @@ template bool EHHeaderParser::decodeEHHdr(A &addressSpace, pint_t ehHdrStart, pint_t ehHdrEnd, EHHeaderInfo &ehHdrInfo) { pint_t p = ehHdrStart; + + // Ensure that we don't read data beyond the end of .eh_frame_hdr + if (ehHdrEnd - ehHdrStart < 4) { + // Don't print a message for an empty .eh_frame_hdr (this can happen if + // the linker script defines symbols for it even in the empty case). + if (ehHdrEnd == ehHdrStart) + return false; + _LIBUNWIND_LOG("unsupported .eh_frame_hdr at %" PRIx64 + ": need at least 4 bytes of data but only got %zd", + static_cast(ehHdrStart), + static_cast(ehHdrEnd - ehHdrStart)); + return false; + } uint8_t version = addressSpace.get8(p++); if (version != 1) { _LIBUNWIND_LOG("unsupported .eh_frame_hdr version: %" PRIu8 " at %" PRIx64,