1 //===-- MemoryTagManagerAArch64MTE.cpp --------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "MemoryTagManagerAArch64MTE.h"
10 
11 using namespace lldb_private;
12 
13 static const unsigned MTE_START_BIT = 56;
14 static const unsigned MTE_TAG_MAX = 0xf;
15 static const unsigned MTE_GRANULE_SIZE = 16;
16 
17 lldb::addr_t
GetLogicalTag(lldb::addr_t addr) const18 MemoryTagManagerAArch64MTE::GetLogicalTag(lldb::addr_t addr) const {
19   return (addr >> MTE_START_BIT) & MTE_TAG_MAX;
20 }
21 
22 lldb::addr_t
RemoveNonAddressBits(lldb::addr_t addr) const23 MemoryTagManagerAArch64MTE::RemoveNonAddressBits(lldb::addr_t addr) const {
24   // Here we're ignoring the whole top byte. If you've got MTE
25   // you must also have TBI (top byte ignore).
26   // The other 4 bits could contain other extension bits or
27   // user metadata.
28   return addr & ~((lldb::addr_t)0xFF << MTE_START_BIT);
29 }
30 
AddressDiff(lldb::addr_t addr1,lldb::addr_t addr2) const31 ptrdiff_t MemoryTagManagerAArch64MTE::AddressDiff(lldb::addr_t addr1,
32                                                   lldb::addr_t addr2) const {
33   return RemoveNonAddressBits(addr1) - RemoveNonAddressBits(addr2);
34 }
35 
GetGranuleSize() const36 lldb::addr_t MemoryTagManagerAArch64MTE::GetGranuleSize() const {
37   return MTE_GRANULE_SIZE;
38 }
39 
GetAllocationTagType() const40 int32_t MemoryTagManagerAArch64MTE::GetAllocationTagType() const {
41   return eMTE_allocation;
42 }
43 
GetTagSizeInBytes() const44 size_t MemoryTagManagerAArch64MTE::GetTagSizeInBytes() const { return 1; }
45 
46 MemoryTagManagerAArch64MTE::TagRange
ExpandToGranule(TagRange range) const47 MemoryTagManagerAArch64MTE::ExpandToGranule(TagRange range) const {
48   // Ignore reading a length of 0
49   if (!range.IsValid())
50     return range;
51 
52   const size_t granule = GetGranuleSize();
53 
54   // Align start down to granule start
55   lldb::addr_t new_start = range.GetRangeBase();
56   lldb::addr_t align_down_amount = new_start % granule;
57   new_start -= align_down_amount;
58 
59   // Account for the distance we moved the start above
60   size_t new_len = range.GetByteSize() + align_down_amount;
61   // Then align up to the end of the granule
62   size_t align_up_amount = granule - (new_len % granule);
63   if (align_up_amount != granule)
64     new_len += align_up_amount;
65 
66   return TagRange(new_start, new_len);
67 }
68 
69 llvm::Expected<MemoryTagManager::TagRange>
MakeTaggedRange(lldb::addr_t addr,lldb::addr_t end_addr,const lldb_private::MemoryRegionInfos & memory_regions) const70 MemoryTagManagerAArch64MTE::MakeTaggedRange(
71     lldb::addr_t addr, lldb::addr_t end_addr,
72     const lldb_private::MemoryRegionInfos &memory_regions) const {
73   // First check that the range is not inverted.
74   // We must remove tags here otherwise an address with a higher
75   // tag value will always be > the other.
76   ptrdiff_t len = AddressDiff(end_addr, addr);
77   if (len <= 0) {
78     return llvm::createStringError(
79         llvm::inconvertibleErrorCode(),
80         "End address (0x%" PRIx64
81         ") must be greater than the start address (0x%" PRIx64 ")",
82         end_addr, addr);
83   }
84 
85   // Region addresses will not have memory tags. So when searching
86   // we must use an untagged address.
87   MemoryRegionInfo::RangeType tag_range(RemoveNonAddressBits(addr), len);
88   tag_range = ExpandToGranule(tag_range);
89 
90   // Make a copy so we can use the original for errors and the final return.
91   MemoryRegionInfo::RangeType remaining_range(tag_range);
92 
93   // While there are parts of the range that don't have a matching tagged memory
94   // region
95   while (remaining_range.IsValid()) {
96     // Search for a region that contains the start of the range
97     MemoryRegionInfos::const_iterator region = std::find_if(
98         memory_regions.cbegin(), memory_regions.cend(),
99         [&remaining_range](const MemoryRegionInfo &region) {
100           return region.GetRange().Contains(remaining_range.GetRangeBase());
101         });
102 
103     if (region == memory_regions.cend() ||
104         region->GetMemoryTagged() != MemoryRegionInfo::eYes) {
105       // Some part of this range is untagged (or unmapped) so error
106       return llvm::createStringError(llvm::inconvertibleErrorCode(),
107                                      "Address range 0x%" PRIx64 ":0x%" PRIx64
108                                      " is not in a memory tagged region",
109                                      tag_range.GetRangeBase(),
110                                      tag_range.GetRangeEnd());
111     }
112 
113     // We've found some part of the range so remove that part and continue
114     // searching for the rest. Moving the base "slides" the range so we need to
115     // save/restore the original end. If old_end is less than the new base, the
116     // range will be set to have 0 size and we'll exit the while.
117     lldb::addr_t old_end = remaining_range.GetRangeEnd();
118     remaining_range.SetRangeBase(region->GetRange().GetRangeEnd());
119     remaining_range.SetRangeEnd(old_end);
120   }
121 
122   // Every part of the range is contained within a tagged memory region.
123   return tag_range;
124 }
125 
126 llvm::Expected<std::vector<lldb::addr_t>>
UnpackTagsData(const std::vector<uint8_t> & tags,size_t granules) const127 MemoryTagManagerAArch64MTE::UnpackTagsData(const std::vector<uint8_t> &tags,
128                                            size_t granules /*=0*/) const {
129   // 0 means don't check the number of tags before unpacking
130   if (granules) {
131     size_t num_tags = tags.size() / GetTagSizeInBytes();
132     if (num_tags != granules) {
133       return llvm::createStringError(
134           llvm::inconvertibleErrorCode(),
135           "Packed tag data size does not match expected number of tags. "
136           "Expected %zu tag(s) for %zu granule(s), got %zu tag(s).",
137           granules, granules, num_tags);
138     }
139   }
140 
141   // (if bytes per tag was not 1, we would reconstruct them here)
142 
143   std::vector<lldb::addr_t> unpacked;
144   unpacked.reserve(tags.size());
145   for (auto it = tags.begin(); it != tags.end(); ++it) {
146     // Check all tags are in range
147     if (*it > MTE_TAG_MAX) {
148       return llvm::createStringError(
149           llvm::inconvertibleErrorCode(),
150           "Found tag 0x%x which is > max MTE tag value of 0x%x.", *it,
151           MTE_TAG_MAX);
152     }
153     unpacked.push_back(*it);
154   }
155 
156   return unpacked;
157 }
158 
PackTags(const std::vector<lldb::addr_t> & tags) const159 llvm::Expected<std::vector<uint8_t>> MemoryTagManagerAArch64MTE::PackTags(
160     const std::vector<lldb::addr_t> &tags) const {
161   std::vector<uint8_t> packed;
162   packed.reserve(tags.size() * GetTagSizeInBytes());
163 
164   for (auto tag : tags) {
165     if (tag > MTE_TAG_MAX) {
166       return llvm::createStringError(llvm::inconvertibleErrorCode(),
167                                      "Found tag 0x%" PRIx64
168                                      " which is > max MTE tag value of 0x%x.",
169                                      tag, MTE_TAG_MAX);
170     }
171     packed.push_back(static_cast<uint8_t>(tag));
172   }
173 
174   return packed;
175 }
176 
177 llvm::Expected<std::vector<lldb::addr_t>>
RepeatTagsForRange(const std::vector<lldb::addr_t> & tags,TagRange range) const178 MemoryTagManagerAArch64MTE::RepeatTagsForRange(
179     const std::vector<lldb::addr_t> &tags, TagRange range) const {
180   std::vector<lldb::addr_t> new_tags;
181 
182   // If the range is not empty
183   if (range.IsValid()) {
184     if (tags.empty()) {
185       return llvm::createStringError(
186           llvm::inconvertibleErrorCode(),
187           "Expected some tags to cover given range, got zero.");
188     }
189 
190     // We assume that this range has already been expanded/aligned to granules
191     size_t granules = range.GetByteSize() / GetGranuleSize();
192     new_tags.reserve(granules);
193     for (size_t to_copy = 0; granules > 0; granules -= to_copy) {
194       to_copy = granules > tags.size() ? tags.size() : granules;
195       new_tags.insert(new_tags.end(), tags.begin(), tags.begin() + to_copy);
196     }
197   }
198 
199   return new_tags;
200 }
201