encoded_program_fuzz_unittest.cc 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. // Copyright (c) 2011 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. // Fuzz testing for EncodedProgram serialized format and assembly.
  5. //
  6. // We would like some assurance that if an EncodedProgram is malformed we will
  7. // not crash. The EncodedProgram could be malformed either due to malicious
  8. // attack to due to an error in patch generation.
  9. //
  10. // We try a lot of arbitrary modifications to the serialized form and make sure
  11. // that the outcome is not a crash.
  12. #include "courgette/encoded_program.h"
  13. #include <stddef.h>
  14. #include <memory>
  15. #include "base/test/test_suite.h"
  16. #include "courgette/base_test_unittest.h"
  17. #include "courgette/courgette.h"
  18. #include "courgette/courgette_flow.h"
  19. #include "courgette/streams.h"
  20. class DecodeFuzzTest : public BaseTest {
  21. public:
  22. void FuzzExe(const char *) const;
  23. private:
  24. void FuzzByte(const std::string& buffer, const std::string& output,
  25. size_t index) const;
  26. void FuzzBits(const std::string& buffer, const std::string& output,
  27. size_t index, int bits_to_flip) const;
  28. // Returns true if could assemble, false if rejected.
  29. bool TryAssemble(const std::string& buffer, std::string* output) const;
  30. };
  31. // Loads an executable and does fuzz testing in the serialized format.
  32. void DecodeFuzzTest::FuzzExe(const char* file_name) const {
  33. std::string file1 = FileContents(file_name);
  34. const uint8_t* original_data = reinterpret_cast<const uint8_t*>(file1.data());
  35. size_t original_length = file1.length();
  36. courgette::CourgetteFlow flow;
  37. courgette::RegionBuffer original_buffer(
  38. courgette::Region(original_data, original_length));
  39. flow.ReadDisassemblerFromBuffer(flow.ONLY, original_buffer);
  40. EXPECT_EQ(courgette::C_OK, flow.status());
  41. EXPECT_TRUE(nullptr != flow.data(flow.ONLY)->disassembler.get());
  42. flow.CreateAssemblyProgramFromDisassembler(flow.ONLY, false);
  43. EXPECT_EQ(courgette::C_OK, flow.status());
  44. EXPECT_TRUE(nullptr != flow.data(flow.ONLY)->program.get());
  45. flow.CreateEncodedProgramFromDisassemblerAndAssemblyProgram(flow.ONLY);
  46. EXPECT_EQ(courgette::C_OK, flow.status());
  47. EXPECT_TRUE(nullptr != flow.data(flow.ONLY)->encoded.get());
  48. flow.DestroyAssemblyProgram(flow.ONLY);
  49. EXPECT_EQ(courgette::C_OK, flow.status());
  50. EXPECT_TRUE(nullptr == flow.data(flow.ONLY)->program.get());
  51. flow.DestroyDisassembler(flow.ONLY);
  52. EXPECT_EQ(courgette::C_OK, flow.status());
  53. EXPECT_TRUE(nullptr == flow.data(flow.ONLY)->disassembler.get());
  54. flow.WriteSinkStreamSetFromEncodedProgram(flow.ONLY);
  55. EXPECT_EQ(courgette::C_OK, flow.status());
  56. flow.DestroyEncodedProgram(flow.ONLY);
  57. EXPECT_EQ(courgette::C_OK, flow.status());
  58. EXPECT_TRUE(nullptr == flow.data(flow.ONLY)->encoded.get());
  59. courgette::SinkStream sink;
  60. flow.WriteSinkStreamFromSinkStreamSet(flow.ONLY, &sink);
  61. EXPECT_EQ(courgette::C_OK, flow.status());
  62. EXPECT_TRUE(flow.ok());
  63. EXPECT_FALSE(flow.failed());
  64. size_t length = sink.Length();
  65. std::string base_buffer(reinterpret_cast<const char*>(sink.Buffer()), length);
  66. std::string base_output;
  67. bool ok = TryAssemble(base_buffer, &base_output);
  68. EXPECT_TRUE(ok);
  69. // Now we have a good serialized EncodedProgram in |base_buffer|. Time to
  70. // fuzz.
  71. // More intense fuzzing on the first part because it contains more control
  72. // information like substeam lengths.
  73. size_t position = 0;
  74. for ( ; position < 100 && position < length; position += 1) {
  75. FuzzByte(base_buffer, base_output, position);
  76. }
  77. // We would love to fuzz every position, but it takes too long.
  78. for ( ; position < length; position += 900) {
  79. FuzzByte(base_buffer, base_output, position);
  80. }
  81. }
  82. // FuzzByte tries to break the EncodedProgram deserializer and assembler. It
  83. // takes a good serialization of and EncodedProgram, flips some bits, and checks
  84. // that the behaviour is reasonable. It has testing checks for unreasonable
  85. // behaviours.
  86. void DecodeFuzzTest::FuzzByte(const std::string& base_buffer,
  87. const std::string& base_output,
  88. size_t index) const {
  89. printf("Fuzzing position %d\n", static_cast<int>(index));
  90. // The following 10 values are a compromize between run time and coverage of
  91. // the 255 'wrong' values at this byte position.
  92. // 0xFF flips all the bits.
  93. FuzzBits(base_buffer, base_output, index, 0xFF);
  94. // 0x7F flips the most bits without changing Varint32 framing.
  95. FuzzBits(base_buffer, base_output, index, 0x7F);
  96. // These all flip one bit.
  97. FuzzBits(base_buffer, base_output, index, 0x80);
  98. FuzzBits(base_buffer, base_output, index, 0x40);
  99. FuzzBits(base_buffer, base_output, index, 0x20);
  100. FuzzBits(base_buffer, base_output, index, 0x10);
  101. FuzzBits(base_buffer, base_output, index, 0x08);
  102. FuzzBits(base_buffer, base_output, index, 0x04);
  103. FuzzBits(base_buffer, base_output, index, 0x02);
  104. FuzzBits(base_buffer, base_output, index, 0x01);
  105. }
  106. // FuzzBits tries to break the EncodedProgram deserializer and assembler. It
  107. // takes a good serialization of and EncodedProgram, flips some bits, and checks
  108. // that the behaviour is reasonable.
  109. //
  110. // There are EXPECT calls to check for unreasonable behaviour. These are
  111. // somewhat arbitrary in that the parameters cannot easily be derived from first
  112. // principles. They may need updating as the serialized format evolves.
  113. void DecodeFuzzTest::FuzzBits(const std::string& base_buffer,
  114. const std::string& base_output,
  115. size_t index, int bits_to_flip) const {
  116. std::string modified_buffer = base_buffer;
  117. std::string modified_output;
  118. modified_buffer[index] ^= bits_to_flip;
  119. bool ok = TryAssemble(modified_buffer, &modified_output);
  120. if (ok) {
  121. // We normally expect TryAssemble to fail. But sometimes it succeeds.
  122. // What could have happened? We changed one byte in the serialized form:
  123. //
  124. // * If we changed one of the copied bytes, we would see a single byte
  125. // change in the output.
  126. // * If we changed an address table element, all the references to that
  127. // address would be different.
  128. // * If we changed a copy count, we would run out of data in some stream,
  129. // or leave data remaining, so should not be here.
  130. // * If we changed an origin address, it could affect all relocations based
  131. // off that address. If no relocations were based off the address then
  132. // there will be no changes.
  133. // * If we changed an origin address, it could cause some abs32 relocs to
  134. // shift from one page to the next, changing the number and layout of
  135. // blocks in the base relocation table.
  136. // Generated length could vary slightly due to base relocation table layout.
  137. // In the worst case the number of base relocation blocks doubles, approx
  138. // 12/4096 or 0.3% size of file.
  139. size_t base_length = base_output.length();
  140. size_t modified_length = modified_output.length();
  141. ptrdiff_t diff = base_length - modified_length;
  142. if (diff < -200 || diff > 200) {
  143. EXPECT_EQ(base_length, modified_length);
  144. }
  145. size_t changed_byte_count = 0;
  146. for (size_t i = 0; i < base_length && i < modified_length; ++i) {
  147. changed_byte_count += (base_output[i] != modified_output[i]);
  148. }
  149. if (index > 60) { // Beyond the origin addresses ...
  150. EXPECT_NE(0U, changed_byte_count); // ... we expect some difference.
  151. }
  152. // Currently all changes are smaller than this number:
  153. EXPECT_GE(45000U, changed_byte_count);
  154. }
  155. }
  156. bool DecodeFuzzTest::TryAssemble(const std::string& file,
  157. std::string* output) const {
  158. courgette::CourgetteFlow flow;
  159. courgette::RegionBuffer file_buffer(courgette::Region(
  160. reinterpret_cast<const uint8_t*>(file.data()), file.length()));
  161. flow.ReadSourceStreamSetFromBuffer(flow.ONLY, file_buffer);
  162. if (flow.failed())
  163. return false;
  164. flow.ReadEncodedProgramFromSourceStreamSet(flow.ONLY);
  165. if (flow.failed())
  166. return false;
  167. courgette::SinkStream sink;
  168. flow.WriteExecutableFromEncodedProgram(flow.ONLY, &sink);
  169. if (flow.failed())
  170. return false;
  171. output->clear();
  172. output->assign(reinterpret_cast<const char*>(sink.Buffer()), sink.Length());
  173. return true;
  174. }
  175. TEST_F(DecodeFuzzTest, All) {
  176. FuzzExe("setup1.exe");
  177. FuzzExe("elf-32-1.exe");
  178. }
  179. int main(int argc, char** argv) {
  180. return base::TestSuite(argc, argv).Run();
  181. }