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