Skip to content

Commit a0b674f

Browse files
committed
Fix UB in EmulateInstructionARM64.cpp
This fixes an unhandled signed integer overflow in AddWithCarry() by using the llvm::checkedAdd() function. Thats to Vedant Kumar for the suggestion! <rdar://problem/60926115> Differential Revision: https://reviews.llvm.org/D80955
1 parent a66e1d2 commit a0b674f

File tree

5 files changed

+100
-19
lines changed

5 files changed

+100
-19
lines changed

lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.cpp

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88

99
#include "EmulateInstructionARM64.h"
1010

11-
#include <stdlib.h>
12-
1311
#include "lldb/Core/Address.h"
1412
#include "lldb/Core/PluginManager.h"
1513
#include "lldb/Symbol/UnwindPlan.h"
@@ -18,10 +16,14 @@
1816
#include "lldb/Utility/RegisterValue.h"
1917
#include "lldb/Utility/Stream.h"
2018

19+
#include "llvm/Support/CheckedArithmetic.h"
20+
2121
#include "Plugins/Process/Utility/ARMDefines.h"
2222
#include "Plugins/Process/Utility/ARMUtils.h"
2323
#include "Plugins/Process/Utility/lldb-arm64-register-enums.h"
2424

25+
#include <cstdlib>
26+
2527
#define GPR_OFFSET(idx) ((idx)*8)
2628
#define GPR_OFFSET_NAME(reg) 0
2729
#define FPU_OFFSET(idx) ((idx)*16)
@@ -85,23 +87,6 @@ static inline uint64_t LSL(uint64_t x, integer shift) {
8587
return x << shift;
8688
}
8789

88-
// AddWithCarry()
89-
// ===============
90-
static inline uint64_t
91-
AddWithCarry(uint32_t N, uint64_t x, uint64_t y, bit carry_in,
92-
EmulateInstructionARM64::ProcState &proc_state) {
93-
uint64_t unsigned_sum = UInt(x) + UInt(y) + UInt(carry_in);
94-
int64_t signed_sum = SInt(x) + SInt(y) + UInt(carry_in);
95-
uint64_t result = unsigned_sum;
96-
if (N < 64)
97-
result = Bits64(result, N - 1, 0);
98-
proc_state.N = Bit64(result, N - 1);
99-
proc_state.Z = IsZero(result);
100-
proc_state.C = UInt(result) == unsigned_sum;
101-
proc_state.V = SInt(result) == signed_sum;
102-
return result;
103-
}
104-
10590
// ConstrainUnpredictable()
10691
// ========================
10792

@@ -582,6 +567,24 @@ bool EmulateInstructionARM64::ConditionHolds(const uint32_t cond) {
582567
return result;
583568
}
584569

