[Lldb-commits] [lldb] ee0c382 - [lldb][AArch64] Add MakeTaggedRanges to MemoryTagManager

David Spickett via lldb-commits lldb-commits at lists.llvm.org
Wed Jan 26 03:30:15 PST 2022


Author: David Spickett
Date: 2022-01-26T11:30:10Z
New Revision: ee0c3820f8862aaf39d7ad542672092c35dda266

URL: https://github.com/llvm/llvm-project/commit/ee0c3820f8862aaf39d7ad542672092c35dda266
DIFF: https://github.com/llvm/llvm-project/commit/ee0c3820f8862aaf39d7ad542672092c35dda266.diff

LOG: [lldb][AArch64] Add MakeTaggedRanges to MemoryTagManager

This is to be used when you want to know what subranges
of a larger range have memory tagging. Like MakeTaggedRange
but memory without tags is skipped and you get a list of ranges back.

Will be used later by DumpDataExtractor to show memory tags.

MakeTaggedRanges assumes that the memory regions it is
given are sorted in ascending order and do not overlap.
For the current use case where you get regions from
GetMemoryRegions and are on some Linux like OS, this is
reasonable to assume.

I've used asserts to check those conditions. In future
any API binding will check them up front to prevent a crash.

Reviewed By: omjavaid

Differential Revision: https://reviews.llvm.org/D112824

Added: 
    

Modified: 
    lldb/include/lldb/Target/MemoryTagManager.h
    lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp
    lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h
    lldb/unittests/Process/Utility/MemoryTagManagerAArch64MTETest.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/Target/MemoryTagManager.h b/lldb/include/lldb/Target/MemoryTagManager.h
