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