[go: nahoru, domu]

blob: 8b85e207018e3ac3e989089dda6c7f749866b98c [file] [log] [blame]
Avi Drissman0ff8a7e2022-09-13 18:35:421// Copyright 2011 The Chromium Authors
sra@chromium.org04ca1bc2009-05-08 23:00:292// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Fuzz testing for EncodedProgram serialized format and assembly.
6//
7// We would like some assurance that if an EncodedProgram is malformed we will
8// not crash. The EncodedProgram could be malformed either due to malicious
9// attack to due to an error in patch generation.
10//
11// We try a lot of arbitrary modifications to the serialized form and make sure
12// that the outcome is not a crash.
huangs8d5be252016-01-29 22:14:1813#include "courgette/encoded_program.h"
sra@chromium.org04ca1bc2009-05-08 23:00:2914
aviab98dcc92015-12-21 19:35:3315#include <stddef.h>
16
mostynb1007a4a2016-04-11 23:18:0617#include <memory>
18
huangs8d5be252016-01-29 22:14:1819#include "base/test/test_suite.h"
dgarrett@chromium.orgbf0ece42011-10-18 01:08:3520#include "courgette/base_test_unittest.h"
sra@chromium.org04ca1bc2009-05-08 23:00:2921#include "courgette/courgette.h"
huangs8b91a4c92017-04-25 23:04:5822#include "courgette/courgette_flow.h"
sra@chromium.org04ca1bc2009-05-08 23:00:2923#include "courgette/streams.h"
24
dgarrett@chromium.orgbf0ece42011-10-18 01:08:3525class DecodeFuzzTest : public BaseTest {
sra@chromium.org04ca1bc2009-05-08 23:00:2926 public:
27 void FuzzExe(const char *) const;
28
29 private:
sra@chromium.org04ca1bc2009-05-08 23:00:2930 void FuzzByte(const std::string& buffer, const std::string& output,
31 size_t index) const;
32 void FuzzBits(const std::string& buffer, const std::string& output,
33 size_t index, int bits_to_flip) const;
34
35 // Returns true if could assemble, false if rejected.
36 bool TryAssemble(const std::string& buffer, std::string* output) const;
sra@chromium.org04ca1bc2009-05-08 23:00:2937};
38
sra@chromium.org04ca1bc2009-05-08 23:00:2939// Loads an executable and does fuzz testing in the serialized format.
40void DecodeFuzzTest::FuzzExe(const char* file_name) const {
41 std::string file1 = FileContents(file_name);
42
huangs8b91a4c92017-04-25 23:04:5843 const uint8_t* original_data = reinterpret_cast<const uint8_t*>(file1.data());
dgarrett@chromium.org4b3d192b2011-11-08 20:32:2644 size_t original_length = file1.length();
huangs8b91a4c92017-04-25 23:04:5845 courgette::CourgetteFlow flow;
sra@chromium.org04ca1bc2009-05-08 23:00:2946
huangs8b91a4c92017-04-25 23:04:5847 courgette::RegionBuffer original_buffer(
48 courgette::Region(original_data, original_length));
huangs88451332017-05-18 19:50:3449 flow.ReadDisassemblerFromBuffer(flow.ONLY, original_buffer);
50 EXPECT_EQ(courgette::C_OK, flow.status());
51 EXPECT_TRUE(nullptr != flow.data(flow.ONLY)->disassembler.get());
52
53 flow.CreateAssemblyProgramFromDisassembler(flow.ONLY, false);
huangs8b91a4c92017-04-25 23:04:5854 EXPECT_EQ(courgette::C_OK, flow.status());
55 EXPECT_TRUE(nullptr != flow.data(flow.ONLY)->program.get());
sra@chromium.org04ca1bc2009-05-08 23:00:2956
huangs88451332017-05-18 19:50:3457 flow.CreateEncodedProgramFromDisassemblerAndAssemblyProgram(flow.ONLY);
huangs8b91a4c92017-04-25 23:04:5858 EXPECT_EQ(courgette::C_OK, flow.status());
59 EXPECT_TRUE(nullptr != flow.data(flow.ONLY)->encoded.get());
sra@chromium.org04ca1bc2009-05-08 23:00:2960
huangs8b91a4c92017-04-25 23:04:5861 flow.DestroyAssemblyProgram(flow.ONLY);
62 EXPECT_EQ(courgette::C_OK, flow.status());
63 EXPECT_TRUE(nullptr == flow.data(flow.ONLY)->program.get());
sra@chromium.org04ca1bc2009-05-08 23:00:2964
huangs88451332017-05-18 19:50:3465 flow.DestroyDisassembler(flow.ONLY);
66 EXPECT_EQ(courgette::C_OK, flow.status());
67 EXPECT_TRUE(nullptr == flow.data(flow.ONLY)->disassembler.get());
68
huangs8b91a4c92017-04-25 23:04:5869 flow.WriteSinkStreamSetFromEncodedProgram(flow.ONLY);
70 EXPECT_EQ(courgette::C_OK, flow.status());
sra@chromium.org04ca1bc2009-05-08 23:00:2971
huangs8b91a4c92017-04-25 23:04:5872 flow.DestroyEncodedProgram(flow.ONLY);
73 EXPECT_EQ(courgette::C_OK, flow.status());
74 EXPECT_TRUE(nullptr == flow.data(flow.ONLY)->encoded.get());
sra@chromium.org04ca1bc2009-05-08 23:00:2975
76 courgette::SinkStream sink;
huangs8b91a4c92017-04-25 23:04:5877 flow.WriteSinkStreamFromSinkStreamSet(flow.ONLY, &sink);
78 EXPECT_EQ(courgette::C_OK, flow.status());
79 EXPECT_TRUE(flow.ok());
80 EXPECT_FALSE(flow.failed());
sra@chromium.org04ca1bc2009-05-08 23:00:2981
82 size_t length = sink.Length();
83
84 std::string base_buffer(reinterpret_cast<const char*>(sink.Buffer()), length);
85 std::string base_output;
86 bool ok = TryAssemble(base_buffer, &base_output);
hans@chromium.org515f2492011-01-14 10:36:2887 EXPECT_TRUE(ok);
sra@chromium.org04ca1bc2009-05-08 23:00:2988
89 // Now we have a good serialized EncodedProgram in |base_buffer|. Time to
90 // fuzz.
91
92 // More intense fuzzing on the first part because it contains more control
93 // information like substeam lengths.
94 size_t position = 0;
95 for ( ; position < 100 && position < length; position += 1) {
96 FuzzByte(base_buffer, base_output, position);
97 }
98 // We would love to fuzz every position, but it takes too long.
99 for ( ; position < length; position += 900) {
100 FuzzByte(base_buffer, base_output, position);
101 }
102}
103
104// FuzzByte tries to break the EncodedProgram deserializer and assembler. It
105// takes a good serialization of and EncodedProgram, flips some bits, and checks
106// that the behaviour is reasonable. It has testing checks for unreasonable
107// behaviours.
108void DecodeFuzzTest::FuzzByte(const std::string& base_buffer,
109 const std::string& base_output,
110 size_t index) const {
111 printf("Fuzzing position %d\n", static_cast<int>(index));
112
113 // The following 10 values are a compromize between run time and coverage of
114 // the 255 'wrong' values at this byte position.
115
116 // 0xFF flips all the bits.
117 FuzzBits(base_buffer, base_output, index, 0xFF);
118 // 0x7F flips the most bits without changing Varint32 framing.
119 FuzzBits(base_buffer, base_output, index, 0x7F);
120 // These all flip one bit.
121 FuzzBits(base_buffer, base_output, index, 0x80);
122 FuzzBits(base_buffer, base_output, index, 0x40);
123 FuzzBits(base_buffer, base_output, index, 0x20);
124 FuzzBits(base_buffer, base_output, index, 0x10);
125 FuzzBits(base_buffer, base_output, index, 0x08);
126 FuzzBits(base_buffer, base_output, index, 0x04);
127 FuzzBits(base_buffer, base_output, index, 0x02);
128 FuzzBits(base_buffer, base_output, index, 0x01);
129}
130
131// FuzzBits tries to break the EncodedProgram deserializer and assembler. It
132// takes a good serialization of and EncodedProgram, flips some bits, and checks
133// that the behaviour is reasonable.
134//
135// There are EXPECT calls to check for unreasonable behaviour. These are
136// somewhat arbitrary in that the parameters cannot easily be derived from first
137// principles. They may need updating as the serialized format evolves.
138void DecodeFuzzTest::FuzzBits(const std::string& base_buffer,
139 const std::string& base_output,
140 size_t index, int bits_to_flip) const {
141 std::string modified_buffer = base_buffer;
142 std::string modified_output;
143 modified_buffer[index] ^= bits_to_flip;
144
145 bool ok = TryAssemble(modified_buffer, &modified_output);
146
147 if (ok) {
148 // We normally expect TryAssemble to fail. But sometimes it succeeds.
149 // What could have happened? We changed one byte in the serialized form:
150 //
151 // * If we changed one of the copied bytes, we would see a single byte
152 // change in the output.
153 // * If we changed an address table element, all the references to that
154 // address would be different.
155 // * If we changed a copy count, we would run out of data in some stream,
156 // or leave data remaining, so should not be here.
157 // * If we changed an origin address, it could affect all relocations based
158 // off that address. If no relocations were based off the address then
159 // there will be no changes.
160 // * If we changed an origin address, it could cause some abs32 relocs to
161 // shift from one page to the next, changing the number and layout of
162 // blocks in the base relocation table.
163
164 // Generated length could vary slightly due to base relocation table layout.
165 // In the worst case the number of base relocation blocks doubles, approx
166 // 12/4096 or 0.3% size of file.
167 size_t base_length = base_output.length();
168 size_t modified_length = modified_output.length();
169 ptrdiff_t diff = base_length - modified_length;
170 if (diff < -200 || diff > 200) {
171 EXPECT_EQ(base_length, modified_length);
172 }
173
174 size_t changed_byte_count = 0;
175 for (size_t i = 0; i < base_length && i < modified_length; ++i) {
176 changed_byte_count += (base_output[i] != modified_output[i]);
177 }
178
179 if (index > 60) { // Beyond the origin addresses ...
sra@google.com54f1b822009-07-18 03:28:40180 EXPECT_NE(0U, changed_byte_count); // ... we expect some difference.
sra@chromium.org04ca1bc2009-05-08 23:00:29181 }
182 // Currently all changes are smaller than this number:
sra@google.com54f1b822009-07-18 03:28:40183 EXPECT_GE(45000U, changed_byte_count);
sra@chromium.org04ca1bc2009-05-08 23:00:29184 }
185}
186
huangs8b91a4c92017-04-25 23:04:58187bool DecodeFuzzTest::TryAssemble(const std::string& file,
sra@chromium.org04ca1bc2009-05-08 23:00:29188 std::string* output) const {
huangs8b91a4c92017-04-25 23:04:58189 courgette::CourgetteFlow flow;
190 courgette::RegionBuffer file_buffer(courgette::Region(
191 reinterpret_cast<const uint8_t*>(file.data()), file.length()));
192 flow.ReadSourceStreamSetFromBuffer(flow.ONLY, file_buffer);
193 if (flow.failed())
194 return false;
sra@chromium.org04ca1bc2009-05-08 23:00:29195
huangs8b91a4c92017-04-25 23:04:58196 flow.ReadEncodedProgramFromSourceStreamSet(flow.ONLY);
197 if (flow.failed())
198 return false;
sra@chromium.org04ca1bc2009-05-08 23:00:29199
huangs8b91a4c92017-04-25 23:04:58200 courgette::SinkStream sink;
201 flow.WriteExecutableFromEncodedProgram(flow.ONLY, &sink);
202 if (flow.failed())
203 return false;
sra@chromium.org04ca1bc2009-05-08 23:00:29204
huangs8b91a4c92017-04-25 23:04:58205 output->clear();
206 output->assign(reinterpret_cast<const char*>(sink.Buffer()), sink.Length());
207 return true;
sra@chromium.org04ca1bc2009-05-08 23:00:29208}
209
210TEST_F(DecodeFuzzTest, All) {
211 FuzzExe("setup1.exe");
dgarrett@chromium.org4b3d192b2011-11-08 20:32:26212 FuzzExe("elf-32-1.exe");
sra@chromium.org04ca1bc2009-05-08 23:00:29213}
214
215int main(int argc, char** argv) {
brettw@chromium.org20e14912010-08-17 19:40:11216 return base::TestSuite(argc, argv).Run();
sra@chromium.org04ca1bc2009-05-08 23:00:29217}