Skip to content

[libunwind] Avoid reading OOB for non-existent .eh_frame_hdr #68815

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 11, 2023

Conversation

arichardson
Copy link
Member

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 ehHdrSize check to avoid reading this out-of-bounds
data and potentially crashing.

Depends on #68813 which I've included as the first commit here (not sure how to sensibly do stacked PRs).

@arichardson arichardson requested a review from compnerd October 11, 2023 15:54
@arichardson arichardson requested a review from a team as a code owner October 11, 2023 15:54
@llvmbot
Copy link
Member

llvmbot commented Oct 11, 2023

@llvm/pr-subscribers-libunwind

Author: Alexander Richardson (arichardson)

Changes

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 ehHdrSize check to avoid reading this out-of-bounds
data and potentially crashing.

Depends on #68813 which I've included as the first commit here (not sure how to sensibly do stacked PRs).


Full diff: https://github.com/llvm/llvm-project/pull/68815.diff

1 Files Affected:

  • (modified) libunwind/src/EHHeaderParser.hpp (+16-3)
diff --git a/libunwind/src/EHHeaderParser.hpp b/libunwind/src/EHHeaderParser.hpp
index ed4317c89055c9e..8d37594e7b9be89 100644
--- a/libunwind/src/EHHeaderParser.hpp
+++ b/libunwind/src/EHHeaderParser.hpp
@@ -35,7 +35,7 @@ template <typename A> class EHHeaderParser {
     uint8_t table_enc;
   };
 
-  static bool decodeEHHdr(A &addressSpace, pint_t ehHdrStart, pint_t ehHdrEnd,
+  static bool decodeEHHdr(A &addressSpace, pint_t ehHdrStart, size_t ehHdrSize,
                           EHHeaderInfo &ehHdrInfo);
   static bool findFDE(A &addressSpace, pint_t pc, pint_t ehHdrStart,
                       uint32_t sectionLength,
@@ -53,8 +53,21 @@ template <typename A> class EHHeaderParser {
 
 template <typename A>
 bool EHHeaderParser<A>::decodeEHHdr(A &addressSpace, pint_t ehHdrStart,
-                                    pint_t ehHdrEnd, EHHeaderInfo &ehHdrInfo) {
+                                    size_t ehHdrSize, EHHeaderInfo &ehHdrInfo) {
   pint_t p = ehHdrStart;
+  pint_t ehHdrEnd = ehHdrStart + ehHdrSize;
+
+  // Ensure that we don't read data beyond the end of .eh_frame_hdr
+  if (ehHdrSize < 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 (ehHdrSize == 0)
+      return false;
+    _LIBUNWIND_LOG("unsupported .eh_frame_hdr at %" PRIx64
+                   ": need at least 4 bytes of data but only got %zd",
+                   static_cast<uint64_t>(ehHdrStart), ehHdrSize);
+    return false;
+  }
   uint8_t version = addressSpace.get8(p++);
   if (version != 1) {
     _LIBUNWIND_LOG("unsupported .eh_frame_hdr version: %" PRIu8 " at %" PRIx64,
@@ -106,7 +119,7 @@ bool EHHeaderParser<A>::findFDE(A &addressSpace, pint_t pc, pint_t ehHdrStart,
   pint_t ehHdrEnd = ehHdrStart + sectionLength;
 
   EHHeaderParser<A>::EHHeaderInfo hdrInfo;
-  if (!EHHeaderParser<A>::decodeEHHdr(addressSpace, ehHdrStart, ehHdrEnd,
+  if (!EHHeaderParser<A>::decodeEHHdr(addressSpace, ehHdrStart, sectionLength,
                                       hdrInfo))
     return false;
 

Copy link
Member

@compnerd compnerd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@arichardson arichardson force-pushed the libunwind-avoid-oob-read branch from 0190d73 to 6341524 Compare October 11, 2023 18:39
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 8000d308`

This change adds a ehHdrSize check to avoid reading this out-of-bounds
data and potentially crashing.
@arichardson arichardson force-pushed the libunwind-avoid-oob-read branch from 6341524 to 5d4e2bc Compare October 11, 2023 18:41
@arichardson arichardson merged commit eb21049 into llvm:main Oct 11, 2023
@arichardson arichardson deleted the libunwind-avoid-oob-read branch October 11, 2023 18:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants