1""" 2Check that lldb features work when the AArch64 Guarded Control Stack (GCS) 3extension is enabled. 4""" 5 6import lldb 7from lldbsuite.test.decorators import * 8from lldbsuite.test.lldbtest import * 9from lldbsuite.test import lldbutil 10 11 12class AArch64LinuxGCSTestCase(TestBase): 13 NO_DEBUG_INFO_TESTCASE = True 14 15 @skipUnlessArch("aarch64") 16 @skipUnlessPlatform(["linux"]) 17 def test_gcs_region(self): 18 if not self.isAArch64GCS(): 19 self.skipTest("Target must support GCS.") 20 21 # This test assumes that we have /proc/<PID>/smaps files 22 # that include "VmFlags:" lines. 23 # AArch64 kernel config defaults to enabling smaps with 24 # PROC_PAGE_MONITOR and "VmFlags" was added in kernel 3.8, 25 # before GCS was supported at all. 26 27 self.build() 28 self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) 29 30 lldbutil.run_break_set_by_file_and_line( 31 self, 32 "main.c", 33 line_number("main.c", "// Set break point at this line."), 34 num_expected_locations=1, 35 ) 36 37 self.runCmd("run", RUN_SUCCEEDED) 38 39 if self.process().GetState() == lldb.eStateExited: 40 self.fail("Test program failed to run.") 41 42 self.expect( 43 "thread list", 44 STOPPED_DUE_TO_BREAKPOINT, 45 substrs=["stopped", "stop reason = breakpoint"], 46 ) 47 48 # By now either the program or the system C library enabled GCS and there 49 # should be one region marked for use by it (we cannot predict exactly 50 # where it will be). 51 self.runCmd("memory region --all") 52 found_ss = False 53 for line in self.res.GetOutput().splitlines(): 54 if line.strip() == "shadow stack: yes": 55 if found_ss: 56 self.fail("Found more than one shadow stack region.") 57 found_ss = True 58 59 self.assertTrue(found_ss, "Failed to find a shadow stack region.") 60 61 # Note that we must let the debugee get killed here as it cannot exit 62 # cleanly if GCS was manually enabled. 63 64 @skipUnlessArch("aarch64") 65 @skipUnlessPlatform(["linux"]) 66 def test_gcs_fault(self): 67 if not self.isAArch64GCS(): 68 self.skipTest("Target must support GCS.") 69 70 self.build() 71 self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) 72 self.runCmd("run", RUN_SUCCEEDED) 73 74 if self.process().GetState() == lldb.eStateExited: 75 self.fail("Test program failed to run.") 76 77 self.expect( 78 "thread list", 79 "Expected stopped by SIGSEGV.", 80 substrs=[ 81 "stopped", 82 "stop reason = signal SIGSEGV: control protection fault", 83 ], 84 ) 85 86 # This helper reads all the GCS registers and optionally compares them 87 # against a previous state, then returns the current register values. 88 def check_gcs_registers( 89 self, 90 expected_gcs_features_enabled=None, 91 expected_gcs_features_locked=None, 92 expected_gcspr_el0=None, 93 ): 94 thread = self.dbg.GetSelectedTarget().process.GetThreadAtIndex(0) 95 registerSets = thread.GetFrameAtIndex(0).GetRegisters() 96 gcs_registers = registerSets.GetFirstValueByName( 97 r"Guarded Control Stack Registers" 98 ) 99 100 gcs_features_enabled = gcs_registers.GetChildMemberWithName( 101 "gcs_features_enabled" 102 ).GetValueAsUnsigned() 103 if expected_gcs_features_enabled is not None: 104 self.assertEqual(expected_gcs_features_enabled, gcs_features_enabled) 105 106 gcs_features_locked = gcs_registers.GetChildMemberWithName( 107 "gcs_features_locked" 108 ).GetValueAsUnsigned() 109 if expected_gcs_features_locked is not None: 110 self.assertEqual(expected_gcs_features_locked, gcs_features_locked) 111 112 gcspr_el0 = gcs_registers.GetChildMemberWithName( 113 "gcspr_el0" 114 ).GetValueAsUnsigned() 115 if expected_gcspr_el0 is not None: 116 self.assertEqual(expected_gcspr_el0, gcspr_el0) 117 118 return gcs_features_enabled, gcs_features_locked, gcspr_el0 119 120 @skipUnlessArch("aarch64") 121 @skipUnlessPlatform(["linux"]) 122 def test_gcs_registers(self): 123 if not self.isAArch64GCS(): 124 self.skipTest("Target must support GCS.") 125 126 self.build() 127 self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) 128 129 self.runCmd("b test_func") 130 self.runCmd("b test_func2") 131 self.runCmd("run", RUN_SUCCEEDED) 132 133 if self.process().GetState() == lldb.eStateExited: 134 self.fail("Test program failed to run.") 135 136 self.expect( 137 "thread list", 138 STOPPED_DUE_TO_BREAKPOINT, 139 substrs=["stopped", "stop reason = breakpoint"], 140 ) 141 142 self.expect("register read --all", substrs=["Guarded Control Stack Registers:"]) 143 144 enabled, locked, spr_el0 = self.check_gcs_registers() 145 146 # Features enabled should have at least the enable bit set, it could have 147 # others depending on what the C library did, but we can't rely on always 148 # having them. 149 self.assertTrue(enabled & 1, "Expected GCS enable bit to be set.") 150 151 # Features locked we cannot predict, we will just assert that it remains 152 # the same as we continue. 153 154 # spr_el0 will point to some memory region that is a shadow stack region. 155 self.expect(f"memory region {spr_el0}", substrs=["shadow stack: yes"]) 156 157 # Continue into test_func2, where the GCS pointer should have been 158 # decremented, and the other registers remain the same. 159 self.runCmd("continue") 160 161 self.expect( 162 "thread list", 163 STOPPED_DUE_TO_BREAKPOINT, 164 substrs=["stopped", "stop reason = breakpoint"], 165 ) 166 167 _, _, spr_el0 = self.check_gcs_registers(enabled, locked, spr_el0 - 8) 168 169 # Any combination of GCS feature lock bits might have been set by the C 170 # library, and could be set to 0 or 1. To check that we can modify them, 171 # invert one of those bits then write it back to the lock register. 172 # The stack pushing feature is bit 2 of that register. 173 STACK_PUSH = 2 174 # Get the original value of the stack push lock bit. 175 stack_push = bool((locked >> STACK_PUSH) & 1) 176 # Invert the value and put it back into the set of lock bits. 177 new_locked = (locked & ~(1 << STACK_PUSH)) | (int(not stack_push) << STACK_PUSH) 178 # Write the new lock bits, which are the same as before, only with stack 179 # push locked (if it was previously unlocked), or unlocked (if it was 180 # previously locked). 181 self.runCmd(f"register write gcs_features_locked 0x{new_locked:x}") 182 # We should be able to read back this new set of lock bits. 183 self.expect( 184 f"register read gcs_features_locked", 185 substrs=[f"gcs_features_locked = 0x{new_locked:016x}"], 186 ) 187 188 # We could prove the write made it to hardware by trying to prctl() to 189 # enable or disable the stack push feature here, but because the libc 190 # may or may not have locked it, it's tricky to coordinate this. Given 191 # that we know the other registers can be written and their values are 192 # seen by the process, we can assume this is too. 193 194 # Restore the original lock bits, as the libc may rely on being able 195 # to use certain features during program execution. 196 self.runCmd(f"register write gcs_features_locked 0x{locked:x}") 197 198 # Modify the guarded control stack pointer to cause a fault. 199 spr_el0 += 8 200 self.runCmd(f"register write gcspr_el0 {spr_el0}") 201 self.expect( 202 "register read gcspr_el0", substrs=[f"gcspr_el0 = 0x{spr_el0:016x}"] 203 ) 204 205 # If we wrote it back correctly, we will now fault. Don't pass this signal 206 # to the application, as we will continue past it later. 207 self.runCmd("process handle SIGSEGV --pass false") 208 self.runCmd("continue") 209 210 self.expect( 211 "thread list", 212 "Expected stopped by SIGSEGV.", 213 substrs=[ 214 "stopped", 215 "stop reason = signal SIGSEGV: control protection fault", 216 ], 217 ) 218 219 # Now to prove we can write gcs_features_enabled, disable GCS and continue 220 # past the fault we caused. Note that although the libc likely locked the 221 # ability to disable GCS, ptrace bypasses the lock bits. 222 enabled &= ~1 223 self.runCmd(f"register write gcs_features_enabled {enabled}") 224 self.expect( 225 "register read gcs_features_enabled", 226 substrs=[ 227 f"gcs_features_enabled = 0x{enabled:016x}", 228 f"= (PUSH = {(enabled >> 2) & 1}, WRITE = {(enabled >> 1) & 1}, ENABLE = {enabled & 1})", 229 ], 230 ) 231 232 # With GCS disabled, the invalid guarded control stack pointer is not 233 # checked, so the program can finish normally. 234 self.runCmd("continue") 235 self.expect( 236 "process status", 237 substrs=[ 238 "exited with status = 0", 239 ], 240 ) 241 242 @skipUnlessPlatform(["linux"]) 243 def test_gcs_expression_simple(self): 244 if not self.isAArch64GCS(): 245 self.skipTest("Target must support GCS.") 246 247 self.build() 248 self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) 249 250 # Break before GCS has been enabled. 251 self.runCmd("b main") 252 # And after it has been enabled. 253 lldbutil.run_break_set_by_file_and_line( 254 self, 255 "main.c", 256 line_number("main.c", "// Set break point at this line."), 257 num_expected_locations=1, 258 ) 259 260 self.runCmd("run", RUN_SUCCEEDED) 261 262 if self.process().GetState() == lldb.eStateExited: 263 self.fail("Test program failed to run.") 264 265 self.expect( 266 "thread list", 267 STOPPED_DUE_TO_BREAKPOINT, 268 substrs=["stopped", "stop reason = breakpoint"], 269 ) 270 271 # GCS has not been enabled yet and the ABI plugin should know not to 272 # attempt pushing to the control stack. 273 before = self.check_gcs_registers() 274 expr_cmd = "p get_gcs_status()" 275 self.expect(expr_cmd, substrs=["(unsigned long) 0"]) 276 self.check_gcs_registers(*before) 277 278 # Continue to when GCS has been enabled. 279 self.runCmd("continue") 280 self.expect( 281 "thread list", 282 STOPPED_DUE_TO_BREAKPOINT, 283 substrs=["stopped", "stop reason = breakpoint"], 284 ) 285 286 # If we fail to setup the GCS entry, we should not leave any of the GCS registers 287 # changed. The last thing we do is write a new GCS entry to memory and 288 # to simulate the failure of that, temporarily point the GCS to the zero page. 289 # 290 # We use the value 8 here because LLDB will decrement it by 8 so it points to 291 # what we think will be an empty entry on the guarded control stack. 292 _, _, original_gcspr = self.check_gcs_registers() 293 self.runCmd("register write gcspr_el0 8") 294 before = self.check_gcs_registers() 295 self.expect(expr_cmd, error=True) 296 self.check_gcs_registers(*before) 297 # Point to the valid shadow stack region again. 298 self.runCmd(f"register write gcspr_el0 {original_gcspr}") 299 300 # This time we do need to push to the GCS and having done so, we can 301 # return from this expression without causing a fault. 302 before = self.check_gcs_registers() 303 self.expect(expr_cmd, substrs=["(unsigned long) 1"]) 304 self.check_gcs_registers(*before) 305 306 @skipUnlessPlatform(["linux"]) 307 def test_gcs_expression_disable_gcs(self): 308 if not self.isAArch64GCS(): 309 self.skipTest("Target must support GCS.") 310 311 self.build() 312 self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) 313 314 # Break after GCS is enabled. 315 lldbutil.run_break_set_by_file_and_line( 316 self, 317 "main.c", 318 line_number("main.c", "// Set break point at this line."), 319 num_expected_locations=1, 320 ) 321 322 self.runCmd("run", RUN_SUCCEEDED) 323 324 if self.process().GetState() == lldb.eStateExited: 325 self.fail("Test program failed to run.") 326 327 self.expect( 328 "thread list", 329 STOPPED_DUE_TO_BREAKPOINT, 330 substrs=["stopped", "stop reason = breakpoint"], 331 ) 332 333 # Unlock all features so the expression can enable them again. 334 self.runCmd("register write gcs_features_locked 0") 335 # Disable all features, but keep GCS itself enabled. 336 PR_SHADOW_STACK_ENABLE = 1 337 self.runCmd(f"register write gcs_features_enabled 0x{PR_SHADOW_STACK_ENABLE:x}") 338 339 enabled, locked, spr_el0 = self.check_gcs_registers() 340 # We restore everything apart GCS being enabled, as we are not allowed to 341 # go from disabled -> enabled via ptrace. 342 self.expect("p change_gcs_config(false)", substrs=["true"]) 343 enabled &= ~1 344 self.check_gcs_registers(enabled, locked, spr_el0) 345 346 @skipUnlessPlatform(["linux"]) 347 def test_gcs_expression_enable_gcs(self): 348 if not self.isAArch64GCS(): 349 self.skipTest("Target must support GCS.") 350 351 self.build() 352 self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) 353 354 # Break before GCS is enabled. 355 self.runCmd("b main") 356 357 self.runCmd("run", RUN_SUCCEEDED) 358 359 if self.process().GetState() == lldb.eStateExited: 360 self.fail("Test program failed to run.") 361 362 self.expect( 363 "thread list", 364 STOPPED_DUE_TO_BREAKPOINT, 365 substrs=["stopped", "stop reason = breakpoint"], 366 ) 367 368 # Unlock all features so the expression can enable them again. 369 self.runCmd("register write gcs_features_locked 0") 370 # Disable all features. The program needs PR_SHADOW_STACK_PUSH, but it 371 # will enable that itself. 372 self.runCmd(f"register write gcs_features_enabled 0") 373 374 enabled, locked, spr_el0 = self.check_gcs_registers() 375 self.expect("p change_gcs_config(true)", substrs=["true"]) 376 # Though we could disable GCS with ptrace, we choose not to to be 377 # consistent with the disabled -> enabled behaviour. 378 enabled |= 1 379 self.check_gcs_registers(enabled, locked, spr_el0) 380 381 @skipIfLLVMTargetMissing("AArch64") 382 def test_gcs_core_file(self): 383 # To re-generate the core file, build the test file and run it on a 384 # machine with GCS enabled. Note that because the kernel decides where 385 # the GCS is stored, the value of gcspr_el0 and which memory region it 386 # points to may change between runs. 387 388 self.runCmd("target create --core corefile") 389 390 self.expect( 391 "bt", 392 substrs=["stop reason = SIGSEGV: control protection fault"], 393 ) 394 395 self.expect( 396 "register read --all", 397 substrs=[ 398 "Guarded Control Stack Registers:", 399 "gcs_features_enabled = 0x0000000000000001", 400 "gcs_features_locked = 0x0000000000000000", 401 "gcspr_el0 = 0x0000ffffa83ffff0", 402 ], 403 ) 404 405 # Should get register fields for both. They have the same fields. 406 self.expect( 407 "register read gcs_features_enabled", 408 substrs=["= (PUSH = 0, WRITE = 0, ENABLE = 1)"], 409 ) 410 self.expect( 411 "register read gcs_features_locked", 412 substrs=["= (PUSH = 0, WRITE = 0, ENABLE = 0)"], 413 ) 414 415 # Core files do not include /proc/pid/smaps, so we cannot see the 416 # shadow stack "ss" flag. gcspr_el0 should at least point to some mapped 417 # region. 418 self.expect( 419 "memory region $gcspr_el0", 420 substrs=["[0x0000ffffa8000000-0x0000ffffa8400000) rw-"], 421 ) 422