Skip to content

Commit

Permalink
[Android Unwinder] Add support for refuse to unwind
Browse files Browse the repository at this point in the history
This CL adds the support for unwind instruction
10000000 00000000.

Bug: 1240698
Change-Id: Ie8b04daf034631518c4d13ab5471f22db4983b87
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3153978
Commit-Queue: Charlie Hu <chenleihu@google.com>
Reviewed-by: Mike Wittman <wittman@chromium.org>
Cr-Commit-Position: refs/heads/main@{#921309}
  • Loading branch information
Charlie Hu authored and Chromium LUCI CQ committed Sep 14, 2021
1 parent 8483232 commit 82c2ed3
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 40 deletions.
28 changes: 15 additions & 13 deletions base/profiler/chrome_unwind_table_android.cc
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ UnwindInstructionResult ExecuteUnwindInstruction(
CheckedNumeric<uintptr_t>(RegisterContextStackPointer(thread_context)) +
offset;
if (!new_sp.AssignIfValid(&RegisterContextStackPointer(thread_context))) {
return UnwindInstructionResult::STACK_POINTER_OUT_OF_BOUNDS;
return UnwindInstructionResult::kAborted;
}
} else if (GetTopBits(*instruction, 2) == 0b01) {
// 01xxxxxx
Expand All @@ -89,7 +89,7 @@ UnwindInstructionResult ExecuteUnwindInstruction(
CheckedNumeric<uintptr_t>(RegisterContextStackPointer(thread_context)) -
offset;
if (!new_sp.AssignIfValid(&RegisterContextStackPointer(thread_context))) {
return UnwindInstructionResult::STACK_POINTER_OUT_OF_BOUNDS;
return UnwindInstructionResult::kAborted;
}
} else if (GetTopBits(*instruction, 4) == 0b1001) {
// 1001nnnn (nnnn != 13,15)
Expand All @@ -109,25 +109,27 @@ UnwindInstructionResult ExecuteUnwindInstruction(
const uint8_t max_register_index = (*instruction++ & 0b00000111) + 4;
for (int n = 4; n <= max_register_index; n++) {
if (!PopRegister(thread_context, n)) {
return UnwindInstructionResult::STACK_POINTER_OUT_OF_BOUNDS;
return UnwindInstructionResult::kAborted;
}
}
if (!PopRegister(thread_context, 14)) {
return UnwindInstructionResult::STACK_POINTER_OUT_OF_BOUNDS;
return UnwindInstructionResult::kAborted;
}
} else if (*instruction == 0b10000000 && *(instruction + 1) == 0) {
// 10000000 00000000
// Refuse to unwind.
instruction += 2;
return UnwindInstructionResult::kAborted;
} else if (GetTopBits(*instruction, 4) == 0b1000) {
// 1000iiii iiiiiiii
// Pop up to 12 integer registers under masks {r15-r12}, {r11-r4}
const uint32_t register_bitmask =
((*instruction & 0xf) << 8) + *(instruction + 1);
// 10000000 00000000 is reserved for 'Refused to unwind'.
DCHECK_NE(register_bitmask, 0ul);
instruction += 2;

// 1000iiii iiiiiiii
// Pop up to 12 integer registers under masks {r15-r12}, {r11-r4}
for (int register_index = 4; register_index < 16; register_index++) {
if (register_bitmask & (1 << (register_index - 4))) {
if (!PopRegister(thread_context, register_index)) {
return UnwindInstructionResult::STACK_POINTER_OUT_OF_BOUNDS;
return UnwindInstructionResult::kAborted;
}
}
}
Expand All @@ -145,7 +147,7 @@ UnwindInstructionResult ExecuteUnwindInstruction(
if (!pc_was_updated)
thread_context->arm_pc = thread_context->arm_lr;

return UnwindInstructionResult::COMPLETED;
return UnwindInstructionResult::kCompleted;
} else if (*instruction == 0b10110010) {
// 10110010 uleb128
// vsp = vsp + 0x204 + (uleb128 << 2)
Expand All @@ -156,12 +158,12 @@ UnwindInstructionResult ExecuteUnwindInstruction(
(CheckedNumeric<uintptr_t>(DecodeULEB128(instruction)) << 2) + 0x204;

if (!new_sp.AssignIfValid(&RegisterContextStackPointer(thread_context))) {
return UnwindInstructionResult::STACK_POINTER_OUT_OF_BOUNDS;
return UnwindInstructionResult::kAborted;
}
} else {
NOTREACHED();
}
return UnwindInstructionResult::INSTRUCTION_PENDING;
return UnwindInstructionResult::kInstructionPending;
}

const uint8_t* GetFirstUnwindInstructionFromFunctionOffsetTableIndex(
Expand Down
9 changes: 4 additions & 5 deletions base/profiler/chrome_unwind_table_android.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,10 @@

namespace base {

enum UnwindInstructionResult {
COMPLETED, // Signals the end of unwind process.
INSTRUCTION_PENDING, // Continues to unwind next instruction.
STACK_POINTER_OUT_OF_BOUNDS, // Stack pointer is out of bounds after
// execution of unwind instruction.
enum class UnwindInstructionResult {
kCompleted, // Signals the end of unwind process.
kInstructionPending, // Continues to unwind next instruction.
kAborted, // Unable to unwind.
};

// Execute a single unwind instruction on the given thread_context,
Expand Down
58 changes: 36 additions & 22 deletions base/profiler/chrome_unwind_table_android_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ TEST(ChromeAndroidUnwindInstructionTest,
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::INSTRUCTION_PENDING);
UnwindInstructionResult::kInstructionPending);
EXPECT_FALSE(pc_was_updated);
ASSERT_EQ(current_instruction, &instruction + 1);
EXPECT_EQ(0x10000004ul, thread_context.arm_sp);
Expand All @@ -35,7 +35,7 @@ TEST(ChromeAndroidUnwindInstructionTest,
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::INSTRUCTION_PENDING);
UnwindInstructionResult::kInstructionPending);
EXPECT_FALSE(pc_was_updated);
ASSERT_EQ(current_instruction, &instruction + 1);
EXPECT_EQ(0x10000014ul, thread_context.arm_sp);
Expand All @@ -50,7 +50,7 @@ TEST(ChromeAndroidUnwindInstructionTest,
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::INSTRUCTION_PENDING);
UnwindInstructionResult::kInstructionPending);
EXPECT_FALSE(pc_was_updated);
ASSERT_EQ(current_instruction, &instruction + 1);
EXPECT_EQ(0x10000100ul, thread_context.arm_sp);
Expand All @@ -65,7 +65,7 @@ TEST(ChromeAndroidUnwindInstructionTest,
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::STACK_POINTER_OUT_OF_BOUNDS);
UnwindInstructionResult::kAborted);
ASSERT_EQ(current_instruction, &instruction + 1);
EXPECT_EQ(0xffffffff, thread_context.arm_sp);
}
Expand All @@ -79,7 +79,7 @@ TEST(ChromeAndroidUnwindInstructionTest,
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::INSTRUCTION_PENDING);
UnwindInstructionResult::kInstructionPending);
EXPECT_FALSE(pc_was_updated);
ASSERT_EQ(current_instruction, &instruction + 1);
EXPECT_EQ(0x0ffffffcul, thread_context.arm_sp);
Expand All @@ -95,7 +95,7 @@ TEST(ChromeAndroidUnwindInstructionTest,
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::INSTRUCTION_PENDING);
UnwindInstructionResult::kInstructionPending);
EXPECT_FALSE(pc_was_updated);
ASSERT_EQ(current_instruction, &instruction + 1);
EXPECT_EQ(0x0fffffecul, thread_context.arm_sp);
Expand All @@ -110,7 +110,7 @@ TEST(ChromeAndroidUnwindInstructionTest,
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::INSTRUCTION_PENDING);
UnwindInstructionResult::kInstructionPending);
EXPECT_FALSE(pc_was_updated);
ASSERT_EQ(current_instruction, &instruction + 1);
EXPECT_EQ(0x0fffff00ul, thread_context.arm_sp);
Expand All @@ -125,7 +125,7 @@ TEST(ChromeAndroidUnwindInstructionTest,
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::STACK_POINTER_OUT_OF_BOUNDS);
UnwindInstructionResult::kAborted);
EXPECT_FALSE(pc_was_updated);
ASSERT_EQ(current_instruction, &instruction + 1);
EXPECT_EQ(0x0ul, thread_context.arm_sp);
Expand Down Expand Up @@ -168,7 +168,7 @@ TEST_P(ChromeAndroidUnwindSetStackPointerFromRegisterValueTest,
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::INSTRUCTION_PENDING);
UnwindInstructionResult::kInstructionPending);
EXPECT_FALSE(pc_was_updated);
ASSERT_EQ(current_instruction, &instruction + 1);
EXPECT_EQ(100ul + register_index, thread_context.arm_sp);
Expand All @@ -183,7 +183,7 @@ TEST(ChromeAndroidUnwindInstructionTest, TestCompleteWithNoPriorPCUpdate) {
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::COMPLETED);
UnwindInstructionResult::kCompleted);
ASSERT_EQ(current_instruction, &instruction + 1);
EXPECT_EQ(114ul, thread_context.arm_pc);
}
Expand All @@ -197,7 +197,7 @@ TEST(ChromeAndroidUnwindInstructionTest, TestCompleteWithPriorPCUpdate) {
bool pc_was_updated = true;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::COMPLETED);
UnwindInstructionResult::kCompleted);
ASSERT_EQ(current_instruction, &instruction + 1);
EXPECT_EQ(115ul, thread_context.arm_pc);
}
Expand Down Expand Up @@ -233,7 +233,7 @@ TEST(ChromeAndroidUnwindInstructionTest,
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::INSTRUCTION_PENDING);
UnwindInstructionResult::kInstructionPending);
EXPECT_TRUE(pc_was_updated);
ASSERT_EQ(current_instruction, instruction + 2);
EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack[0] + 4), thread_context.arm_sp);
Expand Down Expand Up @@ -285,7 +285,7 @@ TEST(ChromeAndroidUnwindInstructionTest, TestPopDiscontinuousRegisters) {
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::INSTRUCTION_PENDING);
UnwindInstructionResult::kInstructionPending);
EXPECT_FALSE(pc_was_updated);
ASSERT_EQ(current_instruction, instruction + 2);
EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack[0] + 3), thread_context.arm_sp);
Expand Down Expand Up @@ -336,7 +336,7 @@ TEST(ChromeAndroidUnwindInstructionTest,
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::STACK_POINTER_OUT_OF_BOUNDS);
UnwindInstructionResult::kAborted);
EXPECT_FALSE(pc_was_updated);
ASSERT_EQ(current_instruction, instruction + 2);
EXPECT_EQ(0xffffffff, thread_context.arm_sp);
Expand All @@ -358,6 +358,20 @@ TEST(ChromeAndroidUnwindInstructionTest,
EXPECT_EQ(114ul, thread_context.arm_pc);
}

TEST(ChromeAndroidUnwindInstructionTest, TestRefuseToUnwind) {
RegisterContext thread_context = {};

const uint8_t instruction[] = {0b10000000, 0b0};
const uint8_t* current_instruction = instruction;

bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::kAborted);
EXPECT_FALSE(pc_was_updated);
ASSERT_EQ(current_instruction, instruction + 2);
}

TEST(ChromeAndroidUnwindInstructionTest,
TestPopRegistersIncludingR14MinRegisters) {
RegisterContext thread_context = {};
Expand Down Expand Up @@ -387,7 +401,7 @@ TEST(ChromeAndroidUnwindInstructionTest,
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::INSTRUCTION_PENDING);
UnwindInstructionResult::kInstructionPending);
EXPECT_FALSE(pc_was_updated);
ASSERT_EQ(current_instruction, &instruction + 1);
EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack[0] + 2), thread_context.arm_sp);
Expand Down Expand Up @@ -437,7 +451,7 @@ TEST(ChromeAndroidUnwindInstructionTest,
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::INSTRUCTION_PENDING);
UnwindInstructionResult::kInstructionPending);
EXPECT_FALSE(pc_was_updated);
ASSERT_EQ(current_instruction, &instruction + 1);
EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack[0] + 6), thread_context.arm_sp);
Expand Down Expand Up @@ -487,7 +501,7 @@ TEST(ChromeAndroidUnwindInstructionTest,
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::INSTRUCTION_PENDING);
UnwindInstructionResult::kInstructionPending);
EXPECT_FALSE(pc_was_updated);
ASSERT_EQ(current_instruction, &instruction + 1);
EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack[0] + 9), thread_context.arm_sp);
Expand Down Expand Up @@ -534,7 +548,7 @@ TEST(ChromeAndroidUnwindInstructionTest, TestPopRegistersIncludingR14Overflow) {
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::STACK_POINTER_OUT_OF_BOUNDS);
UnwindInstructionResult::kAborted);
EXPECT_FALSE(pc_was_updated);
ASSERT_EQ(current_instruction, &instruction + 1);
EXPECT_EQ(0xffffffff, thread_context.arm_sp);
Expand Down Expand Up @@ -567,7 +581,7 @@ TEST(ChromeAndroidUnwindInstructionTest, TestBigStackPointerIncrementMinValue) {
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::INSTRUCTION_PENDING);
UnwindInstructionResult::kInstructionPending);
EXPECT_FALSE(pc_was_updated);
ASSERT_EQ(current_instruction, increment_0 + sizeof(increment_0));
// vsp + 0x204 + (0 << 2)
Expand All @@ -591,7 +605,7 @@ TEST(ChromeAndroidUnwindInstructionTest, TestBigStackPointerIncrementMidValue) {
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::INSTRUCTION_PENDING);
UnwindInstructionResult::kInstructionPending);
EXPECT_FALSE(pc_was_updated);
ASSERT_EQ(current_instruction, increment_4 + sizeof(increment_4));
EXPECT_EQ(0x10000214ul, thread_context.arm_sp);
Expand All @@ -615,7 +629,7 @@ TEST(ChromeAndroidUnwindInstructionTest,
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::INSTRUCTION_PENDING);
UnwindInstructionResult::kInstructionPending);
EXPECT_FALSE(pc_was_updated);
ASSERT_EQ(current_instruction, increment_128 + sizeof(increment_128));
EXPECT_EQ(0x10000404ul, thread_context.arm_sp);
Expand All @@ -634,7 +648,7 @@ TEST(ChromeAndroidUnwindInstructionTest, TestBigStackPointerIncrementOverflow) {
bool pc_was_updated = false;
ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
&thread_context),
UnwindInstructionResult::STACK_POINTER_OUT_OF_BOUNDS);
UnwindInstructionResult::kAborted);
EXPECT_FALSE(pc_was_updated);
ASSERT_EQ(current_instruction,
increment_overflow + sizeof(increment_overflow));
Expand Down

0 comments on commit 82c2ed3

Please sign in to comment.