1 : // Copyright 2012 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : #include "syzygy/block_graph/block_util.h"
16 :
17 : #include "gtest/gtest.h"
18 : #include "syzygy/block_graph/basic_block_assembler.h"
19 :
20 : namespace block_graph {
21 :
22 : namespace {
23 :
24 : class BlockUtilTest: public testing::Test {
25 : public:
26 : BlockUtilTest()
27 : : bb_("foo"),
28 E : start_addr_(0xF00D) {
29 E : }
30 :
31 E : void TestAttributes(BlockGraph::BlockAttributes attributes, bool expected) {
32 E : BlockGraph::Block* code = image_.AddBlock(BlockGraph::CODE_BLOCK, 40, "c");
33 E : code->set_attributes(attributes);
34 E : ASSERT_EQ(expected, CodeBlockAttributesAreBasicBlockSafe(code));
35 E : }
36 :
37 E : BlockGraph::Size AddInstructions(bool add_source_ranges) {
38 : using core::eax;
39 : using core::ebp;
40 : using core::esp;
41 :
42 E : BasicBlockAssembler assm(bb_.instructions().begin(), &bb_.instructions());
43 :
44 E : assm.push(ebp);
45 E : assm.mov(ebp, esp);
46 E : assm.mov(eax, Operand(ebp, Displacement(8)));
47 E : assm.pop(ebp);
48 : // assm.ret(0);
49 :
50 E : BasicBlock::Instructions::iterator inst_it(bb_.instructions().begin());
51 E : BlockGraph::RelativeAddress next_addr(start_addr_);
52 :
53 E : BasicBlock::Offset next_offs = 0;
54 E : for (; inst_it != bb_.instructions().end(); ++inst_it) {
55 E : if (add_source_ranges)
56 : inst_it->set_source_range(
57 E : Instruction::SourceRange(next_addr, inst_it->size()));
58 :
59 E : next_addr += inst_it->size();
60 E : next_offs += inst_it->size();
61 E : }
62 :
63 E : BasicBlockReference ref(BlockGraph::PC_RELATIVE_REF, 4, &bb_);
64 : bb_.successors().push_back(
65 E : Successor(Successor::kConditionAbove, ref, 5));
66 E : next_offs += 5;
67 :
68 E : if (add_source_ranges)
69 : bb_.successors().back().set_source_range(
70 E : Successor::SourceRange(next_addr, 5));
71 :
72 : bb_.successors().push_back(
73 E : Successor(Successor::kConditionBelowOrEqual, ref, 0));
74 :
75 E : return next_offs;
76 E : }
77 :
78 : protected:
79 : BlockGraph image_;
80 : BlockGraph::RelativeAddress start_addr_;
81 : BasicCodeBlock bb_;
82 : };
83 :
84 : } // namespace
85 :
86 E : TEST_F(BlockUtilTest, CodeBlockAttributesAreBasicBlockSafeGapBlock) {
87 E : ASSERT_NO_FATAL_FAILURE(TestAttributes(BlockGraph::GAP_BLOCK, false));
88 E : }
89 :
90 E : TEST_F(BlockUtilTest, CodeBlockAttributesAreBasicBlockSafePaddingBlock) {
91 E : ASSERT_NO_FATAL_FAILURE(TestAttributes(BlockGraph::PADDING_BLOCK, false));
92 E : }
93 :
94 E : TEST_F(BlockUtilTest, CodeBlockAttributesAreBasicBlockSafeHasInlineAssembly) {
95 : ASSERT_NO_FATAL_FAILURE(TestAttributes(BlockGraph::HAS_INLINE_ASSEMBLY,
96 E : false));
97 E : }
98 :
99 E : TEST_F(BlockUtilTest, CodeBlockAttributesAreBasicBlockSafeUnsupportedCompiler) {
100 : ASSERT_NO_FATAL_FAILURE(TestAttributes(
101 E : BlockGraph::BUILT_BY_UNSUPPORTED_COMPILER, false));
102 E : }
103 :
104 E : TEST_F(BlockUtilTest, CodeBlockAttributesAreBasicBlockSafeErroredDisassembly) {
105 : ASSERT_NO_FATAL_FAILURE(TestAttributes(BlockGraph::ERRORED_DISASSEMBLY,
106 E : false));
107 E : }
108 :
109 E : TEST_F(BlockUtilTest, CodeBlockAttributesAreBasicBlockSafeExceptionHandling) {
110 : ASSERT_NO_FATAL_FAILURE(TestAttributes(BlockGraph::HAS_EXCEPTION_HANDLING,
111 E : false));
112 E : }
113 :
114 E : TEST_F(BlockUtilTest, CodeBlockAttributesAreBasicBlockSafeDisassembledPastEnd) {
115 : ASSERT_NO_FATAL_FAILURE(TestAttributes(BlockGraph::DISASSEMBLED_PAST_END,
116 E : false));
117 E : }
118 :
119 E : TEST_F(BlockUtilTest, CodeBlockAttributesAreBasicBlockSafeBuiltBySyzygy) {
120 : ASSERT_NO_FATAL_FAILURE(TestAttributes(
121 E : BlockGraph::HAS_INLINE_ASSEMBLY | BlockGraph::BUILT_BY_SYZYGY, true));
122 E : }
123 :
124 E : TEST_F(BlockUtilTest, GetBasicBlockSourceRangeEmptyFails) {
125 E : BlockGraph::Size instr_len = AddInstructions(false);
126 :
127 E : BlockGraph::Block::SourceRange source_range;
128 E : ASSERT_FALSE(GetBasicBlockSourceRange(bb_, &source_range));
129 E : EXPECT_EQ(0, source_range.size());
130 E : }
131 :
132 E : TEST_F(BlockUtilTest, GetBasicBlockSourceRangeNonContiguousFails) {
133 E : BlockGraph::Size instr_len = AddInstructions(true);
134 :
135 : // Make the range non-contiguous by pushing the successor out one byte.
136 : BlockGraph::Block::SourceRange range =
137 E : bb_.successors().front().source_range();
138 : bb_.successors().front().set_source_range(
139 E : BlockGraph::Block::SourceRange(range.start() + 1, range.size()));
140 :
141 E : BlockGraph::Block::SourceRange source_range;
142 E : ASSERT_FALSE(GetBasicBlockSourceRange(bb_, &source_range));
143 E : EXPECT_EQ(0, source_range.size());
144 E : }
145 :
146 E : TEST_F(BlockUtilTest, GetBasicBlockSourceRangeSequentialSucceeds) {
147 E : BlockGraph::Size instr_len = AddInstructions(true);
148 :
149 E : BlockGraph::Block::SourceRange source_range;
150 E : ASSERT_TRUE(GetBasicBlockSourceRange(bb_, &source_range));
151 : BlockGraph::Block::SourceRange expected_range(
152 E : BlockGraph::RelativeAddress(0xF00D), instr_len);
153 E : EXPECT_EQ(expected_range, source_range);
154 E : }
155 :
156 E : TEST_F(BlockUtilTest, GetBasicBlockSourceRangeNonSequentialSucceeds) {
157 E : BlockGraph::Size instr_len = AddInstructions(true);
158 :
159 : // Shuffle the ranges by flipping the first and last ranges.
160 E : BlockGraph::Block::SourceRange temp = bb_.successors().front().source_range();
161 : bb_.successors().front().set_source_range(
162 E : bb_.instructions().front().source_range());
163 E : bb_.instructions().front().set_source_range(temp);
164 :
165 E : BlockGraph::Block::SourceRange source_range;
166 E : ASSERT_TRUE(GetBasicBlockSourceRange(bb_, &source_range));
167 : BlockGraph::Block::SourceRange expected_range(
168 E : BlockGraph::RelativeAddress(0xF00D), instr_len);
169 E : EXPECT_EQ(expected_range, source_range);
170 E : }
171 :
172 E : TEST_F(BlockUtilTest, GetBasicBlockSourceRangePrependInstructionsSucceeds) {
173 E : BlockGraph::Size instr_len = AddInstructions(true);
174 :
175 : // Prepend some instrumentation-like code.
176 E : BasicBlockAssembler assm(bb_.instructions().begin(), &bb_.instructions());
177 E : assm.push(Immediate(0xBADF00D));
178 E : assm.call(Displacement(&bb_));
179 :
180 E : BlockGraph::Block::SourceRange source_range;
181 E : ASSERT_TRUE(GetBasicBlockSourceRange(bb_, &source_range));
182 : BlockGraph::Block::SourceRange expected_range(
183 E : BlockGraph::RelativeAddress(0xF00D), instr_len);
184 E : EXPECT_EQ(expected_range, source_range);
185 E : }
186 :
187 E : TEST_F(BlockUtilTest, IsUnsafeReference) {
188 : // Some safe blocks.
189 E : BlockGraph::Block* s1 = image_.AddBlock(BlockGraph::CODE_BLOCK, 40, "s1");
190 E : BlockGraph::Block* s2 = image_.AddBlock(BlockGraph::CODE_BLOCK, 40, "s2");
191 :
192 : // Some unsafe blocks.
193 E : BlockGraph::Block* u1 = image_.AddBlock(BlockGraph::CODE_BLOCK, 40, "u1");
194 E : u1->set_attribute(BlockGraph::HAS_INLINE_ASSEMBLY);
195 E : BlockGraph::Block* u2 = image_.AddBlock(BlockGraph::CODE_BLOCK, 40, "u2");
196 E : u2->set_attribute(BlockGraph::BUILT_BY_UNSUPPORTED_COMPILER);
197 :
198 : // If neither or only one has an unsafe attribute then it's not unsafe.
199 : EXPECT_FALSE(IsUnsafeReference(s1, BlockGraph::Reference(
200 : BlockGraph::PC_RELATIVE_REF,
201 : BlockGraph::Reference::kMaximumSize,
202 E : s2, 0, 0)));
203 : EXPECT_FALSE(IsUnsafeReference(s1, BlockGraph::Reference(
204 : BlockGraph::PC_RELATIVE_REF,
205 : BlockGraph::Reference::kMaximumSize,
206 E : u1, 0, 0)));
207 : EXPECT_FALSE(IsUnsafeReference(u2, BlockGraph::Reference(
208 : BlockGraph::PC_RELATIVE_REF,
209 : BlockGraph::Reference::kMaximumSize,
210 E : s2, 0, 0)));
211 :
212 : // If the reference points to a non-zero offset then it's unsafe.
213 : EXPECT_TRUE(IsUnsafeReference(s1, BlockGraph::Reference(
214 : BlockGraph::PC_RELATIVE_REF,
215 : BlockGraph::Reference::kMaximumSize,
216 E : s2, 4, 4)));
217 :
218 : // If both the referring and referred blocks have unsafe attributes,
219 : // the reference is unsafe.
220 : EXPECT_TRUE(IsUnsafeReference(u1, BlockGraph::Reference(
221 : BlockGraph::PC_RELATIVE_REF,
222 : BlockGraph::Reference::kMaximumSize,
223 E : u2, 0, 0)));
224 E : }
225 :
226 : } // namespace block_graph
|