1 //===-- CommandObjectMemoryTag.cpp ----------------------------------------===//
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 "CommandObjectMemoryTag.h"
10 #include "lldb/Host/OptionParser.h"
11 #include "lldb/Interpreter/CommandReturnObject.h"
12 #include "lldb/Interpreter/OptionArgParser.h"
13 #include "lldb/Interpreter/OptionGroupFormat.h"
14 #include "lldb/Interpreter/OptionValueString.h"
15 #include "lldb/Target/Process.h"
16 
17 using namespace lldb;
18 using namespace lldb_private;
19 
20 #define LLDB_OPTIONS_memory_tag_read
21 #include "CommandOptions.inc"
22 
23 class CommandObjectMemoryTagRead : public CommandObjectParsed {
24 public:
25   CommandObjectMemoryTagRead(CommandInterpreter &interpreter)
26       : CommandObjectParsed(interpreter, "tag",
27                             "Read memory tags for the given range of memory."
28                             " Mismatched tags will be marked.",
29                             nullptr,
30                             eCommandRequiresTarget | eCommandRequiresProcess |
31                                 eCommandProcessMustBePaused) {
32     // Address
33     m_arguments.push_back(
34         CommandArgumentEntry{CommandArgumentData(eArgTypeAddressOrExpression)});
35     // Optional end address
36     m_arguments.push_back(CommandArgumentEntry{
37         CommandArgumentData(eArgTypeAddressOrExpression, eArgRepeatOptional)});
38   }
39 
40   ~CommandObjectMemoryTagRead() override = default;
41 
42 protected:
43   bool DoExecute(Args &command, CommandReturnObject &result) override {
44     if ((command.GetArgumentCount() < 1) || (command.GetArgumentCount() > 2)) {
45       result.AppendError(
46           "wrong number of arguments; expected at least <address-expression>, "
47           "at most <address-expression> <end-address-expression>");
48       return false;
49     }
50 
51     Status error;
52     addr_t start_addr = OptionArgParser::ToAddress(
53         &m_exe_ctx, command[0].ref(), LLDB_INVALID_ADDRESS, &error);
54     if (start_addr == LLDB_INVALID_ADDRESS) {
55       result.AppendErrorWithFormatv("Invalid address expression, {0}",
56                                     error.AsCString());
57       return false;
58     }
59 
60     // Default 1 byte beyond start, rounds up to at most 1 granule later
61     addr_t end_addr = start_addr + 1;
62 
63     if (command.GetArgumentCount() > 1) {
64       end_addr = OptionArgParser::ToAddress(&m_exe_ctx, command[1].ref(),
65                                             LLDB_INVALID_ADDRESS, &error);
66       if (end_addr == LLDB_INVALID_ADDRESS) {
67         result.AppendErrorWithFormatv("Invalid end address expression, {0}",
68                                       error.AsCString());
69         return false;
70       }
71     }
72 
73     Process *process = m_exe_ctx.GetProcessPtr();
74     llvm::Expected<const MemoryTagManager *> tag_manager_or_err =
75         process->GetMemoryTagManager();
76 
77     if (!tag_manager_or_err) {
78       result.SetError(Status(tag_manager_or_err.takeError()));
79       return false;
80     }
81 
82     const MemoryTagManager *tag_manager = *tag_manager_or_err;
83 
84     MemoryRegionInfos memory_regions;
85     // If this fails the list of regions is cleared, so we don't need to read
86     // the return status here.
87     process->GetMemoryRegions(memory_regions);
88     llvm::Expected<MemoryTagManager::TagRange> tagged_range =
89         tag_manager->MakeTaggedRange(start_addr, end_addr, memory_regions);
90 
91     if (!tagged_range) {
92       result.SetError(Status(tagged_range.takeError()));
93       return false;
94     }
95 
96     llvm::Expected<std::vector<lldb::addr_t>> tags = process->ReadMemoryTags(
97         tagged_range->GetRangeBase(), tagged_range->GetByteSize());
98 
99     if (!tags) {
100       result.SetError(Status(tags.takeError()));
101       return false;
102     }
103 
104     lldb::addr_t logical_tag = tag_manager->GetLogicalTag(start_addr);
105     result.AppendMessageWithFormatv("Logical tag: {0:x}", logical_tag);
106     result.AppendMessage("Allocation tags:");
107 
108     addr_t addr = tagged_range->GetRangeBase();
109     for (auto tag : *tags) {
110       addr_t next_addr = addr + tag_manager->GetGranuleSize();
111       // Showing tagged adresses here until we have non address bit handling
112       result.AppendMessageWithFormatv("[{0:x}, {1:x}): {2:x}{3}", addr,
113                                       next_addr, tag,
114                                       logical_tag == tag ? "" : " (mismatch)");
115       addr = next_addr;
116     }
117 
118     result.SetStatus(eReturnStatusSuccessFinishResult);
119     return true;
120   }
121 };
122 
123 #define LLDB_OPTIONS_memory_tag_write
124 #include "CommandOptions.inc"
125 
126 class CommandObjectMemoryTagWrite : public CommandObjectParsed {
127 public:
128   class OptionGroupTagWrite : public OptionGroup {
129   public:
130     OptionGroupTagWrite() : OptionGroup(), m_end_addr(LLDB_INVALID_ADDRESS) {}
131 
132     ~OptionGroupTagWrite() override = default;
133 
134     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
135       return llvm::makeArrayRef(g_memory_tag_write_options);
136     }
137 
138     Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value,
139                           ExecutionContext *execution_context) override {
140       Status status;
141       const int short_option =
142           g_memory_tag_write_options[option_idx].short_option;
143 
144       switch (short_option) {
145       case 'e':
146         m_end_addr = OptionArgParser::ToAddress(execution_context, option_value,
147                                                 LLDB_INVALID_ADDRESS, &status);
148         break;
149       default:
150         llvm_unreachable("Unimplemented option");
151       }
152 
153       return status;
154     }
155 
156     void OptionParsingStarting(ExecutionContext *execution_context) override {
157       m_end_addr = LLDB_INVALID_ADDRESS;
158     }
159 
160     lldb::addr_t m_end_addr;
161   };
162 
163   CommandObjectMemoryTagWrite(CommandInterpreter &interpreter)
164       : CommandObjectParsed(interpreter, "tag",
165                             "Write memory tags starting from the granule that "
166                             "contains the given address.",
167                             nullptr,
168                             eCommandRequiresTarget | eCommandRequiresProcess |
169                                 eCommandProcessMustBePaused),
170         m_option_group(), m_tag_write_options() {
171     // Address
172     m_arguments.push_back(
173         CommandArgumentEntry{CommandArgumentData(eArgTypeAddressOrExpression)});
174     // One or more tag values
175     m_arguments.push_back(CommandArgumentEntry{
176         CommandArgumentData(eArgTypeValue, eArgRepeatPlus)});
177 
178     m_option_group.Append(&m_tag_write_options);
179     m_option_group.Finalize();
180   }
181 
182   ~CommandObjectMemoryTagWrite() override = default;
183 
184   Options *GetOptions() override { return &m_option_group; }
185 
186 protected:
187   bool DoExecute(Args &command, CommandReturnObject &result) override {
188     if (command.GetArgumentCount() < 2) {
189       result.AppendError("wrong number of arguments; expected "
190                          "<address-expression> <tag> [<tag> [...]]");
191       return false;
192     }
193 
194     Status error;
195     addr_t start_addr = OptionArgParser::ToAddress(
196         &m_exe_ctx, command[0].ref(), LLDB_INVALID_ADDRESS, &error);
197     if (start_addr == LLDB_INVALID_ADDRESS) {
198       result.AppendErrorWithFormatv("Invalid address expression, {0}",
199                                     error.AsCString());
200       return false;
201     }
202 
203     command.Shift(); // shift off start address
204 
205     std::vector<lldb::addr_t> tags;
206     for (auto &entry : command) {
207       lldb::addr_t tag_value;
208       // getAsInteger returns true on failure
209       if (entry.ref().getAsInteger(0, tag_value)) {
210         result.AppendErrorWithFormat(
211             "'%s' is not a valid unsigned decimal string value.\n",
212             entry.c_str());
213         return false;
214       }
215       tags.push_back(tag_value);
216     }
217 
218     Process *process = m_exe_ctx.GetProcessPtr();
219     llvm::Expected<const MemoryTagManager *> tag_manager_or_err =
220         process->GetMemoryTagManager();
221 
222     if (!tag_manager_or_err) {
223       result.SetError(Status(tag_manager_or_err.takeError()));
224       return false;
225     }
226 
227     const MemoryTagManager *tag_manager = *tag_manager_or_err;
228 
229     MemoryRegionInfos memory_regions;
230     // If this fails the list of regions is cleared, so we don't need to read
231     // the return status here.
232     process->GetMemoryRegions(memory_regions);
233 
234     // We have to assume start_addr is not granule aligned.
235     // So if we simply made a range:
236     // (start_addr, start_addr + (N * granule_size))
237     // We would end up with a range that isn't N granules but N+1
238     // granules. To avoid this we'll align the start first using the method that
239     // doesn't check memory attributes. (if the final range is untagged we'll
240     // handle that error later)
241     lldb::addr_t aligned_start_addr =
242         tag_manager->ExpandToGranule(MemoryTagManager::TagRange(start_addr, 1))
243             .GetRangeBase();
244 
245     lldb::addr_t end_addr = 0;
246     // When you have an end address you want to align the range like tag read
247     // does. Meaning, align the start down (which we've done) and align the end
248     // up.
249     if (m_tag_write_options.m_end_addr != LLDB_INVALID_ADDRESS)
250       end_addr = m_tag_write_options.m_end_addr;
251     else
252       // Without an end address assume number of tags matches number of granules
253       // to write to
254       end_addr =
255           aligned_start_addr + (tags.size() * tag_manager->GetGranuleSize());
256 
257     // Now we've aligned the start address so if we ask for another range
258     // using the number of tags N, we'll get back a range that is also N
259     // granules in size.
260     llvm::Expected<MemoryTagManager::TagRange> tagged_range =
261         tag_manager->MakeTaggedRange(aligned_start_addr, end_addr,
262                                      memory_regions);
263 
264     if (!tagged_range) {
265       result.SetError(Status(tagged_range.takeError()));
266       return false;
267     }
268 
269     Status status = process->WriteMemoryTags(tagged_range->GetRangeBase(),
270                                              tagged_range->GetByteSize(), tags);
271 
272     if (status.Fail()) {
273       result.SetError(status);
274       return false;
275     }
276 
277     result.SetStatus(eReturnStatusSuccessFinishResult);
278     return true;
279   }
280 
281   OptionGroupOptions m_option_group;
282   OptionGroupTagWrite m_tag_write_options;
283 };
284 
285 CommandObjectMemoryTag::CommandObjectMemoryTag(CommandInterpreter &interpreter)
286     : CommandObjectMultiword(
287           interpreter, "tag", "Commands for manipulating memory tags",
288           "memory tag <sub-command> [<sub-command-options>]") {
289   CommandObjectSP read_command_object(
290       new CommandObjectMemoryTagRead(interpreter));
291   read_command_object->SetCommandName("memory tag read");
292   LoadSubCommand("read", read_command_object);
293 
294   CommandObjectSP write_command_object(
295       new CommandObjectMemoryTagWrite(interpreter));
296   write_command_object->SetCommandName("memory tag write");
297   LoadSubCommand("write", write_command_object);
298 }
299 
300 CommandObjectMemoryTag::~CommandObjectMemoryTag() = default;
301