• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===-- cli-wrapper-mpxtable.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 // C++ includes
10 #include <cerrno>
11 #include <string>
12 
13 #include "cli-wrapper-mpxtable.h"
14 #include "lldb/API/SBCommandInterpreter.h"
15 #include "lldb/API/SBCommandReturnObject.h"
16 #include "lldb/API/SBMemoryRegionInfo.h"
17 #include "lldb/API/SBProcess.h"
18 #include "lldb/API/SBTarget.h"
19 #include "lldb/API/SBThread.h"
20 
21 #include "llvm/ADT/Triple.h"
22 #include "llvm/ADT/Twine.h"
23 
GetPtr(char * cptr,uint64_t & ptr,lldb::SBFrame & frame,lldb::SBCommandReturnObject & result)24 static bool GetPtr(char *cptr, uint64_t &ptr, lldb::SBFrame &frame,
25                    lldb::SBCommandReturnObject &result) {
26   if (!cptr) {
27     result.SetError("Bad argument.");
28     result.SetStatus(lldb::eReturnStatusFailed);
29     return false;
30   }
31 
32   lldb::SBValue ptr_addr = frame.GetValueForVariablePath(cptr);
33   if (!ptr_addr.IsValid()) {
34     result.SetError("Invalid pointer.");
35     result.SetStatus(lldb::eReturnStatusFailed);
36     return false;
37   }
38   ptr = ptr_addr.GetLoadAddress();
39   return true;
40 }
41 
42 enum {
43   mpx_base_mask_64 = ~(uint64_t)0xFFFULL,
44   mpx_bd_mask_64 = 0xFFFFFFF00000ULL,
45   bd_r_shift_64 = 20,
46   bd_l_shift_64 = 3,
47   bt_r_shift_64 = 3,
48   bt_l_shift_64 = 5,
49   bt_mask_64 = 0x0000000FFFF8ULL,
50 
51   mpx_base_mask_32 = 0xFFFFFFFFFFFFF000ULL,
52   mpx_bd_mask_32 = 0xFFFFF000ULL,
53   bd_r_shift_32 = 12,
54   bd_l_shift_32 = 2,
55   bt_r_shift_32 = 2,
56   bt_l_shift_32 = 4,
57   bt_mask_32 = 0x00000FFCULL,
58 };
59 
PrintBTEntry(lldb::addr_t lbound,lldb::addr_t ubound,uint64_t value,uint64_t meta,lldb::SBCommandReturnObject & result)60 static void PrintBTEntry(lldb::addr_t lbound, lldb::addr_t ubound,
61                          uint64_t value, uint64_t meta,
62                          lldb::SBCommandReturnObject &result) {
63   const lldb::addr_t one_cmpl64 = ~((lldb::addr_t)0);
64   const lldb::addr_t one_cmpl32 = ~((uint32_t)0);
65 
66   if ((lbound == one_cmpl64 || one_cmpl32) && ubound == 0) {
67     result.Printf("Null bounds on map: pointer value = 0x%lx\n", value);
68   } else {
69     result.Printf("    lbound = 0x%lx,", lbound);
70     result.Printf(" ubound = 0x%lx", ubound);
71     result.Printf(" (pointer value = 0x%lx,", value);
72     result.Printf(" metadata = 0x%lx)\n", meta);
73   }
74 }
75 
GetBTEntryAddr(uint64_t bndcfgu,uint64_t ptr,lldb::SBTarget & target,llvm::Triple::ArchType arch,size_t & size,lldb::addr_t & bt_entry_addr,lldb::SBCommandReturnObject & result,lldb::SBError & error)76 static bool GetBTEntryAddr(uint64_t bndcfgu, uint64_t ptr,
77                            lldb::SBTarget &target, llvm::Triple::ArchType arch,
78                            size_t &size, lldb::addr_t &bt_entry_addr,
79                            lldb::SBCommandReturnObject &result,
80                            lldb::SBError &error) {
81   lldb::addr_t mpx_base_mask;
82   lldb::addr_t mpx_bd_mask;
83   lldb::addr_t bd_r_shift;
84   lldb::addr_t bd_l_shift;
85   lldb::addr_t bt_r_shift;
86   lldb::addr_t bt_l_shift;
87   lldb::addr_t bt_mask;
88 
89   if (arch == llvm::Triple::ArchType::x86_64) {
90     mpx_base_mask = mpx_base_mask_64;
91     mpx_bd_mask = mpx_bd_mask_64;
92     bd_r_shift = bd_r_shift_64;
93     bd_l_shift = bd_l_shift_64;
94     bt_r_shift = bt_r_shift_64;
95     bt_l_shift = bt_l_shift_64;
96     bt_mask = bt_mask_64;
97   } else if (arch == llvm::Triple::ArchType::x86) {
98     mpx_base_mask = mpx_base_mask_32;
99     mpx_bd_mask = mpx_bd_mask_32;
100     bd_r_shift = bd_r_shift_32;
101     bd_l_shift = bd_l_shift_32;
102     bt_r_shift = bt_r_shift_32;
103     bt_l_shift = bt_l_shift_32;
104     bt_mask = bt_mask_32;
105   } else {
106     result.SetError("Invalid arch.");
107     result.SetStatus(lldb::eReturnStatusFailed);
108     return false;
109   }
110 
111   size = target.GetAddressByteSize();
112   lldb::addr_t mpx_bd_base = bndcfgu & mpx_base_mask;
113   lldb::addr_t bd_entry_offset = ((ptr & mpx_bd_mask) >> bd_r_shift)
114                                  << bd_l_shift;
115   lldb::addr_t bd_entry_addr = mpx_bd_base + bd_entry_offset;
116 
117   std::vector<uint8_t> bd_entry_v(size);
118   size_t ret = target.GetProcess().ReadMemory(
119       bd_entry_addr, static_cast<void *>(bd_entry_v.data()), size, error);
120   if (ret != size || !error.Success()) {
121     result.SetError("Failed access to BD entry.");
122     return false;
123   }
124 
125   lldb::SBData data;
126   data.SetData(error, bd_entry_v.data(), bd_entry_v.size(),
127                target.GetByteOrder(), size);
128   lldb::addr_t bd_entry = data.GetAddress(error, 0);
129 
130   if (!error.Success()) {
131     result.SetError("Failed access to BD entry.");
132     return false;
133   }
134 
135   if ((bd_entry & 0x01) == 0) {
136     result.SetError("Invalid bound directory.");
137     result.SetStatus(lldb::eReturnStatusFailed);
138     return false;
139   }
140 
141   // Clear status bit.
142   //
143   bd_entry--;
144 
145   lldb::addr_t bt_addr = bd_entry & ~bt_r_shift;
146   lldb::addr_t bt_entry_offset = ((ptr & bt_mask) >> bt_r_shift) << bt_l_shift;
147   bt_entry_addr = bt_addr + bt_entry_offset;
148 
149   return true;
150 }
151 
GetBTEntry(uint64_t bndcfgu,uint64_t ptr,lldb::SBTarget & target,llvm::Triple::ArchType arch,lldb::SBCommandReturnObject & result,lldb::SBError & error)152 static bool GetBTEntry(uint64_t bndcfgu, uint64_t ptr, lldb::SBTarget &target,
153                        llvm::Triple::ArchType arch,
154                        lldb::SBCommandReturnObject &result,
155                        lldb::SBError &error) {
156   lldb::addr_t bt_entry_addr;
157   size_t size;
158   if (!GetBTEntryAddr(bndcfgu, ptr, target, arch, size, bt_entry_addr, result,
159                       error))
160     return false;
161 
162   // bt_entry_v must have space to store the 4 elements of the BT entry (lower
163   // boundary,
164   // upper boundary, pointer value and meta data), which all have the same size
165   // 'size'.
166   //
167   std::vector<uint8_t> bt_entry_v(size * 4);
168   size_t ret = target.GetProcess().ReadMemory(
169       bt_entry_addr, static_cast<void *>(bt_entry_v.data()), size * 4, error);
170 
171   if ((ret != (size * 4)) || !error.Success()) {
172     result.SetError("Unsuccessful. Failed access to BT entry.");
173     result.SetStatus(lldb::eReturnStatusFailed);
174     return false;
175   }
176 
177   lldb::addr_t lbound;
178   lldb::addr_t ubound;
179   uint64_t value;
180   uint64_t meta;
181   lldb::SBData data;
182   data.SetData(error, bt_entry_v.data(), bt_entry_v.size(),
183                target.GetByteOrder(), size);
184   lbound = data.GetAddress(error, size * 0);
185   ubound = data.GetAddress(error, size * 1);
186   value = data.GetAddress(error, size * 2);
187   meta = data.GetAddress(error, size * 3);
188   // ubound is stored as one's complement.
189   if (arch == llvm::Triple::ArchType::x86) {
190     ubound = (~ubound) & 0x00000000FFFFFFFF;
191   } else {
192     ubound = ~ubound;
193   }
194 
195   if (!error.Success()) {
196     result.SetError("Failed access to BT entry.");
197     return false;
198   }
199 
200   PrintBTEntry(lbound, ubound, value, meta, result);
201 
202   result.SetStatus(lldb::eReturnStatusSuccessFinishResult);
203   return true;
204 }
205 
uIntToU8(uint64_t input,size_t size)206 static std::vector<uint8_t> uIntToU8(uint64_t input, size_t size) {
207   std::vector<uint8_t> output;
208   for (size_t i = 0; i < size; i++)
209     output.push_back(
210         static_cast<uint8_t>((input & (0xFFULL << (i * 8))) >> (i * 8)));
211 
212   return output;
213 }
214 
SetBTEntry(uint64_t bndcfgu,uint64_t ptr,lldb::addr_t lbound,lldb::addr_t ubound,lldb::SBTarget & target,llvm::Triple::ArchType arch,lldb::SBCommandReturnObject & result,lldb::SBError & error)215 static bool SetBTEntry(uint64_t bndcfgu, uint64_t ptr, lldb::addr_t lbound,
216                        lldb::addr_t ubound, lldb::SBTarget &target,
217                        llvm::Triple::ArchType arch,
218                        lldb::SBCommandReturnObject &result,
219                        lldb::SBError &error) {
220   lldb::addr_t bt_entry_addr;
221   size_t size;
222 
223   if (!GetBTEntryAddr(bndcfgu, ptr, target, arch, size, bt_entry_addr, result,
224                       error))
225     return false;
226 
227   // bt_entry_v must have space to store only 2 elements of the BT Entry, the
228   // lower boundary and the upper boundary, which both have size 'size'.
229   //
230   std::vector<uint8_t> bt_entry_v(size * 2);
231 
232   std::vector<uint8_t> lbound_v = uIntToU8(lbound, size);
233   bt_entry_v.insert(bt_entry_v.begin(), lbound_v.begin(), lbound_v.end());
234   std::vector<uint8_t> ubound_v = uIntToU8(~ubound, size);
235   bt_entry_v.insert(bt_entry_v.begin() + size, ubound_v.begin(),
236                     ubound_v.end());
237 
238   size_t ret = target.GetProcess().WriteMemory(
239       bt_entry_addr, (void *)(bt_entry_v.data()), size * 2, error);
240   if ((ret != (size * 2)) || !error.Success()) {
241     result.SetError("Failed access to BT entry.");
242     result.SetStatus(lldb::eReturnStatusFailed);
243     return false;
244   }
245 
246   result.SetStatus(lldb::eReturnStatusSuccessFinishResult);
247   return true;
248 }
249 
GetInitInfo(lldb::SBDebugger debugger,lldb::SBTarget & target,llvm::Triple::ArchType & arch,uint64_t & bndcfgu,char * arg,uint64_t & ptr,lldb::SBCommandReturnObject & result,lldb::SBError & error)250 static bool GetInitInfo(lldb::SBDebugger debugger, lldb::SBTarget &target,
251                         llvm::Triple::ArchType &arch, uint64_t &bndcfgu,
252                         char *arg, uint64_t &ptr,
253                         lldb::SBCommandReturnObject &result,
254                         lldb::SBError &error) {
255   target = debugger.GetSelectedTarget();
256   if (!target.IsValid()) {
257     result.SetError("Invalid target.");
258     result.SetStatus(lldb::eReturnStatusFailed);
259     return false;
260   }
261 
262   const std::string triple_s(target.GetTriple());
263   const llvm::Triple triple(triple_s);
264 
265   arch = triple.getArch();
266 
267   if ((arch != llvm::Triple::ArchType::x86) &&
268       (arch != llvm::Triple::ArchType::x86_64)) {
269     result.SetError("Platform not supported.");
270     result.SetStatus(lldb::eReturnStatusFailed);
271     return false;
272   }
273 
274   lldb::SBFrame frame =
275       target.GetProcess().GetSelectedThread().GetSelectedFrame();
276   if (!frame.IsValid()) {
277     result.SetError("No valid process, thread or frame.");
278     result.SetStatus(lldb::eReturnStatusFailed);
279     return false;
280   }
281 
282   lldb::SBValue bndcfgu_val = frame.FindRegister("bndcfgu");
283   if (!bndcfgu_val.IsValid()) {
284     result.SetError("Cannot access register BNDCFGU. Does the target support "
285                     "Intel(R) Memory Protection Extensions (Intel(R) MPX)?");
286     result.SetStatus(lldb::eReturnStatusFailed);
287     return false;
288   }
289 
290   lldb::SBData bndcfgu_data = bndcfgu_val.GetData();
291   bndcfgu = bndcfgu_data.GetUnsignedInt64(error, 0);
292   if (!error.Success()) {
293     result.SetError(error, "Invalid read of register BNDCFGU.");
294     return false;
295   }
296 
297   if (!GetPtr(arg, ptr, frame, result))
298     return false;
299 
300   return true;
301 }
302 
303 class MPXTableShow : public lldb::SBCommandPluginInterface {
304 public:
DoExecute(lldb::SBDebugger debugger,char ** command,lldb::SBCommandReturnObject & result)305   bool DoExecute(lldb::SBDebugger debugger, char **command,
306                  lldb::SBCommandReturnObject &result) override {
307 
308     if (command) {
309       int arg_c = 0;
310       char *arg;
311 
312       while (*command) {
313         if (arg_c >= 1) {
314           result.SetError("Too many arguments. See help.");
315           result.SetStatus(lldb::eReturnStatusFailed);
316           return false;
317         }
318         arg_c++;
319         arg = *command;
320         command++;
321       }
322 
323       if (!debugger.IsValid()) {
324         result.SetError("Invalid debugger.");
325         result.SetStatus(lldb::eReturnStatusFailed);
326         return false;
327       }
328 
329       lldb::SBTarget target;
330       llvm::Triple::ArchType arch;
331       lldb::SBError error;
332       uint64_t bndcfgu;
333       uint64_t ptr;
334 
335       if (!GetInitInfo(debugger, target, arch, bndcfgu, arg, ptr, result,
336                        error))
337         return false;
338 
339       return GetBTEntry(bndcfgu, ptr, target, arch, result, error);
340     }
341 
342     result.SetError("Too few arguments. See help.");
343     result.SetStatus(lldb::eReturnStatusFailed);
344     return false;
345   }
346 };
347 
348 class MPXTableSet : public lldb::SBCommandPluginInterface {
349 public:
DoExecute(lldb::SBDebugger debugger,char ** command,lldb::SBCommandReturnObject & result)350   bool DoExecute(lldb::SBDebugger debugger, char **command,
351                  lldb::SBCommandReturnObject &result) override {
352 
353     if (command) {
354       int arg_c = 0;
355       char *arg[3];
356 
357       while (*command) {
358         arg[arg_c] = *command;
359         command++;
360         arg_c++;
361       }
362 
363       if (arg_c != 3) {
364         result.SetError("Wrong arguments. See help.");
365         return false;
366       }
367 
368       if (!debugger.IsValid()) {
369         result.SetError("Invalid debugger.");
370         return false;
371       }
372 
373       lldb::SBTarget target;
374       llvm::Triple::ArchType arch;
375       lldb::SBError error;
376       uint64_t bndcfgu;
377       uint64_t ptr;
378 
379       if (!GetInitInfo(debugger, target, arch, bndcfgu, arg[0], ptr, result,
380                        error))
381         return false;
382 
383       char *endptr;
384       errno = 0;
385       uint64_t lbound = std::strtoul(arg[1], &endptr, 16);
386       if (endptr == arg[1] || errno == ERANGE) {
387         result.SetError("Lower Bound: bad argument format.");
388         errno = 0;
389         return false;
390       }
391 
392       uint64_t ubound = std::strtoul(arg[2], &endptr, 16);
393       if (endptr == arg[1] || errno == ERANGE) {
394         result.SetError("Upper Bound: bad argument format.");
395         errno = 0;
396         return false;
397       }
398 
399       return SetBTEntry(bndcfgu, ptr, lbound, ubound, target, arch, result,
400                         error);
401     }
402 
403     result.SetError("Too few arguments. See help.");
404     return false;
405   }
406 };
407 
MPXPluginInitialize(lldb::SBDebugger & debugger)408 bool MPXPluginInitialize(lldb::SBDebugger &debugger) {
409   lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter();
410   lldb::SBCommand mpxTable = interpreter.AddMultiwordCommand(
411       "mpx-table", "A utility to access the Intel(R) MPX table entries.");
412 
413   const char *mpx_show_help = "Show the Intel(R) MPX table entry of a pointer."
414                               "\nmpx-table show <pointer>";
415   mpxTable.AddCommand("show", new MPXTableShow(), mpx_show_help);
416 
417   const char *mpx_set_help =
418       "Set the Intel(R) MPX table entry of a pointer.\n"
419       "mpx-table set <pointer> <lower bound> <upper bound>";
420   mpxTable.AddCommand("set", new MPXTableSet(), mpx_set_help);
421 
422   return true;
423 }
424