diff options
Diffstat (limited to 'tests/component')
-rw-r--r-- | tests/component/edify_test.cpp | 5 | ||||
-rw-r--r-- | tests/component/imgdiff_test.cpp | 9 | ||||
-rw-r--r-- | tests/component/updater_test.cpp | 172 | ||||
-rw-r--r-- | tests/component/verifier_test.cpp | 206 |
4 files changed, 276 insertions, 116 deletions
diff --git a/tests/component/edify_test.cpp b/tests/component/edify_test.cpp index 287e40cc6..61a1e6b64 100644 --- a/tests/component/edify_test.cpp +++ b/tests/component/edify_test.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <memory> #include <string> #include <gtest/gtest.h> @@ -21,7 +22,7 @@ #include "edify/expr.h" static void expect(const char* expr_str, const char* expected) { - Expr* e; + std::unique_ptr<Expr> e; int error_count = 0; EXPECT_EQ(0, parse_string(expr_str, &e, &error_count)); EXPECT_EQ(0, error_count); @@ -152,7 +153,7 @@ TEST_F(EdifyTest, big_string) { TEST_F(EdifyTest, unknown_function) { // unknown function const char* script1 = "unknown_function()"; - Expr* expr; + std::unique_ptr<Expr> expr; int error_count = 0; EXPECT_EQ(1, parse_string(script1, &expr, &error_count)); EXPECT_EQ(1, error_count); diff --git a/tests/component/imgdiff_test.cpp b/tests/component/imgdiff_test.cpp index be2dd385b..2f648501c 100644 --- a/tests/component/imgdiff_test.cpp +++ b/tests/component/imgdiff_test.cpp @@ -18,13 +18,14 @@ #include <vector> #include <android-base/file.h> +#include <android-base/memory.h> #include <android-base/test_utils.h> #include <applypatch/imgdiff.h> #include <applypatch/imgpatch.h> #include <gtest/gtest.h> #include <ziparchive/zip_writer.h> -#include "applypatch/utils.h" +using android::base::get_unaligned; static ssize_t MemorySink(const unsigned char* data, ssize_t len, void* token) { std::string* s = static_cast<std::string*>(token); @@ -41,7 +42,7 @@ static void verify_patch_header(const std::string& patch, size_t* num_normal, si ASSERT_GE(size, 12U); ASSERT_EQ("IMGDIFF2", std::string(data, 8)); - const int num_chunks = Read4(data + 8); + const int num_chunks = get_unaligned<int32_t>(data + 8); ASSERT_GE(num_chunks, 0); size_t normal = 0; @@ -51,7 +52,7 @@ static void verify_patch_header(const std::string& patch, size_t* num_normal, si size_t pos = 12; for (int i = 0; i < num_chunks; ++i) { ASSERT_LE(pos + 4, size); - int type = Read4(data + pos); + int type = get_unaligned<int32_t>(data + pos); pos += 4; if (type == CHUNK_NORMAL) { pos += 24; @@ -59,7 +60,7 @@ static void verify_patch_header(const std::string& patch, size_t* num_normal, si normal++; } else if (type == CHUNK_RAW) { ASSERT_LE(pos + 4, size); - ssize_t data_len = Read4(data + pos); + ssize_t data_len = get_unaligned<int32_t>(data + pos); ASSERT_GT(data_len, 0); pos += 4 + data_len; ASSERT_LE(pos, size); diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index 8c4bdbaa4..5652ddf46 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp @@ -19,7 +19,9 @@ #include <sys/types.h> #include <unistd.h> +#include <memory> #include <string> +#include <vector> #include <android-base/file.h> #include <android-base/properties.h> @@ -27,12 +29,17 @@ #include <android-base/strings.h> #include <android-base/test_utils.h> #include <bootloader_message/bootloader_message.h> +#include <bsdiff.h> #include <gtest/gtest.h> #include <ziparchive/zip_archive.h> +#include <ziparchive/zip_writer.h> #include "common/test_constants.h" #include "edify/expr.h" #include "error_code.h" +#include "otautil/SysUtil.h" +#include "print_sha1.h" +#include "updater/blockimg.h" #include "updater/install.h" #include "updater/updater.h" @@ -40,7 +47,7 @@ struct selabel_handle *sehandle = nullptr; static void expect(const char* expected, const char* expr_str, CauseCode cause_code, UpdaterInfo* info = nullptr) { - Expr* e; + std::unique_ptr<Expr> e; int error_count = 0; ASSERT_EQ(0, parse_string(expr_str, &e, &error_count)); ASSERT_EQ(0, error_count); @@ -64,12 +71,19 @@ static void expect(const char* expected, const char* expr_str, CauseCode cause_c ASSERT_EQ(cause_code, state.cause_code); } +static std::string get_sha1(const std::string& content) { + uint8_t digest[SHA_DIGEST_LENGTH]; + SHA1(reinterpret_cast<const uint8_t*>(content.c_str()), content.size(), digest); + return print_sha1(digest); +} + class UpdaterTest : public ::testing::Test { - protected: - virtual void SetUp() { - RegisterBuiltins(); - RegisterInstallFunctions(); - } + protected: + virtual void SetUp() override { + RegisterBuiltins(); + RegisterInstallFunctions(); + RegisterBlockImageFunctions(); + } }; TEST_F(UpdaterTest, getprop) { @@ -113,6 +127,55 @@ TEST_F(UpdaterTest, sha1_check) { expect(nullptr, "sha1_check()", kArgsParsingFailure); } +TEST_F(UpdaterTest, apply_patch_check) { + // Zero-argument is not valid. + expect(nullptr, "apply_patch_check()", kArgsParsingFailure); + + // File not found. + expect("", "apply_patch_check(\"/doesntexist\")", kNoCause); + + std::string src_file = from_testdata_base("old.file"); + std::string src_content; + ASSERT_TRUE(android::base::ReadFileToString(src_file, &src_content)); + size_t src_size = src_content.size(); + std::string src_hash = get_sha1(src_content); + + // One-argument with EMMC:file:size:sha1 should pass the check. + std::string filename = android::base::Join( + std::vector<std::string>{ "EMMC", src_file, std::to_string(src_size), src_hash }, ":"); + std::string cmd = "apply_patch_check(\"" + filename + "\")"; + expect("t", cmd.c_str(), kNoCause); + + // EMMC:file:(size-1):sha1:(size+1):sha1 should fail the check. + std::string filename_bad = android::base::Join( + std::vector<std::string>{ "EMMC", src_file, std::to_string(src_size - 1), src_hash, + std::to_string(src_size + 1), src_hash }, + ":"); + cmd = "apply_patch_check(\"" + filename_bad + "\")"; + expect("", cmd.c_str(), kNoCause); + + // EMMC:file:(size-1):sha1:size:sha1:(size+1):sha1 should pass the check. + filename_bad = + android::base::Join(std::vector<std::string>{ "EMMC", src_file, std::to_string(src_size - 1), + src_hash, std::to_string(src_size), src_hash, + std::to_string(src_size + 1), src_hash }, + ":"); + cmd = "apply_patch_check(\"" + filename_bad + "\")"; + expect("t", cmd.c_str(), kNoCause); + + // Multiple arguments. + cmd = "apply_patch_check(\"" + filename + "\", \"wrong_sha1\", \"wrong_sha2\")"; + expect("", cmd.c_str(), kNoCause); + + cmd = "apply_patch_check(\"" + filename + "\", \"wrong_sha1\", \"" + src_hash + + "\", \"wrong_sha2\")"; + expect("t", cmd.c_str(), kNoCause); + + cmd = "apply_patch_check(\"" + filename_bad + "\", \"wrong_sha1\", \"" + src_hash + + "\", \"wrong_sha2\")"; + expect("t", cmd.c_str(), kNoCause); +} + TEST_F(UpdaterTest, file_getprop) { // file_getprop() expects two arguments. expect(nullptr, "file_getprop()", kArgsParsingFailure); @@ -447,3 +510,100 @@ TEST_F(UpdaterTest, show_progress) { // recovery-updater protocol expects 3 tokens ("progress <frac> <secs>"). ASSERT_EQ(3U, android::base::Split(cmd, " ").size()); } + +TEST_F(UpdaterTest, block_image_update) { + // Create a zip file with new_data and patch_data. + TemporaryFile zip_file; + FILE* zip_file_ptr = fdopen(zip_file.fd, "wb"); + ZipWriter zip_writer(zip_file_ptr); + + // Add a dummy new data. + ASSERT_EQ(0, zip_writer.StartEntry("new_data", 0)); + ASSERT_EQ(0, zip_writer.FinishEntry()); + + // Generate and add the patch data. + std::string src_content = std::string(4096, 'a') + std::string(4096, 'c'); + std::string tgt_content = std::string(4096, 'b') + std::string(4096, 'd'); + TemporaryFile patch_file; + ASSERT_EQ(0, bsdiff::bsdiff(reinterpret_cast<const uint8_t*>(src_content.data()), + src_content.size(), reinterpret_cast<const uint8_t*>(tgt_content.data()), + tgt_content.size(), patch_file.path, nullptr)); + std::string patch_content; + ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch_content)); + ASSERT_EQ(0, zip_writer.StartEntry("patch_data", 0)); + ASSERT_EQ(0, zip_writer.WriteBytes(patch_content.data(), patch_content.size())); + ASSERT_EQ(0, zip_writer.FinishEntry()); + + // Add two transfer lists. The first one contains a bsdiff; and we expect the update to succeed. + std::string src_hash = get_sha1(src_content); + std::string tgt_hash = get_sha1(tgt_content); + std::vector<std::string> transfer_list = { + "4", + "2", + "0", + "2", + "stash " + src_hash + " 2,0,2", + android::base::StringPrintf("bsdiff 0 %zu %s %s 2,0,2 2 - %s:2,0,2", patch_content.size(), + src_hash.c_str(), tgt_hash.c_str(), src_hash.c_str()), + "free " + src_hash, + }; + ASSERT_EQ(0, zip_writer.StartEntry("transfer_list", 0)); + std::string commands = android::base::Join(transfer_list, '\n'); + ASSERT_EQ(0, zip_writer.WriteBytes(commands.data(), commands.size())); + ASSERT_EQ(0, zip_writer.FinishEntry()); + + // Stash and free some blocks, then fail the 2nd update intentionally. + std::vector<std::string> fail_transfer_list = { + "4", + "2", + "0", + "2", + "stash " + tgt_hash + " 2,0,2", + "free " + tgt_hash, + "fail", + }; + ASSERT_EQ(0, zip_writer.StartEntry("fail_transfer_list", 0)); + std::string fail_commands = android::base::Join(fail_transfer_list, '\n'); + ASSERT_EQ(0, zip_writer.WriteBytes(fail_commands.data(), fail_commands.size())); + ASSERT_EQ(0, zip_writer.FinishEntry()); + ASSERT_EQ(0, zip_writer.Finish()); + ASSERT_EQ(0, fclose(zip_file_ptr)); + + MemMapping map; + ASSERT_EQ(0, sysMapFile(zip_file.path, &map)); + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_file.path, &handle)); + + // Set up the handler, command_pipe, patch offset & length. + UpdaterInfo updater_info; + updater_info.package_zip = handle; + TemporaryFile temp_pipe; + updater_info.cmd_pipe = fopen(temp_pipe.path, "wb"); + updater_info.package_zip_addr = map.addr; + updater_info.package_zip_len = map.length; + + // Execute the commands in the 1st transfer list. + TemporaryFile update_file; + ASSERT_TRUE(android::base::WriteStringToFile(src_content, update_file.path)); + std::string script = "block_image_update(\"" + std::string(update_file.path) + + R"(", package_extract_file("transfer_list"), "new_data", "patch_data"))"; + expect("t", script.c_str(), kNoCause, &updater_info); + // The update_file should be patched correctly. + std::string updated_content; + ASSERT_TRUE(android::base::ReadFileToString(update_file.path, &updated_content)); + ASSERT_EQ(tgt_hash, get_sha1(updated_content)); + + // Expect the 2nd update to fail, but expect the stashed blocks to be freed. + script = "block_image_update(\"" + std::string(update_file.path) + + R"(", package_extract_file("fail_transfer_list"), "new_data", "patch_data"))"; + expect("", script.c_str(), kNoCause, &updater_info); + // Updater generates the stash name based on the input file name. + std::string name_digest = get_sha1(update_file.path); + std::string stash_base = "/cache/recovery/" + name_digest; + ASSERT_EQ(0, access(stash_base.c_str(), F_OK)); + ASSERT_EQ(-1, access((stash_base + tgt_hash).c_str(), F_OK)); + ASSERT_EQ(0, rmdir(stash_base.c_str())); + + ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); + CloseArchive(handle); +} diff --git a/tests/component/verifier_test.cpp b/tests/component/verifier_test.cpp index b740af96b..07a8c960f 100644 --- a/tests/component/verifier_test.cpp +++ b/tests/component/verifier_test.cpp @@ -22,93 +22,34 @@ #include <sys/stat.h> #include <sys/types.h> -#include <memory> #include <string> #include <vector> -#include <openssl/sha.h> - +#include <android-base/file.h> #include <android-base/stringprintf.h> -#include <ziparchive/zip_archive.h> +#include <android-base/test_utils.h> -#include "common.h" #include "common/test_constants.h" #include "otautil/SysUtil.h" -#include "ui.h" #include "verifier.h" -RecoveryUI* ui = NULL; - -class MockUI : public RecoveryUI { - bool Init(const std::string&) override { - return true; - } - void SetStage(int, int) override {} - void SetBackground(Icon /*icon*/) override {} - void SetSystemUpdateText(bool /*security_update*/) override {} - - void SetProgressType(ProgressType /*determinate*/) override {} - void ShowProgress(float /*portion*/, float /*seconds*/) override {} - void SetProgress(float /*fraction*/) override {} - - void ShowText(bool /*visible*/) override {} - bool IsTextVisible() override { - return false; - } - bool WasTextEverVisible() override { - return false; - } - void Print(const char* fmt, ...) override { - va_list ap; - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - } - void PrintOnScreenOnly(const char* fmt, ...) override { - va_list ap; - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - } - void ShowFile(const char*) override {} - - void StartMenu(const char* const* /*headers*/, const char* const* /*items*/, - int /*initial_selection*/) override {} - int SelectMenu(int /*sel*/) override { - return 0; - } - void EndMenu() override {} -}; - -void -ui_print(const char* format, ...) { - va_list ap; - va_start(ap, format); - vfprintf(stdout, format, ap); - va_end(ap); -} - class VerifierTest : public testing::TestWithParam<std::vector<std::string>> { - public: - MemMapping memmap; - std::vector<Certificate> certs; - - virtual void SetUp() { - std::vector<std::string> args = GetParam(); - std::string package = from_testdata_base(args[0]); - if (sysMapFile(package.c_str(), &memmap) != 0) { - FAIL() << "Failed to mmap " << package << ": " << strerror(errno) << "\n"; - } - - for (auto it = ++(args.cbegin()); it != args.cend(); ++it) { - std::string public_key_file = from_testdata_base("testkey_" + *it + ".txt"); - ASSERT_TRUE(load_keys(public_key_file.c_str(), certs)); - } + protected: + void SetUp() override { + std::vector<std::string> args = GetParam(); + std::string package = from_testdata_base(args[0]); + if (sysMapFile(package.c_str(), &memmap) != 0) { + FAIL() << "Failed to mmap " << package << ": " << strerror(errno) << "\n"; } - static void SetUpTestCase() { - ui = new MockUI(); + for (auto it = ++args.cbegin(); it != args.cend(); ++it) { + std::string public_key_file = from_testdata_base("testkey_" + *it + ".txt"); + ASSERT_TRUE(load_keys(public_key_file.c_str(), certs)); } + } + + MemMapping memmap; + std::vector<Certificate> certs; }; class VerifierSuccessTest : public VerifierTest { @@ -117,48 +58,105 @@ class VerifierSuccessTest : public VerifierTest { class VerifierFailureTest : public VerifierTest { }; +TEST(VerifierTest, load_keys_multiple_keys) { + std::string testkey_v4; + ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("testkey_v4.txt"), &testkey_v4)); + + std::string testkey_v3; + ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("testkey_v3.txt"), &testkey_v3)); + + std::string keys = testkey_v4 + "," + testkey_v3 + "," + testkey_v4; + TemporaryFile key_file1; + ASSERT_TRUE(android::base::WriteStringToFile(keys, key_file1.path)); + std::vector<Certificate> certs; + ASSERT_TRUE(load_keys(key_file1.path, certs)); + ASSERT_EQ(3U, certs.size()); +} + +TEST(VerifierTest, load_keys_invalid_keys) { + std::vector<Certificate> certs; + ASSERT_FALSE(load_keys("/doesntexist", certs)); + + // Empty file. + TemporaryFile key_file1; + ASSERT_FALSE(load_keys(key_file1.path, certs)); + + // Invalid contents. + ASSERT_TRUE(android::base::WriteStringToFile("invalid", key_file1.path)); + ASSERT_FALSE(load_keys(key_file1.path, certs)); + + std::string testkey_v4; + ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("testkey_v4.txt"), &testkey_v4)); + + // Invalid key version: "v4 ..." => "v6 ...". + std::string invalid_key2(testkey_v4); + invalid_key2[1] = '6'; + TemporaryFile key_file2; + ASSERT_TRUE(android::base::WriteStringToFile(invalid_key2, key_file2.path)); + ASSERT_FALSE(load_keys(key_file2.path, certs)); + + // Invalid key content: inserted extra bytes ",2209831334". + std::string invalid_key3(testkey_v4); + invalid_key3.insert(invalid_key2.size() - 2, ",2209831334"); + TemporaryFile key_file3; + ASSERT_TRUE(android::base::WriteStringToFile(invalid_key3, key_file3.path)); + ASSERT_FALSE(load_keys(key_file3.path, certs)); + + // Invalid key: the last key must not end with an extra ','. + std::string invalid_key4 = testkey_v4 + ","; + TemporaryFile key_file4; + ASSERT_TRUE(android::base::WriteStringToFile(invalid_key4, key_file4.path)); + ASSERT_FALSE(load_keys(key_file4.path, certs)); + + // Invalid key separator. + std::string invalid_key5 = testkey_v4 + ";" + testkey_v4; + TemporaryFile key_file5; + ASSERT_TRUE(android::base::WriteStringToFile(invalid_key5, key_file5.path)); + ASSERT_FALSE(load_keys(key_file5.path, certs)); +} + TEST_P(VerifierSuccessTest, VerifySucceed) { - ASSERT_EQ(verify_file(memmap.addr, memmap.length, certs), VERIFY_SUCCESS); + ASSERT_EQ(verify_file(memmap.addr, memmap.length, certs, nullptr), VERIFY_SUCCESS); } TEST_P(VerifierFailureTest, VerifyFailure) { - ASSERT_EQ(verify_file(memmap.addr, memmap.length, certs), VERIFY_FAILURE); + ASSERT_EQ(verify_file(memmap.addr, memmap.length, certs, nullptr), VERIFY_FAILURE); } INSTANTIATE_TEST_CASE_P(SingleKeySuccess, VerifierSuccessTest, - ::testing::Values( - std::vector<std::string>({"otasigned_v1.zip", "v1"}), - std::vector<std::string>({"otasigned_v2.zip", "v2"}), - std::vector<std::string>({"otasigned_v3.zip", "v3"}), - std::vector<std::string>({"otasigned_v4.zip", "v4"}), - std::vector<std::string>({"otasigned_v5.zip", "v5"}))); + ::testing::Values( + std::vector<std::string>({"otasigned_v1.zip", "v1"}), + std::vector<std::string>({"otasigned_v2.zip", "v2"}), + std::vector<std::string>({"otasigned_v3.zip", "v3"}), + std::vector<std::string>({"otasigned_v4.zip", "v4"}), + std::vector<std::string>({"otasigned_v5.zip", "v5"}))); INSTANTIATE_TEST_CASE_P(MultiKeySuccess, VerifierSuccessTest, - ::testing::Values( - std::vector<std::string>({"otasigned_v1.zip", "v1", "v2"}), - std::vector<std::string>({"otasigned_v2.zip", "v5", "v2"}), - std::vector<std::string>({"otasigned_v3.zip", "v5", "v1", "v3"}), - std::vector<std::string>({"otasigned_v4.zip", "v5", "v1", "v4"}), - std::vector<std::string>({"otasigned_v5.zip", "v4", "v1", "v5"}))); + ::testing::Values( + std::vector<std::string>({"otasigned_v1.zip", "v1", "v2"}), + std::vector<std::string>({"otasigned_v2.zip", "v5", "v2"}), + std::vector<std::string>({"otasigned_v3.zip", "v5", "v1", "v3"}), + std::vector<std::string>({"otasigned_v4.zip", "v5", "v1", "v4"}), + std::vector<std::string>({"otasigned_v5.zip", "v4", "v1", "v5"}))); INSTANTIATE_TEST_CASE_P(WrongKey, VerifierFailureTest, - ::testing::Values( - std::vector<std::string>({"otasigned_v1.zip", "v2"}), - std::vector<std::string>({"otasigned_v2.zip", "v1"}), - std::vector<std::string>({"otasigned_v3.zip", "v5"}), - std::vector<std::string>({"otasigned_v4.zip", "v5"}), - std::vector<std::string>({"otasigned_v5.zip", "v3"}))); + ::testing::Values( + std::vector<std::string>({"otasigned_v1.zip", "v2"}), + std::vector<std::string>({"otasigned_v2.zip", "v1"}), + std::vector<std::string>({"otasigned_v3.zip", "v5"}), + std::vector<std::string>({"otasigned_v4.zip", "v5"}), + std::vector<std::string>({"otasigned_v5.zip", "v3"}))); INSTANTIATE_TEST_CASE_P(WrongHash, VerifierFailureTest, - ::testing::Values( - std::vector<std::string>({"otasigned_v1.zip", "v3"}), - std::vector<std::string>({"otasigned_v2.zip", "v4"}), - std::vector<std::string>({"otasigned_v3.zip", "v1"}), - std::vector<std::string>({"otasigned_v4.zip", "v2"}))); + ::testing::Values( + std::vector<std::string>({"otasigned_v1.zip", "v3"}), + std::vector<std::string>({"otasigned_v2.zip", "v4"}), + std::vector<std::string>({"otasigned_v3.zip", "v1"}), + std::vector<std::string>({"otasigned_v4.zip", "v2"}))); INSTANTIATE_TEST_CASE_P(BadPackage, VerifierFailureTest, - ::testing::Values( - std::vector<std::string>({"random.zip", "v1"}), - std::vector<std::string>({"fake-eocd.zip", "v1"}), - std::vector<std::string>({"alter-metadata.zip", "v1"}), - std::vector<std::string>({"alter-footer.zip", "v1"}))); + ::testing::Values( + std::vector<std::string>({"random.zip", "v1"}), + std::vector<std::string>({"fake-eocd.zip", "v1"}), + std::vector<std::string>({"alter-metadata.zip", "v1"}), + std::vector<std::string>({"alter-footer.zip", "v1"}))); |