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