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