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