diff --git a/lldb/include/lldb/Target/MemoryTagManager.h b/lldb/include/lldb/Target/MemoryTagManager.h --- a/lldb/include/lldb/Target/MemoryTagManager.h +++ b/lldb/include/lldb/Target/MemoryTagManager.h @@ -9,6 +9,7 @@ #ifndef LLDB_TARGET_MEMORYTAGMANAGER_H #define LLDB_TARGET_MEMORYTAGMANAGER_H +#include "lldb/Target/MemoryRegionInfo.h" #include "lldb/Utility/RangeMap.h" #include "lldb/lldb-private.h" #include "llvm/Support/Error.h" @@ -57,6 +58,16 @@ // expanded to 2 granules. virtual TagRange ExpandToGranule(TagRange range) const = 0; + // Given a range addr to end_addr, check that: + // * end_addr >= addr (when memory tags are removed) + // * the granule aligned range is completely covered by tagged memory + // + // If so, return a modified range. This will be expanded to be + // granule aligned and have an untagged base address. + virtual llvm::Expected MakeTaggedRange( + 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/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -1708,30 +1708,19 @@ lldb::addr_t CallocateMemory(size_t size, uint32_t permissions, Status &error); - /// If the address range given is in a memory tagged range and this - /// architecture and process supports memory tagging, return a tag + /// If this architecture and process supports memory tagging, return a tag /// manager that can be used to maniupulate those memory tags. - /// Tags present in the addresses given are ignored. - /// - /// \param[in] addr - /// Start of memory range. - /// - /// \param[in] end_addr - /// End of the memory range. Where end is one beyond the last byte to be - /// included. /// /// \return /// Either a valid pointer to a tag manager or an error describing why one /// could not be provided. - llvm::Expected - GetMemoryTagManager(lldb::addr_t addr, lldb::addr_t end_addr); + llvm::Expected GetMemoryTagManager(); - /// Expands the range addr to addr+len to align with granule boundaries and - /// then calls DoReadMemoryTags to do the target specific operations. - /// Tags are returned unpacked so can be used without conversion. + /// Read memory tags for the range addr to addr+len. It is assumed + /// that this range has already been granule aligned. + /// (see MemoryTagManager::MakeTaggedRange) /// - /// \param[in] tag_manager - /// The tag manager to get memory tagging information from. + /// This calls DoReadMemoryTags to do the target specific operations. /// /// \param[in] addr /// Start of memory range to read tags for. @@ -1740,11 +1729,12 @@ /// Length of memory range to read tags for (in bytes). /// /// \return - /// Either the unpacked tags or an error describing a failure to read - /// or unpack them. - llvm::Expected> - ReadMemoryTags(const MemoryTagManager *tag_manager, lldb::addr_t addr, - size_t len); + /// If this architecture or process does not support memory tagging, + /// an error saying so. + // If it does, either the memory tags or an error describing a + // failure to read or unpack them. + llvm::Expected> ReadMemoryTags(lldb::addr_t addr, + size_t len); /// Resolve dynamically loaded indirect functions. /// diff --git a/lldb/source/Commands/CommandObjectMemoryTag.cpp b/lldb/source/Commands/CommandObjectMemoryTag.cpp --- a/lldb/source/Commands/CommandObjectMemoryTag.cpp +++ b/lldb/source/Commands/CommandObjectMemoryTag.cpp @@ -68,7 +68,7 @@ Process *process = m_exe_ctx.GetProcessPtr(); llvm::Expected tag_manager_or_err = - process->GetMemoryTagManager(start_addr, end_addr); + process->GetMemoryTagManager(); if (!tag_manager_or_err) { result.SetError(Status(tag_manager_or_err.takeError())); @@ -76,9 +76,21 @@ } const MemoryTagManager *tag_manager = *tag_manager_or_err; - ptrdiff_t len = tag_manager->AddressDiff(end_addr, start_addr); - llvm::Expected> tags = - process->ReadMemoryTags(tag_manager, start_addr, len); + + MemoryRegionInfos memory_regions; + // If this fails the list of regions is cleared, so we don't need to read + // the return status here. + process->GetMemoryRegions(memory_regions); + llvm::Expected tagged_range = + tag_manager->MakeTaggedRange(start_addr, end_addr, memory_regions); + + if (!tagged_range) { + result.SetError(Status(tagged_range.takeError())); + return false; + } + + llvm::Expected> tags = process->ReadMemoryTags( + tagged_range->GetRangeBase(), tagged_range->GetByteSize()); if (!tags) { result.SetError(Status(tags.takeError())); @@ -89,8 +101,7 @@ tag_manager->GetLogicalTag(start_addr)); result.AppendMessage("Allocation tags:"); - MemoryTagManager::TagRange initial_range(start_addr, len); - addr_t addr = tag_manager->ExpandToGranule(initial_range).GetRangeBase(); + addr_t addr = tagged_range->GetRangeBase(); for (auto tag : *tags) { addr_t next_addr = addr + tag_manager->GetGranuleSize(); // Showing tagged adresses here until we have non address bit handling diff --git a/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h b/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h --- a/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h +++ b/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h @@ -32,6 +32,10 @@ TagRange ExpandToGranule(TagRange range) const override; + llvm::Expected MakeTaggedRange( + lldb::addr_t addr, lldb::addr_t end_addr, + const lldb_private::MemoryRegionInfos &memory_regions) const override; + llvm::Expected> UnpackTagsData(const std::vector &tags, size_t granules) const override; diff --git a/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp b/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp --- a/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp +++ b/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp @@ -66,6 +66,54 @@ return TagRange(new_start, new_len); } +llvm::Expected +MemoryTagManagerAArch64MTE::MakeTaggedRange( + lldb::addr_t addr, lldb::addr_t end_addr, + const lldb_private::MemoryRegionInfos &memory_regions) const { + ptrdiff_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); + } + + // Region addresses will not be tagged + MemoryRegionInfo::RangeType tag_range(RemoveNonAddressBits(addr), len); + tag_range = ExpandToGranule(tag_range); + + // Make a copy so we can use the original range in errors + MemoryRegionInfo::RangeType remaining_range(tag_range); + + // While we haven't found a matching memory region for some of the range + while (remaining_range.IsValid()) { + MemoryRegionInfos::const_iterator region = std::find_if( + memory_regions.cbegin(), memory_regions.cend(), + [&remaining_range](const MemoryRegionInfo ®ion) { + return region.GetRange().Contains(remaining_range.GetRangeBase()); + }); + + if (region == memory_regions.cend() || + region->GetMemoryTagged() != MemoryRegionInfo::eYes) { + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "Address range 0x%lx:0x%lx is not in a memory tagged region", + tag_range.GetRangeBase(), tag_range.GetRangeEnd()); + } + + // We've found some part of the range, look for the rest. + // Moving the base "slides" the range so we need to save/restore the + // original end. If old_end is then < the new base, the range will be set + // to have 0 size and we'll exit the while. + lldb::addr_t old_end = remaining_range.GetRangeEnd(); + remaining_range.SetRangeBase(region->GetRange().GetRangeEnd()); + remaining_range.SetRangeEnd(old_end); + } + + return tag_range; +} + llvm::Expected> MemoryTagManagerAArch64MTE::UnpackTagsData(const std::vector &tags, size_t granules) const { diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -6066,8 +6066,7 @@ return false; } -llvm::Expected -Process::GetMemoryTagManager(lldb::addr_t addr, lldb::addr_t end_addr) { +llvm::Expected Process::GetMemoryTagManager() { Architecture *arch = GetTarget().GetArchitecturePlugin(); const MemoryTagManager *tag_manager = arch ? arch->GetMemoryTagManager() : nullptr; @@ -6083,66 +6082,22 @@ "Process does not support memory tagging"); } - ptrdiff_t len = tag_manager->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); - } - - // Region lookup is not address size aware so mask the address - MemoryRegionInfo::RangeType tag_range(tag_manager->RemoveNonAddressBits(addr), - len); - tag_range = tag_manager->ExpandToGranule(tag_range); - - // Make a copy so we can use the original range in errors - MemoryRegionInfo::RangeType remaining_range(tag_range); - - // While we haven't found a matching memory region for some of the range - while (remaining_range.IsValid()) { - MemoryRegionInfo region; - Status status = GetMemoryRegionInfo(remaining_range.GetRangeBase(), region); - - if (status.Fail() || region.GetMemoryTagged() != MemoryRegionInfo::eYes) { - return llvm::createStringError( - llvm::inconvertibleErrorCode(), - "Address range 0x%lx:0x%lx is not in a memory tagged region", - tag_range.GetRangeBase(), tag_range.GetRangeEnd()); - } - - if (region.GetRange().GetRangeEnd() >= remaining_range.GetRangeEnd()) { - // We've found a region for the whole range or the last piece of a range - remaining_range.SetByteSize(0); - } else { - // We've found some part of the range, look for the rest - remaining_range.SetRangeBase(region.GetRange().GetRangeEnd()); - } - } - return tag_manager; } llvm::Expected> -Process::ReadMemoryTags(const MemoryTagManager *tag_manager, lldb::addr_t addr, - size_t len) { - if (!tag_manager) { - return llvm::createStringError( - llvm::inconvertibleErrorCode(), - "A memory tag manager is required for reading memory tags."); - } - - MemoryTagManager::TagRange range(tag_manager->RemoveNonAddressBits(addr), - len); - range = tag_manager->ExpandToGranule(range); +Process::ReadMemoryTags(lldb::addr_t addr, size_t len) { + llvm::Expected tag_manager_or_err = + GetMemoryTagManager(); + if (!tag_manager_or_err) + return tag_manager_or_err.takeError(); + const MemoryTagManager *tag_manager = *tag_manager_or_err; llvm::Expected> tag_data = - DoReadMemoryTags(range.GetRangeBase(), range.GetByteSize(), - tag_manager->GetAllocationTagType()); + DoReadMemoryTags(addr, len, tag_manager->GetAllocationTagType()); if (!tag_data) return tag_data.takeError(); - return tag_manager->UnpackTagsData( - *tag_data, range.GetByteSize() / tag_manager->GetGranuleSize()); + return tag_manager->UnpackTagsData(*tag_data, + len / tag_manager->GetGranuleSize()); } diff --git a/lldb/unittests/Process/Utility/MemoryTagManagerAArch64MTETest.cpp b/lldb/unittests/Process/Utility/MemoryTagManagerAArch64MTETest.cpp --- a/lldb/unittests/Process/Utility/MemoryTagManagerAArch64MTETest.cpp +++ b/lldb/unittests/Process/Utility/MemoryTagManagerAArch64MTETest.cpp @@ -94,6 +94,121 @@ manager.ExpandToGranule(MemoryTagManagerAArch64MTE::TagRange(18, 4))); } +static MemoryRegionInfo MakeRegionInfo(lldb::addr_t base, lldb::addr_t size, + bool tagged) { + return MemoryRegionInfo( + MemoryRegionInfo::RangeType(base, size), MemoryRegionInfo::eYes, + MemoryRegionInfo::eYes, MemoryRegionInfo::eYes, MemoryRegionInfo::eYes, + ConstString(), MemoryRegionInfo::eNo, 0, + /*memory_tagged=*/ + tagged ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); +} + +TEST(MemoryTagManagerAArch64MTETest, MakeTaggedRange) { + MemoryTagManagerAArch64MTE manager; + MemoryRegionInfos memory_regions; + + // No regions means no tagged regions, error + ASSERT_THAT_EXPECTED( + manager.MakeTaggedRange(0, 0x10, memory_regions), + llvm::FailedWithMessage( + "Address range 0x0:0x10 is not in a memory tagged region")); + + // Alignment is done before checking regions. + // Here 1 is rounded up to the granule size of 0x10. + ASSERT_THAT_EXPECTED( + manager.MakeTaggedRange(0, 1, memory_regions), + llvm::FailedWithMessage( + "Address range 0x0:0x10 is not in a memory tagged region")); + + // Range must not be inverted + ASSERT_THAT_EXPECTED( + manager.MakeTaggedRange(1, 0, memory_regions), + llvm::FailedWithMessage( + "End address (0x0) must be greater than the start address (0x1)")); + + // Adding a single region to cover the whole range + memory_regions.push_back(MakeRegionInfo(0, 0x1000, true)); + + // Range can have different tags for begin and end + // (which would make it look inverted if we didn't remove them) + // Note that range comes back with an untagged base and alginment + // applied. + MemoryTagManagerAArch64MTE::TagRange expected_range(0x0, 0x10); + llvm::Expected got = + manager.MakeTaggedRange(0x0f00000000000000, 0x0e00000000000001, + memory_regions); + ASSERT_THAT_EXPECTED(got, llvm::Succeeded()); + ASSERT_EQ(*got, expected_range); + + // Error if the range isn't within any region + ASSERT_THAT_EXPECTED( + manager.MakeTaggedRange(0x1000, 0x1010, memory_regions), + llvm::FailedWithMessage( + "Address range 0x1000:0x1010 is not in a memory tagged region")); + + // Error if the first part of a range isn't tagged + memory_regions.clear(); + const char *err_msg = + "Address range 0x0:0x1000 is not in a memory tagged region"; + + // First because it has no region entry + memory_regions.push_back(MakeRegionInfo(0x10, 0x1000, true)); + ASSERT_THAT_EXPECTED(manager.MakeTaggedRange(0, 0x1000, memory_regions), + llvm::FailedWithMessage(err_msg)); + + // Then because the first region is untagged + memory_regions.push_back(MakeRegionInfo(0, 0x10, false)); + ASSERT_THAT_EXPECTED(manager.MakeTaggedRange(0, 0x1000, memory_regions), + llvm::FailedWithMessage(err_msg)); + + // If we tag that first part it succeeds + memory_regions.back().SetMemoryTagged(MemoryRegionInfo::eYes); + expected_range = MemoryTagManagerAArch64MTE::TagRange(0x0, 0x1000); + got = manager.MakeTaggedRange(0, 0x1000, memory_regions); + ASSERT_THAT_EXPECTED(got, llvm::Succeeded()); + ASSERT_EQ(*got, expected_range); + + // Error if the end of a range is untagged + memory_regions.clear(); + + // First because it has no region entry + memory_regions.push_back(MakeRegionInfo(0, 0xF00, true)); + ASSERT_THAT_EXPECTED(manager.MakeTaggedRange(0, 0x1000, memory_regions), + llvm::FailedWithMessage(err_msg)); + + // Then because the last region is untagged + memory_regions.push_back(MakeRegionInfo(0xF00, 0x100, false)); + ASSERT_THAT_EXPECTED(manager.MakeTaggedRange(0, 0x1000, memory_regions), + llvm::FailedWithMessage(err_msg)); + + // If we tag the last part it succeeds + memory_regions.back().SetMemoryTagged(MemoryRegionInfo::eYes); + got = manager.MakeTaggedRange(0, 0x1000, memory_regions); + ASSERT_THAT_EXPECTED(got, llvm::Succeeded()); + ASSERT_EQ(*got, expected_range); + + // Error if the middle of a range is untagged + memory_regions.clear(); + + // First because it has no entry + memory_regions.push_back(MakeRegionInfo(0, 0x500, true)); + memory_regions.push_back(MakeRegionInfo(0x900, 0x700, true)); + ASSERT_THAT_EXPECTED(manager.MakeTaggedRange(0, 0x1000, memory_regions), + llvm::FailedWithMessage(err_msg)); + + // Then because it's untagged + memory_regions.push_back(MakeRegionInfo(0x500, 0x400, false)); + ASSERT_THAT_EXPECTED(manager.MakeTaggedRange(0, 0x1000, memory_regions), + llvm::FailedWithMessage(err_msg)); + + // If we tag the middle part it succeeds + memory_regions.back().SetMemoryTagged(MemoryRegionInfo::eYes); + got = manager.MakeTaggedRange(0, 0x1000, memory_regions); + ASSERT_THAT_EXPECTED(got, llvm::Succeeded()); + ASSERT_EQ(*got, expected_range); +} + TEST(MemoryTagManagerAArch64MTETest, RemoveNonAddressBits) { MemoryTagManagerAArch64MTE manager;