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 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 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 || lbound == one_cmpl32) && ubound == 0) { 67 result.Printf("Null bounds on map: pointer value = 0x%" PRIu64 "\n", value); 68 } else { 69 result.Printf(" lbound = 0x%" PRIu64 ",", lbound); 70 result.Printf(" ubound = 0x%" PRIu64 , ubound); 71 result.Printf(" (pointer value = 0x%" PRIu64 ",", value); 72 result.Printf(" metadata = 0x%" PRIu64 ")\n", meta); 73 } 74 } 75 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 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 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 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 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: 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: 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 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