index ed4d04f712ab..859c43cb437a 100644
--- a/lldb/include/lldb/Target/MemoryTagManager.h
+++ b/lldb/include/lldb/Target/MemoryTagManager.h
@@ -64,7 +64,7 @@ class MemoryTagManager {
   //   (which may include one or more memory regions)
   //
   // If so, return a modified range which will have been expanded
-  // to be granule aligned.
+  // to be granule aligned. Otherwise return an error.
   //
   // Tags in the input addresses are ignored and not present
   // in the returned range.
@@ -72,6 +72,23 @@ class MemoryTagManager {
       lldb::addr_t addr, lldb::addr_t end_addr,
       const lldb_private::MemoryRegionInfos &memory_regions) const = 0;
 
+  // Given a range addr to end_addr, check that end_addr >= addr.
+  // If it is not, return an error saying so.
+  // Otherwise, granule align it and return a set of ranges representing
+  // subsections of the aligned range that have memory tagging enabled.
+  //
+  // Basically a sparse version of MakeTaggedRange. Use this when you
+  // want to know which parts of a larger range have memory tagging.
+  //
+  // Regions in memory_regions should be sorted in ascending order and
+  // not overlap. (use Process GetMemoryRegions)
+  //
+  // Tags in the input addresses are ignored and not present
+  // in the returned ranges.
+  virtual llvm::Expected<std::vector<TagRange>> MakeTaggedRanges(
+      lldb::addr_t addr, lldb::addr_t end_addr,
+      const lldb_private::MemoryRegionInfos &memory_regions) const = 0;
+
   // Return the type value to use in GDB protocol qMemTags packets to read
   // allocation tags. This is named "Allocation" specifically because the spec
   // allows for logical tags to be read the same way, though we do not use that.

diff  --git a/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp b/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp
index fba23401c88f..b71de4cadb18 100644
--- a/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp
+++ b/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp
@@ -7,6 +7,8 @@
 //===----------------------------------------------------------------------===//
 
 #include "MemoryTagManagerAArch64MTE.h"
+#include "llvm/Support/Error.h"
+#include <assert.h>
 
 using namespace lldb_private;
 
@@ -66,6 +68,15 @@ MemoryTagManagerAArch64MTE::ExpandToGranule(TagRange range) const {
   return TagRange(new_start, new_len);
 }
 
+static llvm::Error MakeInvalidRangeErr(lldb::addr_t addr,
+                                       lldb::addr_t end_addr) {
+  return llvm::createStringError(
+      llvm::inconvertibleErrorCode(),
+      "End address (0x%" PRIx64
+      ") must be greater than the start address (0x%" PRIx64 ")",
+      end_addr, addr);
+}
+
 llvm::Expected<MemoryTagManager::TagRange>
 MemoryTagManagerAArch64MTE::MakeTaggedRange(
     lldb::addr_t addr, lldb::addr_t end_addr,
@@ -74,13 +85,8 @@ MemoryTagManagerAArch64MTE::MakeTaggedRange(
   // We must remove tags here otherwise an address with a higher
   // tag value will always be > the other.
   ptr
diff _t len = AddressDiff(end_addr, addr);
-  if (len <= 0) {
-    return llvm::createStringError(
-        llvm::inconvertibleErrorCode(),
-        "End address (0x%" PRIx64
-        ") must be greater than the start address (0x%" PRIx64 ")",
-        end_addr, addr);
-  }
+  if (len <= 0)
+    return MakeInvalidRangeErr(addr, end_addr);
 
   // Region addresses will not have memory tags. So when searching
   // we must use an untagged address.
@@ -123,6 +129,91 @@ MemoryTagManagerAArch64MTE::MakeTaggedRange(
   return tag_range;
 }
 
+llvm::Expected<std::vector<MemoryTagManager::TagRange>>
+MemoryTagManagerAArch64MTE::MakeTaggedRanges(
+    lldb::addr_t addr, lldb::addr_t end_addr,
+    const lldb_private::MemoryRegionInfos &memory_regions) const {
+  // First check that the range is not inverted.
+  // We must remove tags here otherwise an address with a higher
+  // tag value will always be > the other.
+  ptr
diff _t len = AddressDiff(end_addr, addr);
+  if (len <= 0)
+    return MakeInvalidRangeErr(addr, end_addr);
+
+  std::vector<MemoryTagManager::TagRange> tagged_ranges;
+  // No memory regions means no tagged memory at all
+  if (memory_regions.empty())
+    return tagged_ranges;
+
+  // For the logic to work regions must be in ascending order
+  // which is what you'd have if you used GetMemoryRegions.
+  assert(std::is_sorted(
+      memory_regions.begin(), memory_regions.end(),
+      [](const MemoryRegionInfo &lhs, const MemoryRegionInfo &rhs) {
+        return lhs.GetRange().GetRangeBase() < rhs.GetRange().GetRangeBase();
+      }));
+
+  // If we're debugging userspace in an OS like Linux that uses an MMU,
+  // the only reason we'd get overlapping regions is incorrect data.
+  // It is possible that won't hold for embedded with memory protection
+  // units (MPUs) that allow overlaps.
+  //
+  // For now we're going to assume the former, as there is no good way
+  // to handle overlaps. For example:
+  // < requested range >
+  // [--  region 1   --]
+  //           [-- region 2--]
+  // Where the first region will reduce the requested range to nothing
+  // and exit early before it sees the overlap.
+  MemoryRegionInfos::const_iterator overlap = std::adjacent_find(
+      memory_regions.begin(), memory_regions.end(),
+      [](const MemoryRegionInfo &lhs, const MemoryRegionInfo &rhs) {
+        return rhs.GetRange().DoesIntersect(lhs.GetRange());
+      });
+  UNUSED_IF_ASSERT_DISABLED(overlap);
+  assert(overlap == memory_regions.end());
+
+  // Region addresses will not have memory tags so when searching
+  // we must use an untagged address.
+  MemoryRegionInfo::RangeType range(RemoveTagBits(addr), len);
+  range = ExpandToGranule(range);
+
+  // While there are regions to check and the range has non zero length
+  for (const MemoryRegionInfo &region : memory_regions) {
+    // If range we're checking has been reduced to zero length, exit early
+    if (!range.IsValid())
+      break;
+
+    // If the region doesn't overlap the range at all, ignore it.
+    if (!region.GetRange().DoesIntersect(range))
+      continue;
+
+    // If it's tagged record this sub-range.
+    // (assuming that it's already granule aligned)
+    if (region.GetMemoryTagged()) {
+      // The region found may extend outside the requested range.
+      // For example the first region might start before the range.
+      // We must only add what covers the requested range.
+      lldb::addr_t start =
+          std::max(range.GetRangeBase(), region.GetRange().GetRangeBase());
+      lldb::addr_t end =
+          std::min(range.GetRangeEnd(), region.GetRange().GetRangeEnd());
+      tagged_ranges.push_back(MemoryTagManager::TagRange(start, end - start));
+    }
+
+    // Move the range up to start at the end of the region.
+    lldb::addr_t old_end = range.GetRangeEnd();
+    // This "slides" the range so it moves the end as well.
+    range.SetRangeBase(region.GetRange().GetRangeEnd());
+    // So we set the end back to the original end address after sliding it up.
+    range.SetRangeEnd(old_end);
+    // (if the above were to try to set end < begin the range will just be set
+    // to 0 size)
+  }
+
+  return tagged_ranges;
+}
+
 llvm::Expected<std::vector<lldb::addr_t>>
 MemoryTagManagerAArch64MTE::UnpackTagsData(const std::vector<uint8_t> &tags,
                                            size_t granules /*=0*/) const {

diff  --git a/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h b/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h
index 7825ba0a9249..7cda728b140f 100644
--- a/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h
+++ b/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h
@@ -36,6 +36,10 @@ class MemoryTagManagerAArch64MTE : public MemoryTagManager {
       lldb::addr_t addr, lldb::addr_t end_addr,
       const lldb_private::MemoryRegionInfos &memory_regions) const override;
 
+  llvm::Expected<std::vector<TagRange>> MakeTaggedRanges(
+      lldb::addr_t addr, lldb::addr_t end_addr,
+      const lldb_private::MemoryRegionInfos &memory_regions) const override;
+
   llvm::Expected<std::vector<lldb::addr_t>>
   UnpackTagsData(const std::vector<uint8_t> &tags,
                  size_t granules = 0) const override;

diff  --git a/lldb/unittests/Process/Utility/MemoryTagManagerAArch64MTETest.cpp b/lldb/unittests/Process/Utility/MemoryTagManagerAArch64MTETest.cpp
index 1a34b05ac671..78688aef3fd8 100644
--- a/lldb/unittests/Process/Utility/MemoryTagManagerAArch64MTETest.cpp
+++ b/lldb/unittests/Process/Utility/MemoryTagManagerAArch64MTETest.cpp
@@ -165,6 +165,14 @@ TEST(MemoryTagManagerAArch64MTETest, MakeTaggedRange) {
       llvm::FailedWithMessage(
           "End address (0x0) must be greater than the start address (0x1)"));
 
+  // The inversion check ignores tags in the addresses (MTE tags start at bit
+  // 56).
+  ASSERT_THAT_EXPECTED(
+      manager.MakeTaggedRange((lldb::addr_t)1 << 56,
+                              ((lldb::addr_t)2 << 56) + 0x10, memory_regions),
+      llvm::FailedWithMessage(
+          "Address range 0x0:0x10 is not in a memory tagged region"));
+
   // Adding a single region to cover the whole range
   memory_regions.push_back(MakeRegionInfo(0, 0x1000, true));
 
@@ -247,6 +255,122 @@ TEST(MemoryTagManagerAArch64MTETest, MakeTaggedRange) {
   ASSERT_EQ(*got, expected_range);
 }
 
+TEST(MemoryTagManagerAArch64MTETest, MakeTaggedRanges) {
+  MemoryTagManagerAArch64MTE manager;
+  MemoryRegionInfos memory_regions;
+
+  // Note that MakeTaggedRanges takes start/end address.
+  // Whereas TagRanges and regions take start address and size.
+
+  // Range must not be inverted
+  ASSERT_THAT_EXPECTED(
+      manager.MakeTaggedRanges(1, 0, memory_regions),
+      llvm::FailedWithMessage(
+          "End address (0x0) must be greater than the start address (0x1)"));
+
+  // We remove tags before doing the inversion check, so this is not an error.
+  // Also no regions means no tagged regions returned.
+  // (bit 56 is where MTE tags begin)
+  llvm::Expected<std::vector<MemoryTagManager::TagRange>> got =
+      manager.MakeTaggedRanges((lldb::addr_t)2 << 56,
+                               ((lldb::addr_t)1 << 56) + 0x10, memory_regions);
+  ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
+  ASSERT_EQ(*got, std::vector<MemoryTagManager::TagRange>{});
+
+  // Cover whole range, untagged. No ranges returned.
+  memory_regions.push_back(MakeRegionInfo(0, 0x20, false));
+  got = manager.MakeTaggedRanges(0, 0x20, memory_regions);
+  ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
+  ASSERT_EQ(*got, std::vector<MemoryTagManager::TagRange>{});
+
+  // Make the region tagged and it'll be the one range returned.
+  memory_regions.back().SetMemoryTagged(MemoryRegionInfo::eYes);
+  got = manager.MakeTaggedRanges(0, 0x20, memory_regions);
+  ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
+  ASSERT_EQ(*got, std::vector<MemoryTagManager::TagRange>{
+                      MemoryTagManager::TagRange(0, 0x20)});
+
+  // This region will be trimmed if it's larger than the whole range.
+  memory_regions.clear();
+  memory_regions.push_back(MakeRegionInfo(0, 0x40, true));
+  got = manager.MakeTaggedRanges(0x10, 0x30, memory_regions);
+  ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
+  ASSERT_EQ(*got, std::vector<MemoryTagManager::TagRange>{
+                      MemoryTagManager::TagRange(0x10, 0x20)});
+
+  memory_regions.clear();
+
+  // For the following tests we keep the input regions
+  // in ascending order as MakeTaggedRanges expects.
+
+  // Only start of range is tagged, only that is returned.
+  // Start the region just before the requested range to check
+  // we limit the result to the requested range.
+  memory_regions.push_back(MakeRegionInfo(0, 0x20, true));
+  got = manager.MakeTaggedRanges(0x10, 0x100, memory_regions);
+  ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
+  ASSERT_EQ(*got, std::vector<MemoryTagManager::TagRange>{
+                      MemoryTagManager::TagRange(0x10, 0x10)});
+
+  // Add a tagged region at the end, now we get both
+  // and the middle is untagged.
+  // <tagged: [0x0, 0x20)>
+  // <...>
+  // <tagged: [0xE0, 0x120)>
+  // The range added here is deliberately over the end of the
+  // requested range to show that we trim the end.
+  memory_regions.push_back(MakeRegionInfo(0xE0, 0x40, true));
+  got = manager.MakeTaggedRanges(0x10, 0x110, memory_regions);
+  ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
+
+  std::vector<MemoryTagManager::TagRange> expected{
+      MemoryTagManager::TagRange(0x10, 0x10),
+      MemoryTagManager::TagRange(0xE0, 0x30)};
+  ASSERT_EQ(*got, expected);
+
+  // Now add a middle tagged region.
+  // <tagged: [0x0, 0x20)>
+  // <...>
+  // <tagged: [0x90, 0xB0)>
+  // <...>
+  // <tagged: [0xE0, 0x120)>
+  memory_regions.insert(std::next(memory_regions.begin()),
+                        MakeRegionInfo(0x90, 0x20, true));
+
+  // As the given regions are in ascending order, the resulting
+  // tagged ranges are also. So this new range goes in the middle.
+  expected.insert(std::next(expected.begin()),
+                  MemoryTagManager::TagRange(0x90, 0x20));
+  got = manager.MakeTaggedRanges(0x10, 0x110, memory_regions);
+  ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
+  ASSERT_EQ(*got, expected);
+
+  // Then if we add untagged regions in between the tagged,
+  // the output should stay the same.
+  // <tagged:   [0x0, 0x20)>
+  // <untagged: [0x20, 0x90)>
+  // <tagged:   [0x90, 0xB0)>
+  // <untagged: [0xB0, 0xE0)>
+  // <tagged:   [0xE0, 0x120)>
+  memory_regions.insert(std::next(memory_regions.begin()),
+                        MakeRegionInfo(0x20, 0x70, false));
+  memory_regions.insert(std::prev(memory_regions.end()),
+                        MakeRegionInfo(0xB0, 0x30, false));
+  got = manager.MakeTaggedRanges(0x10, 0x110, memory_regions);
+  ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
+  ASSERT_EQ(*got, expected);
+
+  // Finally check that we handle only having the end of the range.
+  memory_regions.clear();
+  expected.clear();
+
+  memory_regions.push_back(MakeRegionInfo(0x100, 0x10, true));
+  expected.push_back(MemoryTagManager::TagRange(0x100, 0x10));
+  got = manager.MakeTaggedRanges(0x10, 0x110, memory_regions);
+  ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
+  ASSERT_EQ(*got, expected);
+}
+
 TEST(MemoryTagManagerAArch64MTETest, RemoveTagBits) {
   MemoryTagManagerAArch64MTE manager;
 


        


More information about the lldb-commits mailing list