570+
uint64_t EmulateInstructionARM64::
571+
AddWithCarry(uint32_t N, uint64_t x, uint64_t y, bit carry_in,
572+
EmulateInstructionARM64::ProcState &proc_state) {
573+
uint64_t unsigned_sum = UInt(x) + UInt(y) + UInt(carry_in);
574+
llvm::Optional<int64_t> signed_sum = llvm::checkedAdd(SInt(x), SInt(y));
575+
bool overflow = !signed_sum;
576+
if (!overflow)
577+
overflow |= !llvm::checkedAdd(*signed_sum, SInt(carry_in));
578+
uint64_t result = unsigned_sum;
579+
if (N < 64)
580+
result = Bits64(result, N - 1, 0);
581+
proc_state.N = Bit64(result, N - 1);
582+
proc_state.Z = IsZero(result);
583+
proc_state.C = UInt(result) != unsigned_sum;
584+
proc_state.V = overflow;
585+
return result;
586+
}
587+
585588
bool EmulateInstructionARM64::EmulateADDSUBImm(const uint32_t opcode) {
586589
// integer d = UInt(Rd);
587590
// integer n = UInt(Rn);

lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,9 @@ class EmulateInstructionARM64 : public lldb_private::EmulateInstruction {
152152
} ProcState;
153153

154154
protected:
155+
static uint64_t AddWithCarry(uint32_t N, uint64_t x, uint64_t y, bool carry_in,
156+
EmulateInstructionARM64::ProcState &proc_state);
157+
155158
typedef struct {
156159
uint32_t mask;
157160
uint32_t value;

lldb/unittests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ add_subdirectory(Editline)
7171
add_subdirectory(Expression)
7272
add_subdirectory(Host)
7373
add_subdirectory(Interpreter)
74+
add_subdirectory(Instruction)
7475
add_subdirectory(Language)
7576
add_subdirectory(ObjectFile)
7677
add_subdirectory(Platform)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
if("ARM" IN_LIST LLVM_TARGETS_TO_BUILD)
2+
add_lldb_unittest(EmulatorTests
3+
TestAArch64Emulator.cpp
4+
LINK_LIBS
5+
lldbCore
6+
lldbSymbol
7+
lldbTarget
8+
lldbPluginInstructionARM64
9+
LINK_COMPONENTS
10+
Support
11+
${LLVM_TARGETS_TO_BUILD})
12+
endif()
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//===-- TestAArch64Emulator.cpp ------------------------------------------===//
2+
3+
//
4+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
#include "gtest/gtest.h"
11+
12+
#include "lldb/Core/Address.h"
13+
#include "lldb/Core/Disassembler.h"
14+
#include "lldb/Target/ExecutionContext.h"
15+
#include "lldb/Utility/ArchSpec.h"
16+
17+
#include "Plugins/Instruction/ARM64/EmulateInstructionARM64.h"
18+
19+
using namespace lldb;
20+
using namespace lldb_private;
21+
22+
struct Arch64EmulatorTester : public EmulateInstructionARM64 {
23+
Arch64EmulatorTester()
24+
: EmulateInstructionARM64(ArchSpec("arm64-apple-ios")) {}
25+
26+
static uint64_t AddWithCarry(uint32_t N, uint64_t x, uint64_t y, bool carry_in,
27+
EmulateInstructionARM64::ProcState &proc_state) {
28+
return EmulateInstructionARM64::AddWithCarry(N, x, y, carry_in, proc_state);
29+
}
30+
};
31+
32+
class TestAArch64Emulator : public testing::Test {
33+
public:
34+
static void SetUpTestCase();
35+
static void TearDownTestCase();
36+
37+
protected:
38+
};
39+
40+
void TestAArch64Emulator::SetUpTestCase() {
41+
EmulateInstructionARM64::Initialize();
42+
}
43+
44+
void TestAArch64Emulator::TearDownTestCase() {
45+
EmulateInstructionARM64::Terminate();
46+
}
47+
48+
TEST_F(TestAArch64Emulator, TestOverflow) {
49+
EmulateInstructionARM64::ProcState pstate;
50+
memset(&pstate, 0, sizeof(pstate));
51+
uint64_t ll_max = std::numeric_limits<int64_t>::max();
52+
Arch64EmulatorTester emu;
53+
ASSERT_EQ(emu.AddWithCarry(64, ll_max, 0, 0, pstate), ll_max);
54+
ASSERT_EQ(pstate.V, 0ULL);
55+
ASSERT_EQ(pstate.C, 0ULL);
56+
ASSERT_EQ(emu.AddWithCarry(64, ll_max, 1, 0, pstate), (uint64_t)(ll_max + 1));
57+
ASSERT_EQ(pstate.V, 1ULL);
58+
ASSERT_EQ(pstate.C, 0ULL);
59+
ASSERT_EQ(emu.AddWithCarry(64, ll_max, 0, 1, pstate), (uint64_t)(ll_max + 1));
60+
ASSERT_EQ(pstate.V, 1ULL);
61+
ASSERT_EQ(pstate.C, 0ULL);
62+
}

0 commit comments

Comments
 (0)