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 : #include "syzygy/block_graph/basic_block_test_util.h"
20 :
21 : namespace block_graph {
22 :
23 : namespace {
24 :
25 : class BlockUtilTest: public testing::Test {
26 : public:
27 : BlockUtilTest()
28 : : bb_(NULL),
29 E : start_addr_(0xF00D) {
30 E : bb_ = subgraph_.AddBasicCodeBlock("foo");
31 E : }
32 :
33 E : BlockGraph::Size AddInstructions(bool add_source_ranges) {
34 : using assm::eax;
35 : using assm::ebp;
36 : using assm::esp;
37 :
38 E : BasicBlockAssembler assm(bb_->instructions().begin(), &bb_->instructions());
39 :
40 E : assm.push(ebp);
41 E : assm.mov(ebp, esp);
42 E : assm.mov(eax, Operand(ebp, Displacement(8)));
43 E : assm.pop(ebp);
44 : // assm.ret(0);
45 :
46 E : BasicBlock::Instructions::iterator inst_it(bb_->instructions().begin());
47 E : BlockGraph::RelativeAddress next_addr(start_addr_);
48 :
49 E : BasicBlock::Offset next_offs = 0;
50 E : for (; inst_it != bb_->instructions().end(); ++inst_it) {
51 E : if (add_source_ranges)
52 : inst_it->set_source_range(
53 E : Instruction::SourceRange(next_addr, inst_it->size()));
54 :
55 E : next_addr += inst_it->size();
56 E : next_offs += inst_it->size();
57 E : }
58 :
59 E : BasicBlockReference ref(BlockGraph::PC_RELATIVE_REF, 4, bb_);
60 : bb_->successors().push_back(
61 E : Successor(Successor::kConditionAbove, ref, 5));
62 E : next_offs += 5;
63 :
64 E : if (add_source_ranges)
65 : bb_->successors().back().set_source_range(
66 E : Successor::SourceRange(next_addr, 5));
67 :
68 : bb_->successors().push_back(
69 E : Successor(Successor::kConditionBelowOrEqual, ref, 0));
70 :
71 E : return next_offs;
72 E : }
73 :
74 : protected:
75 : BlockGraph image_;
76 : BlockGraph::RelativeAddress start_addr_;
77 : BasicBlockSubGraph subgraph_;
78 : BasicCodeBlock* bb_;
79 : };
80 :
81 : } // namespace
82 :
83 E : TEST_F(BlockUtilTest, GetBasicBlockSourceRangeEmptyFails) {
84 E : BlockGraph::Size instr_len = AddInstructions(false);
85 :
86 E : BlockGraph::Block::SourceRange source_range;
87 E : ASSERT_FALSE(GetBasicBlockSourceRange(*bb_, &source_range));
88 E : EXPECT_EQ(0, source_range.size());
89 E : }
90 :
91 E : TEST_F(BlockUtilTest, GetBasicBlockSourceRangeNonContiguousFails) {
92 E : BlockGraph::Size instr_len = AddInstructions(true);
93 :
94 : // Make the range non-contiguous by pushing the successor out one byte.
95 : BlockGraph::Block::SourceRange range =
96 E : bb_->successors().front().source_range();
97 : bb_->successors().front().set_source_range(
98 E : BlockGraph::Block::SourceRange(range.start() + 1, range.size()));
99 :
100 E : BlockGraph::Block::SourceRange source_range;
101 E : ASSERT_FALSE(GetBasicBlockSourceRange(*bb_, &source_range));
102 E : EXPECT_EQ(0, source_range.size());
103 E : }
104 :
105 E : TEST_F(BlockUtilTest, GetBasicBlockSourceRangeSequentialSucceeds) {
106 E : BlockGraph::Size instr_len = AddInstructions(true);
107 :
108 E : BlockGraph::Block::SourceRange source_range;
109 E : ASSERT_TRUE(GetBasicBlockSourceRange(*bb_, &source_range));
110 : BlockGraph::Block::SourceRange expected_range(
111 E : BlockGraph::RelativeAddress(0xF00D), instr_len);
112 E : EXPECT_EQ(expected_range, source_range);
113 E : }
114 :
115 E : TEST_F(BlockUtilTest, GetBasicBlockSourceRangeNonSequentialSucceeds) {
116 E : BlockGraph::Size instr_len = AddInstructions(true);
117 :
118 : // Shuffle the ranges by flipping the first and last ranges.
119 : BlockGraph::Block::SourceRange temp =
120 E : bb_->successors().front().source_range();
121 : bb_->successors().front().set_source_range(
122 E : bb_->instructions().front().source_range());
123 E : bb_->instructions().front().set_source_range(temp);
124 :
125 E : BlockGraph::Block::SourceRange source_range;
126 E : ASSERT_TRUE(GetBasicBlockSourceRange(*bb_, &source_range));
127 : BlockGraph::Block::SourceRange expected_range(
128 E : BlockGraph::RelativeAddress(0xF00D), instr_len);
129 E : EXPECT_EQ(expected_range, source_range);
130 E : }
131 :
132 E : TEST_F(BlockUtilTest, GetBasicBlockSourceRangePrependInstructionsSucceeds) {
133 E : BlockGraph::Size instr_len = AddInstructions(true);
134 :
135 : // Prepend some instrumentation-like code.
136 E : BasicBlockAssembler assm(bb_->instructions().begin(), &bb_->instructions());
137 E : assm.push(Immediate(0xBADF00D));
138 E : assm.call(Immediate(bb_));
139 :
140 E : BlockGraph::Block::SourceRange source_range;
141 E : ASSERT_TRUE(GetBasicBlockSourceRange(*bb_, &source_range));
142 : BlockGraph::Block::SourceRange expected_range(
143 E : BlockGraph::RelativeAddress(0xF00D), instr_len);
144 E : EXPECT_EQ(expected_range, source_range);
145 E : }
146 :
147 E : TEST_F(BlockUtilTest, IsUnsafeReference) {
148 : // Some safe blocks.
149 E : BlockGraph::Block* s1 = image_.AddBlock(BlockGraph::CODE_BLOCK, 40, "s1");
150 E : BlockGraph::Block* s2 = image_.AddBlock(BlockGraph::CODE_BLOCK, 40, "s2");
151 :
152 : // Some unsafe blocks.
153 E : BlockGraph::Block* u1 = image_.AddBlock(BlockGraph::CODE_BLOCK, 40, "u1");
154 E : u1->set_attribute(BlockGraph::HAS_INLINE_ASSEMBLY);
155 E : BlockGraph::Block* u2 = image_.AddBlock(BlockGraph::CODE_BLOCK, 40, "u2");
156 E : u2->set_attribute(BlockGraph::BUILT_BY_UNSUPPORTED_COMPILER);
157 :
158 : // If neither or only one has an unsafe attribute then it's not unsafe.
159 : EXPECT_FALSE(IsUnsafeReference(s1, BlockGraph::Reference(
160 : BlockGraph::PC_RELATIVE_REF,
161 : BlockGraph::Reference::kMaximumSize,
162 E : s2, 0, 0)));
163 : EXPECT_FALSE(IsUnsafeReference(s1, BlockGraph::Reference(
164 : BlockGraph::PC_RELATIVE_REF,
165 : BlockGraph::Reference::kMaximumSize,
166 E : u1, 0, 0)));
167 : EXPECT_FALSE(IsUnsafeReference(u2, BlockGraph::Reference(
168 : BlockGraph::PC_RELATIVE_REF,
169 : BlockGraph::Reference::kMaximumSize,
170 E : s2, 0, 0)));
171 :
172 : // If the reference points to a non-zero offset then it's unsafe.
173 : EXPECT_TRUE(IsUnsafeReference(s1, BlockGraph::Reference(
174 : BlockGraph::PC_RELATIVE_REF,
175 : BlockGraph::Reference::kMaximumSize,
176 E : s2, 4, 4)));
177 :
178 : // If both the referring and referred blocks have unsafe attributes,
179 : // the reference is unsafe.
180 : EXPECT_TRUE(IsUnsafeReference(u1, BlockGraph::Reference(
181 : BlockGraph::PC_RELATIVE_REF,
182 : BlockGraph::Reference::kMaximumSize,
183 E : u2, 0, 0)));
184 E : }
185 :
186 E : TEST_F(BlockUtilTest, CheckNoUnexpectedStackFrameManipulation) {
187 : // Prepend some instrumentation with a conventional calling convention.
188 E : BasicBlockAssembler assm(bb_->instructions().begin(), &bb_->instructions());
189 E : assm.push(assm::ebp);
190 E : assm.mov(assm::ebp, assm::esp);
191 E : assm.mov(assm::eax, Operand(assm::ebp, Displacement(8)));
192 E : assm.pop(assm::ebp);
193 E : assm.ret(0);
194 :
195 E : EXPECT_FALSE(HasUnexpectedStackFrameManipulation(&subgraph_));
196 E : }
197 :
198 E : TEST_F(BlockUtilTest, CheckInvalidInstructionUnexpectedStackFrameManipulation) {
199 : // Prepend some instrumentation with a conventional calling convention.
200 E : BasicBlockAssembler assm(bb_->instructions().begin(), &bb_->instructions());
201 E : assm.push(assm::ebp);
202 E : assm.mov(assm::ebp, assm::esp);
203 : // The instruction LEA is invalid stack frame manipulation.
204 E : assm.lea(assm::ebp, Operand(assm::ebp, Displacement(8)));
205 E : assm.pop(assm::ebp);
206 E : assm.ret(0);
207 :
208 E : EXPECT_TRUE(HasUnexpectedStackFrameManipulation(&subgraph_));
209 E : }
210 :
211 E : TEST_F(BlockUtilTest, CheckInvalidRegisterUnexpectedStackFrameManipulation) {
212 : // Prepend some instrumentation with a conventional calling convention.
213 E : BasicBlockAssembler assm(bb_->instructions().begin(), &bb_->instructions());
214 E : assm.push(assm::ebp);
215 : // The instruction MOV use an invalid register EAX.
216 E : assm.mov(assm::ebp, assm::eax);
217 E : assm.lea(assm::ebp, Operand(assm::ebp, Displacement(8)));
218 E : assm.pop(assm::ebp);
219 E : assm.ret(0);
220 :
221 E : EXPECT_TRUE(HasUnexpectedStackFrameManipulation(&subgraph_));
222 E : }
223 :
224 : namespace {
225 :
226 : // A utility class for using the test data built around the function in
227 : // basic_block_assembly_func.asm.
228 : class BlockUtilOnTestDataTest : public testing::BasicBlockTest {
229 : public:
230 E : virtual void SetUp() OVERRIDE {
231 E : BasicBlockTest::SetUp();
232 E : ASSERT_NO_FATAL_FAILURE(InitBlockGraph());
233 E : }
234 : };
235 :
236 : } // namespace
237 :
238 E : TEST_F(BlockUtilOnTestDataTest, GetJumpTableSize) {
239 : block_graph::BlockGraph::BlockMap::const_iterator block_iter =
240 E : block_graph_.blocks().begin();
241 :
242 E : size_t table_size = 0;
243 E : bool table_found = false;
244 :
245 : // Iterates over the blocks of the block_graph. We expect to find only one
246 : // block containing one jump table.
247 E : for (; block_iter != block_graph_.blocks().end(); ++block_iter) {
248 E : if (block_iter->second.type() != BlockGraph::CODE_BLOCK)
249 E : continue;
250 :
251 : // Iterates over the labels of the block to find the jump tables.
252 : BlockGraph::Block::LabelMap::const_iterator iter_label =
253 E : block_iter->second.labels().begin();
254 E : for (; iter_label != block_iter->second.labels().end(); ++iter_label) {
255 E : if (!iter_label->second.has_attributes(BlockGraph::JUMP_TABLE_LABEL))
256 E : continue;
257 :
258 : // There's only one jump table in the test data.
259 E : EXPECT_FALSE(table_found);
260 E : table_found = true;
261 :
262 : EXPECT_TRUE(block_graph::GetJumpTableSize(&block_iter->second,
263 : iter_label,
264 E : &table_size));
265 E : }
266 E : }
267 E : EXPECT_EQ(3U, table_size);
268 E : }
269 :
270 : } // namespace block_graph
|