Added support to fuzzy_fastboot for more sparse tests

Test: Pixel 2
Change-Id: Ibd491336ace4f346c3900a61bca30ac3023e4f21
This commit is contained in:
Aaron Wisner 2018-08-22 11:57:31 -05:00 committed by Chris Fries
parent 00737b3939
commit ab2645514d
2 changed files with 205 additions and 0 deletions

View file

@ -133,4 +133,6 @@ class FuzzAnyPartitionLocked : public ExtensionsPartition<false> {};
class UserdataPartition : public ExtensionsPartition<true> {};
class SparseTestPartition : public ExtensionsPartition<true> {};
} // end namespace fastboot

View file

@ -80,6 +80,9 @@ std::vector<std::pair<std::string, extension::Configuration::PartitionInfo>>
std::vector<std::pair<std::string, extension::Configuration::PackedInfoTest>>
PACKED_XML_SUCCESS_TESTS;
std::vector<std::pair<std::string, extension::Configuration::PackedInfoTest>> PACKED_XML_FAIL_TESTS;
// This only has 1 or zero elements so it will disappear from gtest when empty
std::vector<std::pair<std::string, extension::Configuration::PartitionInfo>>
SINGLE_PARTITION_XML_WRITE_HASHABLE;
const std::string DEFAULT_OUPUT_NAME = "out.img";
// const char scratch_partition[] = "userdata";
@ -135,6 +138,22 @@ bool PartitionHash(FastBootDriver* fb, const std::string& part, std::string* has
return true;
}
bool SparseToBuf(sparse_file* sf, std::vector<char>* out, bool with_crc = false) {
int64_t len = sparse_file_len(sf, true, with_crc);
if (len <= 0) {
return false;
}
out->clear();
auto cb = [](void* priv, const void* data, size_t len) {
auto vec = static_cast<std::vector<char>*>(priv);
const char* cbuf = static_cast<const char*>(data);
vec->insert(vec->end(), cbuf, cbuf + len);
return 0;
};
return !sparse_file_callback(sf, true, with_crc, cb, out);
}
// Only allow alphanumeric, _, -, and .
const auto not_allowed = [](char c) -> int {
return !(isalnum(c) || c == '_' || c == '-' || c == '.');
@ -524,6 +543,42 @@ TEST_F(Conformance, SparseDownload3) {
EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
}
TEST_F(Conformance, SparseVersionCheck) {
SparseWrapper sparse(4096, 4096);
ASSERT_TRUE(*sparse) << "Sparse image creation failed";
std::vector<char> buf;
ASSERT_TRUE(SparseToBuf(*sparse, &buf)) << "Sparse buffer creation failed";
// Invalid, right after magic
buf[4] = 0xff;
ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
// It can either reject this download or reject it during flash
if (HandleResponse() != DEVICE_FAIL) {
EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
<< "Flashing an invalid sparse version should fail " << sparse.Rep();
}
}
TEST_F(Conformance, SparseCRCCheck) {
SparseWrapper sparse(4096, 4096);
ASSERT_TRUE(*sparse) << "Sparse image creation failed";
std::vector<char> buf = RandomBuf(4096);
ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 0), 0)
<< "Adding data failed to sparse file: " << sparse.Rep();
ASSERT_TRUE(SparseToBuf(*sparse, &buf, true)) << "Sparse buffer creation failed";
// Flip a bit in the crc
buf.back() = buf.back() ^ 0x01;
ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
printf("%02x\n", (unsigned char)buf.back());
// It can either reject this download or reject it during flash
if (HandleResponse() != DEVICE_FAIL) {
EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
<< "Flashing an invalid sparse version should fail " << sparse.Rep();
}
}
TEST_F(UnlockPermissions, Download) {
std::vector<char> buf{'a', 'o', 's', 'p'};
EXPECT_EQ(fb->Download(buf), SUCCESS) << "Download 4-byte payload failed";
@ -767,6 +822,47 @@ TEST_F(Fuzz, CommandMissingArgs) {
}
}
TEST_F(Fuzz, SparseZeroLength) {
SparseWrapper sparse(4096, 0);
ASSERT_TRUE(*sparse) << "Sparse image creation failed";
RetCode ret = fb->Download(*sparse);
// Two ways to handle it
if (ret != DEVICE_FAIL) { // if lazily parsed it better fail on a flash
EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
<< "Flashing zero length sparse image did not fail: " << sparse.Rep();
}
ret = fb->Download(*sparse, true);
if (ret != DEVICE_FAIL) { // if lazily parsed it better fail on a flash
EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
<< "Flashing zero length sparse image did not fail " << sparse.Rep();
}
}
TEST_F(Fuzz, SparseTooManyChunks) {
SparseWrapper sparse(4096, 4096); // 1 block, but we send two chunks that will use 2 blocks
ASSERT_TRUE(*sparse) << "Sparse image creation failed";
std::vector<char> buf = RandomBuf(4096);
ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 0), 0)
<< "Adding data failed to sparse file: " << sparse.Rep();
// We take advantage of the fact the sparse library does not check this
ASSERT_EQ(sparse_file_add_fill(*sparse, 0xdeadbeef, 4096, 1), 0)
<< "Adding fill to sparse file failed: " << sparse.Rep();
RetCode ret = fb->Download(*sparse);
// Two ways to handle it
if (ret != DEVICE_FAIL) { // if lazily parsed it better fail on a flash
EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
<< "Flashing sparse image with 'total_blks' in header 1 too small did not fail "
<< sparse.Rep();
}
ret = fb->Download(*sparse, true);
if (ret != DEVICE_FAIL) { // if lazily parsed it better fail on a flash
EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
<< "Flashing sparse image with 'total_blks' in header 1 too small did not fail "
<< sparse.Rep();
}
}
TEST_F(Fuzz, USBResetSpam) {
auto start = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed;
@ -1379,6 +1475,109 @@ TEST_P(ExtensionsOemConformance, RunOEMTest) {
INSTANTIATE_TEST_CASE_P(XMLOEM, ExtensionsOemConformance, ::testing::ValuesIn(OEM_XML_TESTS));
// Sparse Tests
TEST_P(SparseTestPartition, SparseSingleBlock) {
const std::string name = GetParam().first;
auto part_info = GetParam().second;
const std::string part_name = name + (part_info.slots ? "_a" : "");
SparseWrapper sparse(4096, 4096);
ASSERT_TRUE(*sparse) << "Sparse image creation failed";
std::vector<char> buf = RandomBuf(4096);
ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 0), 0)
<< "Adding data failed to sparse file: " << sparse.Rep();
EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
EXPECT_EQ(fb->Flash(part_name), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
std::string hash, hash_new, err_msg;
int retcode;
ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg)) << err_msg;
ASSERT_EQ(retcode, 0) << err_msg;
// Now flash it the non-sparse way
EXPECT_EQ(fb->FlashPartition(part_name, buf), SUCCESS) << "Flashing image failed: ";
ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_new, &retcode, &err_msg)) << err_msg;
ASSERT_EQ(retcode, 0) << err_msg;
EXPECT_EQ(hash, hash_new) << "Flashing a random buffer of 4096 using sparse and non-sparse "
"methods did not result in the same hash";
}
TEST_P(SparseTestPartition, SparseFill) {
const std::string name = GetParam().first;
auto part_info = GetParam().second;
const std::string part_name = name + (part_info.slots ? "_a" : "");
int64_t size = (max_dl / 4096) * 4096;
SparseWrapper sparse(4096, size);
ASSERT_TRUE(*sparse) << "Sparse image creation failed";
ASSERT_EQ(sparse_file_add_fill(*sparse, 0xdeadbeef, size, 0), 0)
<< "Adding data failed to sparse file: " << sparse.Rep();
EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
EXPECT_EQ(fb->Flash(part_name), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
std::string hash, hash_new, err_msg;
int retcode;
ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg)) << err_msg;
ASSERT_EQ(retcode, 0) << err_msg;
// Now flash it the non-sparse way
std::vector<char> buf(size);
for (auto iter = buf.begin(); iter < buf.end(); iter += 4) {
iter[0] = 0xef;
iter[1] = 0xbe;
iter[2] = 0xad;
iter[3] = 0xde;
}
EXPECT_EQ(fb->FlashPartition(part_name, buf), SUCCESS) << "Flashing image failed: ";
ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_new, &retcode, &err_msg)) << err_msg;
ASSERT_EQ(retcode, 0) << err_msg;
EXPECT_EQ(hash, hash_new) << "Flashing a random buffer of 4096 using sparse and non-sparse "
"methods did not result in the same hash";
}
// This tests to make sure it does not overwrite previous flashes
TEST_P(SparseTestPartition, SparseMultiple) {
const std::string name = GetParam().first;
auto part_info = GetParam().second;
const std::string part_name = name + (part_info.slots ? "_a" : "");
int64_t size = (max_dl / 4096) * 4096;
SparseWrapper sparse(4096, size / 2);
ASSERT_TRUE(*sparse) << "Sparse image creation failed";
ASSERT_EQ(sparse_file_add_fill(*sparse, 0xdeadbeef, size / 2, 0), 0)
<< "Adding data failed to sparse file: " << sparse.Rep();
EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
EXPECT_EQ(fb->Flash(part_name), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
SparseWrapper sparse2(4096, size / 2);
ASSERT_TRUE(*sparse) << "Sparse image creation failed";
std::vector<char> buf = RandomBuf(size / 2);
ASSERT_EQ(sparse_file_add_data(*sparse2, buf.data(), buf.size(), (size / 2) / 4096), 0)
<< "Adding data failed to sparse file: " << sparse2.Rep();
EXPECT_EQ(fb->Download(*sparse2), SUCCESS) << "Download sparse failed: " << sparse2.Rep();
EXPECT_EQ(fb->Flash(part_name), SUCCESS) << "Flashing sparse failed: " << sparse2.Rep();
std::string hash, hash_new, err_msg;
int retcode;
ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg)) << err_msg;
ASSERT_EQ(retcode, 0) << err_msg;
// Now flash it the non-sparse way
std::vector<char> fbuf(size);
for (auto iter = fbuf.begin(); iter < fbuf.begin() + size / 2; iter += 4) {
iter[0] = 0xef;
iter[1] = 0xbe;
iter[2] = 0xad;
iter[3] = 0xde;
}
fbuf.assign(buf.begin(), buf.end());
EXPECT_EQ(fb->FlashPartition(part_name, fbuf), SUCCESS) << "Flashing image failed: ";
ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_new, &retcode, &err_msg)) << err_msg;
ASSERT_EQ(retcode, 0) << err_msg;
EXPECT_EQ(hash, hash_new) << "Flashing a random buffer of 4096 using sparse and non-sparse "
"methods did not result in the same hash";
}
INSTANTIATE_TEST_CASE_P(XMLSparseTest, SparseTestPartition,
::testing::ValuesIn(SINGLE_PARTITION_XML_WRITE_HASHABLE));
void GenerateXmlTests(const extension::Configuration& config) {
// Build the getvar tests
for (const auto it : config.getvars) {
@ -1430,6 +1629,10 @@ void GenerateXmlTests(const extension::Configuration& config) {
std::make_tuple(part_info->first, part_info->second));
}
if (!PARTITION_XML_WRITE_HASHABLE.empty()) {
SINGLE_PARTITION_XML_WRITE_HASHABLE.push_back(PARTITION_XML_WRITE_HASHABLE.front());
}
// Build oem tests
for (const auto it : config.oem) {
auto oem_cmd = it.second;