diff options
-rw-r--r-- | Android.mk | 21 | ||||
-rw-r--r-- | README.md | 17 | ||||
-rw-r--r-- | applypatch/Android.mk | 30 | ||||
-rw-r--r-- | applypatch/Makefile | 32 | ||||
-rw-r--r-- | applypatch/applypatch.cpp | 346 | ||||
-rw-r--r-- | applypatch/applypatch.h | 28 | ||||
-rw-r--r-- | applypatch/bsdiff.cpp | 1 | ||||
-rw-r--r-- | applypatch/bspatch.cpp | 41 | ||||
-rw-r--r-- | applypatch/freecache.cpp | 141 | ||||
-rw-r--r-- | applypatch/imgdiff.cpp | 24 | ||||
-rw-r--r-- | applypatch/imgpatch.cpp | 84 | ||||
-rw-r--r-- | applypatch/include/applypatch/imgpatch.h | 26 | ||||
-rw-r--r-- | applypatch/libimgpatch.pc | 6 | ||||
-rw-r--r-- | applypatch/main.cpp | 116 | ||||
-rw-r--r-- | bootloader.cpp | 133 | ||||
-rw-r--r-- | edify/lexer.ll | 24 | ||||
-rw-r--r-- | edify/main.cpp | 5 | ||||
-rw-r--r-- | install.cpp | 23 | ||||
-rw-r--r-- | minadbd/services.cpp | 3 | ||||
-rw-r--r-- | minui/resources.cpp | 22 | ||||
-rw-r--r-- | minzip/Android.mk | 2 | ||||
-rw-r--r-- | minzip/DirUtil.cpp (renamed from minzip/DirUtil.c) | 61 | ||||
-rw-r--r-- | otafault/Android.mk | 58 | ||||
-rw-r--r-- | otafault/ota_io.cpp | 160 | ||||
-rw-r--r-- | otafault/ota_io.h | 49 | ||||
-rw-r--r-- | otafault/test.cpp | 32 | ||||
-rw-r--r-- | print_sha1.h | 10 | ||||
-rw-r--r-- | tests/Android.mk | 35 | ||||
-rw-r--r-- | tests/component/verifier_test.cpp (renamed from verifier_test.cpp) | 212 | ||||
-rw-r--r-- | tests/testdata/alter-footer.zip (renamed from testdata/alter-footer.zip) | bin | 4009 -> 4009 bytes | |||
-rw-r--r-- | tests/testdata/alter-metadata.zip (renamed from testdata/alter-metadata.zip) | bin | 4009 -> 4009 bytes | |||
-rw-r--r-- | tests/testdata/fake-eocd.zip (renamed from testdata/fake-eocd.zip) | bin | 4313 -> 4313 bytes | |||
-rw-r--r-- | tests/testdata/jarsigned.zip (renamed from testdata/jarsigned.zip) | bin | 2271 -> 2271 bytes | |||
-rw-r--r-- | tests/testdata/otasigned.zip (renamed from testdata/otasigned.zip) | bin | 4009 -> 4009 bytes | |||
-rw-r--r-- | tests/testdata/otasigned_ecdsa_sha256.zip (renamed from testdata/otasigned_ecdsa_sha256.zip) | bin | 3085 -> 3085 bytes | |||
-rw-r--r-- | tests/testdata/otasigned_f4.zip (renamed from testdata/otasigned_f4.zip) | bin | 5195 -> 5195 bytes | |||
-rw-r--r-- | tests/testdata/otasigned_f4_sha256.zip (renamed from testdata/otasigned_f4_sha256.zip) | bin | 5319 -> 5319 bytes | |||
-rw-r--r-- | tests/testdata/otasigned_sha256.zip (renamed from testdata/otasigned_sha256.zip) | bin | 5326 -> 5326 bytes | |||
-rw-r--r-- | tests/testdata/random.zip (renamed from testdata/random.zip) | bin | 1024 -> 1024 bytes | |||
-rw-r--r-- | tests/testdata/test_f4.pk8 (renamed from testdata/test_f4.pk8) | bin | 1217 -> 1217 bytes | |||
-rw-r--r-- | tests/testdata/test_f4.x509.pem (renamed from testdata/test_f4.x509.pem) | 0 | ||||
-rw-r--r-- | tests/testdata/test_f4_sha256.x509.pem (renamed from testdata/test_f4_sha256.x509.pem) | 0 | ||||
-rw-r--r-- | tests/testdata/testkey.pk8 (renamed from testdata/testkey.pk8) | bin | 1217 -> 1217 bytes | |||
-rw-r--r-- | tests/testdata/testkey.x509.pem (renamed from testdata/testkey.x509.pem) | 0 | ||||
-rw-r--r-- | tests/testdata/testkey_ecdsa.pk8 (renamed from testdata/testkey_ecdsa.pk8) | bin | 138 -> 138 bytes | |||
-rw-r--r-- | tests/testdata/testkey_ecdsa.x509.pem (renamed from testdata/testkey_ecdsa.x509.pem) | 0 | ||||
-rw-r--r-- | tests/testdata/testkey_sha256.x509.pem (renamed from testdata/testkey_sha256.x509.pem) | 0 | ||||
-rw-r--r-- | tests/testdata/unsigned.zip (renamed from testdata/unsigned.zip) | bin | 376 -> 376 bytes | |||
-rw-r--r-- | tests/unit/asn1_decoder_test.cpp (renamed from tests/asn1_decoder_test.cpp) | 0 | ||||
-rw-r--r-- | uncrypt/uncrypt.cpp | 432 | ||||
-rw-r--r-- | uncrypt/uncrypt.rc | 10 | ||||
-rw-r--r-- | updater/Android.mk | 4 | ||||
-rw-r--r-- | updater/blockimg.cpp | 44 | ||||
-rw-r--r-- | updater/install.cpp | 159 | ||||
-rw-r--r-- | verifier.cpp | 265 | ||||
-rw-r--r-- | verifier.h | 23 | ||||
-rwxr-xr-x | verifier_test.sh | 121 |
57 files changed, 1532 insertions, 1268 deletions
diff --git a/Android.mk b/Android.mk index 602a85673..4da34eef5 100644 --- a/Android.mk +++ b/Android.mk @@ -104,28 +104,10 @@ LOCAL_CLANG := true LOCAL_MODULE := libverifier LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := \ - asn1_decoder.cpp -include $(BUILD_STATIC_LIBRARY) - -include $(CLEAR_VARS) -LOCAL_CLANG := true -LOCAL_MODULE := verifier_test -LOCAL_FORCE_STATIC_EXECUTABLE := true -LOCAL_MODULE_TAGS := tests -LOCAL_CFLAGS += -Wno-unused-parameter -LOCAL_SRC_FILES := \ - verifier_test.cpp \ asn1_decoder.cpp \ verifier.cpp \ ui.cpp -LOCAL_STATIC_LIBRARIES := \ - libmincrypt \ - libminui \ - libminzip \ - libcutils \ - libc -include $(BUILD_EXECUTABLE) - +include $(BUILD_STATIC_LIBRARY) include $(LOCAL_PATH)/minui/Android.mk \ $(LOCAL_PATH)/minzip/Android.mk \ @@ -135,6 +117,7 @@ include $(LOCAL_PATH)/minui/Android.mk \ $(LOCAL_PATH)/tools/Android.mk \ $(LOCAL_PATH)/edify/Android.mk \ $(LOCAL_PATH)/uncrypt/Android.mk \ + $(LOCAL_PATH)/otafault/Android.mk \ $(LOCAL_PATH)/updater/Android.mk \ $(LOCAL_PATH)/update_verifier/Android.mk \ $(LOCAL_PATH)/applypatch/Android.mk @@ -10,3 +10,20 @@ Quick turn-around testing # without flashing the recovery partition: adb reboot bootloader fastboot boot $ANDROID_PRODUCT_OUT/recovery.img + +Running the tests +----------------- + # After setting up environment and lunch. + mmma -j bootable/recovery + + # Running the tests on device. + adb root + adb sync data + + # 32-bit device + adb shell /data/nativetest/recovery_unit_test/recovery_unit_test + adb shell /data/nativetest/recovery_component_test/recovery_component_test + + # Or 64-bit device + adb shell /data/nativetest64/recovery_unit_test/recovery_unit_test + adb shell /data/nativetest64/recovery_component_test/recovery_component_test diff --git a/applypatch/Android.mk b/applypatch/Android.mk index 93a272997..90a86dcb0 100644 --- a/applypatch/Android.mk +++ b/applypatch/Android.mk @@ -20,18 +20,42 @@ LOCAL_CLANG := true LOCAL_SRC_FILES := applypatch.cpp bspatch.cpp freecache.cpp imgpatch.cpp utils.cpp LOCAL_MODULE := libapplypatch LOCAL_MODULE_TAGS := eng -LOCAL_C_INCLUDES += external/bzip2 external/zlib bootable/recovery -LOCAL_STATIC_LIBRARIES += libbase libmtdutils libmincrypt libbz libz +LOCAL_C_INCLUDES += bootable/recovery +LOCAL_STATIC_LIBRARIES += libbase libotafault libmtdutils libcrypto_static libbz libz + +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) + +LOCAL_CLANG := true +LOCAL_SRC_FILES := bspatch.cpp imgpatch.cpp utils.cpp +LOCAL_MODULE := libimgpatch +LOCAL_C_INCLUDES += bootable/recovery +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +LOCAL_STATIC_LIBRARIES += libcrypto_static libbz libz include $(BUILD_STATIC_LIBRARY) +ifeq ($(HOST_OS),linux) +include $(CLEAR_VARS) + +LOCAL_CLANG := true +LOCAL_SRC_FILES := bspatch.cpp imgpatch.cpp utils.cpp +LOCAL_MODULE := libimgpatch +LOCAL_C_INCLUDES += bootable/recovery +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +LOCAL_STATIC_LIBRARIES += libcrypto_static libbz libz + +include $(BUILD_HOST_STATIC_LIBRARY) +endif # HOST_OS == linux + include $(CLEAR_VARS) LOCAL_CLANG := true LOCAL_SRC_FILES := main.cpp LOCAL_MODULE := applypatch LOCAL_C_INCLUDES += bootable/recovery -LOCAL_STATIC_LIBRARIES += libapplypatch libbase libmtdutils libmincrypt libbz +LOCAL_STATIC_LIBRARIES += libapplypatch libbase libotafault libmtdutils libcrypto_static libbz libedify LOCAL_SHARED_LIBRARIES += libz libcutils libc include $(BUILD_EXECUTABLE) diff --git a/applypatch/Makefile b/applypatch/Makefile new file mode 100644 index 000000000..fa6298d46 --- /dev/null +++ b/applypatch/Makefile @@ -0,0 +1,32 @@ +# Copyright (C) 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This file is for building imgdiff in Chrome OS. + +CPPFLAGS += -iquote.. +CXXFLAGS += -std=c++11 -O3 -Wall -Werror +LDLIBS += -lbz2 -lz + +.PHONY: all clean + +all: imgdiff libimgpatch.a + +clean: + rm -f *.o imgdiff libimgpatch.a + +imgdiff: imgdiff.o bsdiff.o utils.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDLIBS) -o $@ $^ + +libimgpatch.a: imgpatch.o bspatch.o utils.o + ${AR} rcs $@ $^ diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp index f9425af93..f66dbe3bf 100644 --- a/applypatch/applypatch.cpp +++ b/applypatch/applypatch.cpp @@ -25,13 +25,17 @@ #include <sys/types.h> #include <unistd.h> +#include <memory> +#include <string> + #include <android-base/strings.h> -#include "mincrypt/sha.h" +#include "openssl/sha.h" #include "applypatch.h" #include "mtdutils/mtdutils.h" #include "edify/expr.h" #include "print_sha1.h" +#include "otafault/ota_io.h" static int LoadPartitionContents(const char* filename, FileContents* file); static ssize_t FileSink(const unsigned char* data, ssize_t len, void* token); @@ -41,7 +45,7 @@ static int GenerateTarget(FileContents* source_file, const Value* copy_patch_value, const char* source_filename, const char* target_filename, - const uint8_t target_sha1[SHA_DIGEST_SIZE], + const uint8_t target_sha1[SHA_DIGEST_LENGTH], size_t target_size, const Value* bonus_data); @@ -52,8 +56,6 @@ static bool mtd_partitions_scanned = false; // // Return 0 on success. int LoadFileContents(const char* filename, FileContents* file) { - file->data = NULL; - // A special 'filename' beginning with "MTD:" or "EMMC:" means to // load the contents of a partition. if (strncmp(filename, "MTD:", 4) == 0 || @@ -66,27 +68,22 @@ int LoadFileContents(const char* filename, FileContents* file) { return -1; } - file->size = file->st.st_size; - file->data = reinterpret_cast<unsigned char*>(malloc(file->size)); - - FILE* f = fopen(filename, "rb"); + std::vector<unsigned char> data(file->st.st_size); + FILE* f = ota_fopen(filename, "rb"); if (f == NULL) { printf("failed to open \"%s\": %s\n", filename, strerror(errno)); - free(file->data); - file->data = NULL; return -1; } - size_t bytes_read = fread(file->data, 1, file->size, f); - if (bytes_read != static_cast<size_t>(file->size)) { - printf("short read of \"%s\" (%zu bytes of %zd)\n", filename, bytes_read, file->size); - free(file->data); - file->data = NULL; + size_t bytes_read = ota_fread(data.data(), 1, data.size(), f); + if (bytes_read != data.size()) { + printf("short read of \"%s\" (%zu bytes of %zu)\n", filename, bytes_read, data.size()); + ota_fclose(f); return -1; } - fclose(f); - - SHA_hash(file->data, file->size, file->sha1); + ota_fclose(f); + file->data = std::move(data); + SHA1(file->data.data(), file->data.size(), file->sha1); return 0; } @@ -173,7 +170,7 @@ static int LoadPartitionContents(const char* filename, FileContents* file) { } case EMMC: - dev = fopen(partition, "rb"); + dev = ota_fopen(partition, "rb"); if (dev == NULL) { printf("failed to open emmc partition \"%s\": %s\n", partition, strerror(errno)); return -1; @@ -181,55 +178,53 @@ static int LoadPartitionContents(const char* filename, FileContents* file) { } SHA_CTX sha_ctx; - SHA_init(&sha_ctx); - uint8_t parsed_sha[SHA_DIGEST_SIZE]; + SHA1_Init(&sha_ctx); + uint8_t parsed_sha[SHA_DIGEST_LENGTH]; // Allocate enough memory to hold the largest size. - file->data = reinterpret_cast<unsigned char*>(malloc(size[index[pairs-1]])); - char* p = (char*)file->data; - file->size = 0; // # bytes read so far + std::vector<unsigned char> data(size[index[pairs-1]]); + char* p = reinterpret_cast<char*>(data.data()); + size_t data_size = 0; // # bytes read so far bool found = false; for (size_t i = 0; i < pairs; ++i) { // Read enough additional bytes to get us up to the next size. (Again, // we're trying the possibilities in order of increasing size). - size_t next = size[index[i]] - file->size; - size_t read = 0; + size_t next = size[index[i]] - data_size; if (next > 0) { + size_t read = 0; switch (type) { case MTD: read = mtd_read_data(ctx, p, next); break; case EMMC: - read = fread(p, 1, next, dev); + read = ota_fread(p, 1, next, dev); break; } if (next != read) { printf("short read (%zu bytes of %zu) for partition \"%s\"\n", read, next, partition); - free(file->data); - file->data = NULL; return -1; } - SHA_update(&sha_ctx, p, read); - file->size += read; + SHA1_Update(&sha_ctx, p, read); + data_size += read; + p += read; } // Duplicate the SHA context and finalize the duplicate so we can // check it against this pair's expected hash. SHA_CTX temp_ctx; memcpy(&temp_ctx, &sha_ctx, sizeof(SHA_CTX)); - const uint8_t* sha_so_far = SHA_final(&temp_ctx); + uint8_t sha_so_far[SHA_DIGEST_LENGTH]; + SHA1_Final(sha_so_far, &temp_ctx); if (ParseSha1(sha1sum[index[i]].c_str(), parsed_sha) != 0) { printf("failed to parse sha1 %s in %s\n", sha1sum[index[i]].c_str(), filename); - free(file->data); - file->data = NULL; return -1; } - if (memcmp(sha_so_far, parsed_sha, SHA_DIGEST_SIZE) == 0) { + if (memcmp(sha_so_far, parsed_sha, SHA_DIGEST_LENGTH) == 0) { // we have a match. stop reading the partition; we'll return // the data we've read so far. printf("partition read matched size %zu sha %s\n", @@ -237,8 +232,6 @@ static int LoadPartitionContents(const char* filename, FileContents* file) { found = true; break; } - - p += read; } switch (type) { @@ -247,7 +240,7 @@ static int LoadPartitionContents(const char* filename, FileContents* file) { break; case EMMC: - fclose(dev); + ota_fclose(dev); break; } @@ -255,16 +248,13 @@ static int LoadPartitionContents(const char* filename, FileContents* file) { if (!found) { // Ran off the end of the list of (size,sha1) pairs without finding a match. printf("contents of partition \"%s\" didn't match %s\n", partition, filename); - free(file->data); - file->data = NULL; return -1; } - const uint8_t* sha_final = SHA_final(&sha_ctx); - for (size_t i = 0; i < SHA_DIGEST_SIZE; ++i) { - file->sha1[i] = sha_final[i]; - } + SHA1_Final(file->sha1, &sha_ctx); + data.resize(data_size); + file->data = std::move(data); // Fake some stat() info. file->st.st_mode = 0644; file->st.st_uid = 0; @@ -277,24 +267,24 @@ static int LoadPartitionContents(const char* filename, FileContents* file) { // Save the contents of the given FileContents object under the given // filename. Return 0 on success. int SaveFileContents(const char* filename, const FileContents* file) { - int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR); + int fd = ota_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR); if (fd < 0) { printf("failed to open \"%s\" for write: %s\n", filename, strerror(errno)); return -1; } - ssize_t bytes_written = FileSink(file->data, file->size, &fd); - if (bytes_written != file->size) { - printf("short write of \"%s\" (%zd bytes of %zd) (%s)\n", - filename, bytes_written, file->size, strerror(errno)); - close(fd); + ssize_t bytes_written = FileSink(file->data.data(), file->data.size(), &fd); + if (bytes_written != static_cast<ssize_t>(file->data.size())) { + printf("short write of \"%s\" (%zd bytes of %zu) (%s)\n", + filename, bytes_written, file->data.size(), strerror(errno)); + ota_close(fd); return -1; } - if (fsync(fd) != 0) { + if (ota_fsync(fd) != 0) { printf("fsync of \"%s\" failed: %s\n", filename, strerror(errno)); return -1; } - if (close(fd) != 0) { + if (ota_close(fd) != 0) { printf("close of \"%s\" failed: %s\n", filename, strerror(errno)); return -1; } @@ -315,7 +305,7 @@ int SaveFileContents(const char* filename, const FileContents* file) { // "MTD:<partition>[:...]" or "EMMC:<partition_device>[:...]". The target name // might contain multiple colons, but WriteToPartition() only uses the first // two and ignores the rest. Return 0 on success. -int WriteToPartition(unsigned char* data, size_t len, const char* target) { +int WriteToPartition(const unsigned char* data, size_t len, const char* target) { std::string copy(target); std::vector<std::string> pieces = android::base::Split(copy, ":"); @@ -354,7 +344,7 @@ int WriteToPartition(unsigned char* data, size_t len, const char* target) { return -1; } - size_t written = mtd_write_data(ctx, reinterpret_cast<char*>(data), len); + size_t written = mtd_write_data(ctx, reinterpret_cast<const char*>(data), len); if (written != len) { printf("only wrote %zu of %zu bytes to MTD %s\n", written, len, partition); mtd_write_close(ctx); @@ -377,7 +367,7 @@ int WriteToPartition(unsigned char* data, size_t len, const char* target) { case EMMC: { size_t start = 0; bool success = false; - int fd = open(partition, O_RDWR | O_SYNC); + int fd = ota_open(partition, O_RDWR | O_SYNC); if (fd < 0) { printf("failed to open %s: %s\n", partition, strerror(errno)); return -1; @@ -392,22 +382,22 @@ int WriteToPartition(unsigned char* data, size_t len, const char* target) { size_t to_write = len - start; if (to_write > 1<<20) to_write = 1<<20; - ssize_t written = TEMP_FAILURE_RETRY(write(fd, data+start, to_write)); + ssize_t written = TEMP_FAILURE_RETRY(ota_write(fd, data+start, to_write)); if (written == -1) { printf("failed write writing to %s: %s\n", partition, strerror(errno)); return -1; } start += written; } - if (fsync(fd) != 0) { + if (ota_fsync(fd) != 0) { printf("failed to sync to %s (%s)\n", partition, strerror(errno)); return -1; } - if (close(fd) != 0) { + if (ota_close(fd) != 0) { printf("failed to close %s (%s)\n", partition, strerror(errno)); return -1; } - fd = open(partition, O_RDONLY); + fd = ota_open(partition, O_RDONLY); if (fd < 0) { printf("failed to reopen %s for verify (%s)\n", partition, strerror(errno)); return -1; @@ -416,13 +406,13 @@ int WriteToPartition(unsigned char* data, size_t len, const char* target) { // Drop caches so our subsequent verification read // won't just be reading the cache. sync(); - int dc = open("/proc/sys/vm/drop_caches", O_WRONLY); - if (TEMP_FAILURE_RETRY(write(dc, "3\n", 2)) == -1) { + int dc = ota_open("/proc/sys/vm/drop_caches", O_WRONLY); + if (TEMP_FAILURE_RETRY(ota_write(dc, "3\n", 2)) == -1) { printf("write to /proc/sys/vm/drop_caches failed: %s\n", strerror(errno)); } else { printf(" caches dropped\n"); } - close(dc); + ota_close(dc); sleep(1); // verify @@ -442,7 +432,7 @@ int WriteToPartition(unsigned char* data, size_t len, const char* target) { size_t so_far = 0; while (so_far < to_read) { ssize_t read_count = - TEMP_FAILURE_RETRY(read(fd, buffer+so_far, to_read-so_far)); + TEMP_FAILURE_RETRY(ota_read(fd, buffer+so_far, to_read-so_far)); if (read_count == -1) { printf("verify read error %s at %zu: %s\n", partition, p, strerror(errno)); @@ -474,7 +464,7 @@ int WriteToPartition(unsigned char* data, size_t len, const char* target) { return -1; } - if (close(fd) != 0) { + if (ota_close(fd) != 0) { printf("error closing %s (%s)\n", partition, strerror(errno)); return -1; } @@ -494,7 +484,7 @@ int WriteToPartition(unsigned char* data, size_t len, const char* target) { int ParseSha1(const char* str, uint8_t* digest) { const char* ps = str; uint8_t* pd = digest; - for (int i = 0; i < SHA_DIGEST_SIZE * 2; ++i, ++ps) { + for (int i = 0; i < SHA_DIGEST_LENGTH * 2; ++i, ++ps) { int digit; if (*ps >= '0' && *ps <= '9') { digit = *ps - '0'; @@ -521,10 +511,10 @@ int ParseSha1(const char* str, uint8_t* digest) { // found. int FindMatchingPatch(uint8_t* sha1, char* const * const patch_sha1_str, int num_patches) { - uint8_t patch_sha1[SHA_DIGEST_SIZE]; + uint8_t patch_sha1[SHA_DIGEST_LENGTH]; for (int i = 0; i < num_patches; ++i) { if (ParseSha1(patch_sha1_str[i], patch_sha1) == 0 && - memcmp(patch_sha1, sha1, SHA_DIGEST_SIZE) == 0) { + memcmp(patch_sha1, sha1, SHA_DIGEST_LENGTH) == 0) { return i; } } @@ -537,7 +527,6 @@ int FindMatchingPatch(uint8_t* sha1, char* const * const patch_sha1_str, int applypatch_check(const char* filename, int num_patches, char** const patch_sha1_str) { FileContents file; - file.data = NULL; // It's okay to specify no sha1s; the check will pass if the // LoadFileContents is successful. (Useful for reading @@ -549,9 +538,6 @@ int applypatch_check(const char* filename, int num_patches, printf("file \"%s\" doesn't have any of expected " "sha1 sums; checking cache\n", filename); - free(file.data); - file.data = NULL; - // If the source file is missing or corrupted, it might be because // we were killed in the middle of patching it. A copy of it // should have been made in CACHE_TEMP_SOURCE. If that file @@ -565,12 +551,9 @@ int applypatch_check(const char* filename, int num_patches, if (FindMatchingPatch(file.sha1, patch_sha1_str, num_patches) < 0) { printf("cache bits don't match any sha1 for \"%s\"\n", filename); - free(file.data); return 1; } } - - free(file.data); return 0; } @@ -580,11 +563,11 @@ int ShowLicenses() { } ssize_t FileSink(const unsigned char* data, ssize_t len, void* token) { - int fd = *reinterpret_cast<int *>(token); + int fd = *static_cast<int*>(token); ssize_t done = 0; ssize_t wrote; while (done < len) { - wrote = TEMP_FAILURE_RETRY(write(fd, data+done, len-done)); + wrote = TEMP_FAILURE_RETRY(ota_write(fd, data+done, len-done)); if (wrote == -1) { printf("error writing %zd bytes: %s\n", (len-done), strerror(errno)); return done; @@ -594,19 +577,9 @@ ssize_t FileSink(const unsigned char* data, ssize_t len, void* token) { return done; } -typedef struct { - unsigned char* buffer; - ssize_t size; - ssize_t pos; -} MemorySinkInfo; - ssize_t MemorySink(const unsigned char* data, ssize_t len, void* token) { - MemorySinkInfo* msi = reinterpret_cast<MemorySinkInfo*>(token); - if (msi->size - msi->pos < len) { - return -1; - } - memcpy(msi->buffer + msi->pos, data, len); - msi->pos += len; + std::string* s = static_cast<std::string*>(token); + s->append(reinterpret_cast<const char*>(data), len); return len; } @@ -670,7 +643,7 @@ int applypatch(const char* source_filename, target_filename = source_filename; } - uint8_t target_sha1[SHA_DIGEST_SIZE]; + uint8_t target_sha1[SHA_DIGEST_LENGTH]; if (ParseSha1(target_sha1_str, target_sha1) != 0) { printf("failed to parse tgt-sha1 \"%s\"\n", target_sha1_str); return 1; @@ -678,33 +651,29 @@ int applypatch(const char* source_filename, FileContents copy_file; FileContents source_file; - copy_file.data = NULL; - source_file.data = NULL; const Value* source_patch_value = NULL; const Value* copy_patch_value = NULL; // We try to load the target file into the source_file object. if (LoadFileContents(target_filename, &source_file) == 0) { - if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_SIZE) == 0) { + if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_LENGTH) == 0) { // The early-exit case: the patch was already applied, this file // has the desired hash, nothing for us to do. printf("already %s\n", short_sha1(target_sha1).c_str()); - free(source_file.data); return 0; } } - if (source_file.data == NULL || + if (source_file.data.empty() || (target_filename != source_filename && strcmp(target_filename, source_filename) != 0)) { // Need to load the source file: either we failed to load the // target file, or we did but it's different from the source file. - free(source_file.data); - source_file.data = NULL; + source_file.data.clear(); LoadFileContents(source_filename, &source_file); } - if (source_file.data != NULL) { + if (!source_file.data.empty()) { int to_use = FindMatchingPatch(source_file.sha1, patch_sha1_str, num_patches); if (to_use >= 0) { source_patch_value = patch_data[to_use]; @@ -712,8 +681,7 @@ int applypatch(const char* source_filename, } if (source_patch_value == NULL) { - free(source_file.data); - source_file.data = NULL; + source_file.data.clear(); printf("source file is bad; trying copy\n"); if (LoadFileContents(CACHE_TEMP_SOURCE, ©_file) < 0) { @@ -730,19 +698,14 @@ int applypatch(const char* source_filename, if (copy_patch_value == NULL) { // fail. printf("copy file doesn't match source SHA-1s either\n"); - free(copy_file.data); return 1; } } - int result = GenerateTarget(&source_file, source_patch_value, - ©_file, copy_patch_value, - source_filename, target_filename, - target_sha1, target_size, bonus_data); - free(source_file.data); - free(copy_file.data); - - return result; + return GenerateTarget(&source_file, source_patch_value, + ©_file, copy_patch_value, + source_filename, target_filename, + target_sha1, target_size, bonus_data); } /* @@ -756,14 +719,13 @@ int applypatch_flash(const char* source_filename, const char* target_filename, const char* target_sha1_str, size_t target_size) { printf("flash %s: ", target_filename); - uint8_t target_sha1[SHA_DIGEST_SIZE]; + uint8_t target_sha1[SHA_DIGEST_LENGTH]; if (ParseSha1(target_sha1_str, target_sha1) != 0) { printf("failed to parse tgt-sha1 \"%s\"\n", target_sha1_str); return 1; } FileContents source_file; - source_file.data = NULL; std::string target_str(target_filename); std::vector<std::string> pieces = android::base::Split(target_str, ":"); @@ -777,32 +739,27 @@ int applypatch_flash(const char* source_filename, const char* target_filename, pieces.push_back(target_sha1_str); std::string fullname = android::base::Join(pieces, ':'); if (LoadPartitionContents(fullname.c_str(), &source_file) == 0 && - memcmp(source_file.sha1, target_sha1, SHA_DIGEST_SIZE) == 0) { + memcmp(source_file.sha1, target_sha1, SHA_DIGEST_LENGTH) == 0) { // The early-exit case: the image was already applied, this partition // has the desired hash, nothing for us to do. printf("already %s\n", short_sha1(target_sha1).c_str()); - free(source_file.data); return 0; } if (LoadFileContents(source_filename, &source_file) == 0) { - if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_SIZE) != 0) { + if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_LENGTH) != 0) { // The source doesn't have desired checksum. printf("source \"%s\" doesn't have expected sha1 sum\n", source_filename); printf("expected: %s, found: %s\n", short_sha1(target_sha1).c_str(), short_sha1(source_file.sha1).c_str()); - free(source_file.data); return 1; } } - if (WriteToPartition(source_file.data, target_size, target_filename) != 0) { + if (WriteToPartition(source_file.data.data(), target_size, target_filename) != 0) { printf("write of copied data to %s failed\n", target_filename); - free(source_file.data); return 1; } - - free(source_file.data); return 0; } @@ -812,36 +769,57 @@ static int GenerateTarget(FileContents* source_file, const Value* copy_patch_value, const char* source_filename, const char* target_filename, - const uint8_t target_sha1[SHA_DIGEST_SIZE], + const uint8_t target_sha1[SHA_DIGEST_LENGTH], size_t target_size, const Value* bonus_data) { int retry = 1; SHA_CTX ctx; - int output; - MemorySinkInfo msi; + std::string memory_sink_str; FileContents* source_to_use; - char* outname; int made_copy = 0; + bool target_is_partition = (strncmp(target_filename, "MTD:", 4) == 0 || + strncmp(target_filename, "EMMC:", 5) == 0); + const std::string tmp_target_filename = std::string(target_filename) + ".patch"; + // assume that target_filename (eg "/system/app/Foo.apk") is located // on the same filesystem as its top-level directory ("/system"). // We need something that exists for calling statfs(). - char target_fs[strlen(target_filename)+1]; - char* slash = strchr(target_filename+1, '/'); - if (slash != NULL) { - int count = slash - target_filename; - strncpy(target_fs, target_filename, count); - target_fs[count] = '\0'; + std::string target_fs = target_filename; + auto slash_pos = target_fs.find('/', 1); + if (slash_pos != std::string::npos) { + target_fs.resize(slash_pos); + } + + const Value* patch; + if (source_patch_value != NULL) { + source_to_use = source_file; + patch = source_patch_value; } else { - strcpy(target_fs, target_filename); + source_to_use = copy_file; + patch = copy_patch_value; + } + if (patch->type != VAL_BLOB) { + printf("patch is not a blob\n"); + return 1; + } + char* header = patch->data; + ssize_t header_bytes_read = patch->size; + bool use_bsdiff = false; + if (header_bytes_read >= 8 && memcmp(header, "BSDIFF40", 8) == 0) { + use_bsdiff = true; + } else if (header_bytes_read >= 8 && memcmp(header, "IMGDIFF2", 8) == 0) { + use_bsdiff = false; + } else { + printf("Unknown patch file format\n"); + return 1; } do { // Is there enough room in the target filesystem to hold the patched // file? - if (strncmp(target_filename, "MTD:", 4) == 0 || - strncmp(target_filename, "EMMC:", 5) == 0) { + if (target_is_partition) { // If the target is a partition, we're actually going to // write the output to /tmp and then copy it to the // partition. statfs() always returns 0 blocks free for @@ -850,7 +828,7 @@ static int GenerateTarget(FileContents* source_file, // We still write the original source to cache, in case // the partition write is interrupted. - if (MakeFreeSpaceOnCache(source_file->size) < 0) { + if (MakeFreeSpaceOnCache(source_file->data.size()) < 0) { printf("not enough free space on /cache\n"); return 1; } @@ -863,7 +841,7 @@ static int GenerateTarget(FileContents* source_file, } else { int enough_space = 0; if (retry > 0) { - size_t free_space = FreeSpaceForFile(target_fs); + size_t free_space = FreeSpaceForFile(target_fs.c_str()); enough_space = (free_space > (256 << 10)) && // 256k (two-block) minimum (free_space > (target_size * 3 / 2)); // 50% margin of error @@ -891,7 +869,7 @@ static int GenerateTarget(FileContents* source_file, return 1; } - if (MakeFreeSpaceOnCache(source_file->size) < 0) { + if (MakeFreeSpaceOnCache(source_file->data.size()) < 0) { printf("not enough free space on /cache\n"); return 1; } @@ -903,84 +881,53 @@ static int GenerateTarget(FileContents* source_file, made_copy = 1; unlink(source_filename); - size_t free_space = FreeSpaceForFile(target_fs); + size_t free_space = FreeSpaceForFile(target_fs.c_str()); printf("(now %zu bytes free for target) ", free_space); } } - const Value* patch; - if (source_patch_value != NULL) { - source_to_use = source_file; - patch = source_patch_value; - } else { - source_to_use = copy_file; - patch = copy_patch_value; - } - - if (patch->type != VAL_BLOB) { - printf("patch is not a blob\n"); - return 1; - } SinkFn sink = NULL; void* token = NULL; - output = -1; - outname = NULL; - if (strncmp(target_filename, "MTD:", 4) == 0 || - strncmp(target_filename, "EMMC:", 5) == 0) { + int output_fd = -1; + if (target_is_partition) { // We store the decoded output in memory. - msi.buffer = reinterpret_cast<unsigned char*>(malloc(target_size)); - if (msi.buffer == NULL) { - printf("failed to alloc %zu bytes for output\n", target_size); - return 1; - } - msi.pos = 0; - msi.size = target_size; sink = MemorySink; - token = &msi; + token = &memory_sink_str; } else { // We write the decoded output to "<tgt-file>.patch". - outname = reinterpret_cast<char*>(malloc(strlen(target_filename) + 10)); - strcpy(outname, target_filename); - strcat(outname, ".patch"); - - output = open(outname, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR); - if (output < 0) { - printf("failed to open output file %s: %s\n", - outname, strerror(errno)); + output_fd = ota_open(tmp_target_filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, + S_IRUSR | S_IWUSR); + if (output_fd < 0) { + printf("failed to open output file %s: %s\n", tmp_target_filename.c_str(), + strerror(errno)); return 1; } sink = FileSink; - token = &output; + token = &output_fd; } - char* header = patch->data; - ssize_t header_bytes_read = patch->size; - SHA_init(&ctx); + SHA1_Init(&ctx); int result; - - if (header_bytes_read >= 8 && - memcmp(header, "BSDIFF40", 8) == 0) { - result = ApplyBSDiffPatch(source_to_use->data, source_to_use->size, + if (use_bsdiff) { + result = ApplyBSDiffPatch(source_to_use->data.data(), source_to_use->data.size(), patch, 0, sink, token, &ctx); - } else if (header_bytes_read >= 8 && - memcmp(header, "IMGDIFF2", 8) == 0) { - result = ApplyImagePatch(source_to_use->data, source_to_use->size, - patch, sink, token, &ctx, bonus_data); } else { - printf("Unknown patch file format\n"); - return 1; + result = ApplyImagePatch(source_to_use->data.data(), source_to_use->data.size(), + patch, sink, token, &ctx, bonus_data); } - if (output >= 0) { - if (fsync(output) != 0) { - printf("failed to fsync file \"%s\" (%s)\n", outname, strerror(errno)); + if (!target_is_partition) { + if (ota_fsync(output_fd) != 0) { + printf("failed to fsync file \"%s\" (%s)\n", tmp_target_filename.c_str(), + strerror(errno)); result = 1; } - if (close(output) != 0) { - printf("failed to close file \"%s\" (%s)\n", outname, strerror(errno)); + if (ota_close(output_fd) != 0) { + printf("failed to close file \"%s\" (%s)\n", tmp_target_filename.c_str(), + strerror(errno)); result = 1; } } @@ -992,8 +939,8 @@ static int GenerateTarget(FileContents* source_file, } else { printf("applying patch failed; retrying\n"); } - if (outname != NULL) { - unlink(outname); + if (!target_is_partition) { + unlink(tmp_target_filename.c_str()); } } else { // succeeded; no need to retry @@ -1001,35 +948,36 @@ static int GenerateTarget(FileContents* source_file, } } while (retry-- > 0); - const uint8_t* current_target_sha1 = SHA_final(&ctx); - if (memcmp(current_target_sha1, target_sha1, SHA_DIGEST_SIZE) != 0) { + uint8_t current_target_sha1[SHA_DIGEST_LENGTH]; + SHA1_Final(current_target_sha1, &ctx); + if (memcmp(current_target_sha1, target_sha1, SHA_DIGEST_LENGTH) != 0) { printf("patch did not produce expected sha1\n"); return 1; } else { printf("now %s\n", short_sha1(target_sha1).c_str()); } - if (output < 0) { + if (target_is_partition) { // Copy the temp file to the partition. - if (WriteToPartition(msi.buffer, msi.pos, target_filename) != 0) { + if (WriteToPartition(reinterpret_cast<const unsigned char*>(memory_sink_str.c_str()), + memory_sink_str.size(), target_filename) != 0) { printf("write of patched data to %s failed\n", target_filename); return 1; } - free(msi.buffer); } else { // Give the .patch file the same owner, group, and mode of the // original source file. - if (chmod(outname, source_to_use->st.st_mode) != 0) { - printf("chmod of \"%s\" failed: %s\n", outname, strerror(errno)); + if (chmod(tmp_target_filename.c_str(), source_to_use->st.st_mode) != 0) { + printf("chmod of \"%s\" failed: %s\n", tmp_target_filename.c_str(), strerror(errno)); return 1; } - if (chown(outname, source_to_use->st.st_uid, source_to_use->st.st_gid) != 0) { - printf("chown of \"%s\" failed: %s\n", outname, strerror(errno)); + if (chown(tmp_target_filename.c_str(), source_to_use->st.st_uid, source_to_use->st.st_gid) != 0) { + printf("chown of \"%s\" failed: %s\n", tmp_target_filename.c_str(), strerror(errno)); return 1; } // Finally, rename the .patch file to replace the target file. - if (rename(outname, target_filename) != 0) { + if (rename(tmp_target_filename.c_str(), target_filename) != 0) { printf("rename of .patch to \"%s\" failed: %s\n", target_filename, strerror(errno)); return 1; } diff --git a/applypatch/applypatch.h b/applypatch/applypatch.h index 415bc1b3c..9ee39d293 100644 --- a/applypatch/applypatch.h +++ b/applypatch/applypatch.h @@ -17,21 +17,19 @@ #ifndef _APPLYPATCH_H #define _APPLYPATCH_H +#include <stdint.h> #include <sys/stat.h> -#include "mincrypt/sha.h" -#include "edify/expr.h" -typedef struct _Patch { - uint8_t sha1[SHA_DIGEST_SIZE]; - const char* patch_filename; -} Patch; +#include <vector> + +#include "openssl/sha.h" +#include "edify/expr.h" -typedef struct _FileContents { - uint8_t sha1[SHA_DIGEST_SIZE]; - unsigned char* data; - ssize_t size; +struct FileContents { + uint8_t sha1[SHA_DIGEST_LENGTH]; + std::vector<unsigned char> data; struct stat st; -} FileContents; +}; // When there isn't enough room on the target filesystem to hold the // patched version of the file, we copy the original here and delete @@ -68,22 +66,22 @@ void FreeFileContents(FileContents* file); int FindMatchingPatch(uint8_t* sha1, char* const * const patch_sha1_str, int num_patches); -// bsdiff.c +// bsdiff.cpp void ShowBSDiffLicense(); int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size, const Value* patch, ssize_t patch_offset, SinkFn sink, void* token, SHA_CTX* ctx); int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size, const Value* patch, ssize_t patch_offset, - unsigned char** new_data, ssize_t* new_size); + std::vector<unsigned char>* new_data); -// imgpatch.c +// imgpatch.cpp int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, const Value* patch, SinkFn sink, void* token, SHA_CTX* ctx, const Value* bonus_data); -// freecache.c +// freecache.cpp int MakeFreeSpaceOnCache(size_t bytes_needed); #endif diff --git a/applypatch/bsdiff.cpp b/applypatch/bsdiff.cpp index 55dbe5cf1..cca1b32fb 100644 --- a/applypatch/bsdiff.cpp +++ b/applypatch/bsdiff.cpp @@ -224,7 +224,6 @@ static void offtout(off_t x,u_char *buf) int bsdiff(u_char* old, off_t oldsize, off_t** IP, u_char* newdata, off_t newsize, const char* patch_filename) { - int fd; off_t *I; off_t scan,pos,len; off_t lastscan,lastpos,lastoffset; diff --git a/applypatch/bspatch.cpp b/applypatch/bspatch.cpp index 9d201b477..1fc1455a6 100644 --- a/applypatch/bspatch.cpp +++ b/applypatch/bspatch.cpp @@ -22,14 +22,14 @@ #include <stdio.h> #include <sys/stat.h> +#include <sys/types.h> #include <errno.h> -#include <malloc.h> #include <unistd.h> #include <string.h> #include <bzlib.h> -#include "mincrypt/sha.h" +#include "openssl/sha.h" #include "applypatch.h" void ShowBSDiffLicense() { @@ -102,26 +102,22 @@ int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size, const Value* patch, ssize_t patch_offset, SinkFn sink, void* token, SHA_CTX* ctx) { - unsigned char* new_data; - ssize_t new_size; - if (ApplyBSDiffPatchMem(old_data, old_size, patch, patch_offset, - &new_data, &new_size) != 0) { + std::vector<unsigned char> new_data; + if (ApplyBSDiffPatchMem(old_data, old_size, patch, patch_offset, &new_data) != 0) { return -1; } - if (sink(new_data, new_size, token) < new_size) { + if (sink(new_data.data(), new_data.size(), token) < static_cast<ssize_t>(new_data.size())) { printf("short write of output: %d (%s)\n", errno, strerror(errno)); return 1; } - if (ctx) SHA_update(ctx, new_data, new_size); - free(new_data); - + if (ctx) SHA1_Update(ctx, new_data.data(), new_data.size()); return 0; } int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size, const Value* patch, ssize_t patch_offset, - unsigned char** new_data, ssize_t* new_size) { + std::vector<unsigned char>* new_data) { // Patch data format: // 0 8 "BSDIFF40" // 8 8 X @@ -140,12 +136,12 @@ int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size, return 1; } - ssize_t ctrl_len, data_len; + ssize_t ctrl_len, data_len, new_size; ctrl_len = offtin(header+8); data_len = offtin(header+16); - *new_size = offtin(header+24); + new_size = offtin(header+24); - if (ctrl_len < 0 || data_len < 0 || *new_size < 0) { + if (ctrl_len < 0 || data_len < 0 || new_size < 0) { printf("corrupt patch file header (data lengths)\n"); return 1; } @@ -182,18 +178,13 @@ int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size, printf("failed to bzinit extra stream (%d)\n", bzerr); } - *new_data = reinterpret_cast<unsigned char*>(malloc(*new_size)); - if (*new_data == NULL) { - printf("failed to allocate %zd bytes of memory for output file\n", *new_size); - return 1; - } + new_data->resize(new_size); off_t oldpos = 0, newpos = 0; off_t ctrl[3]; - off_t len_read; int i; unsigned char buf[24]; - while (newpos < *new_size) { + while (newpos < new_size) { // Read control data if (FillBuffer(buf, 24, &cstream) != 0) { printf("error while reading control stream\n"); @@ -209,13 +200,13 @@ int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size, } // Sanity check - if (newpos + ctrl[0] > *new_size) { + if (newpos + ctrl[0] > new_size) { printf("corrupt patch (new file overrun)\n"); return 1; } // Read diff string - if (FillBuffer(*new_data + newpos, ctrl[0], &dstream) != 0) { + if (FillBuffer(new_data->data() + newpos, ctrl[0], &dstream) != 0) { printf("error while reading diff stream\n"); return 1; } @@ -232,13 +223,13 @@ int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size, oldpos += ctrl[0]; // Sanity check - if (newpos + ctrl[1] > *new_size) { + if (newpos + ctrl[1] > new_size) { printf("corrupt patch (new file overrun)\n"); return 1; } // Read extra string - if (FillBuffer(*new_data + newpos, ctrl[1], &estream) != 0) { + if (FillBuffer(new_data->data() + newpos, ctrl[1], &estream) != 0) { printf("error while reading extra stream\n"); return 1; } diff --git a/applypatch/freecache.cpp b/applypatch/freecache.cpp index 2eb2f55ef..c84f42797 100644 --- a/applypatch/freecache.cpp +++ b/applypatch/freecache.cpp @@ -25,119 +25,90 @@ #include <dirent.h> #include <ctype.h> +#include <memory> +#include <set> +#include <string> + +#include <android-base/parseint.h> +#include <android-base/stringprintf.h> + #include "applypatch.h" -static int EliminateOpenFiles(char** files, int file_count) { - DIR* d; - struct dirent* de; - d = opendir("/proc"); - if (d == NULL) { +static int EliminateOpenFiles(std::set<std::string>* files) { + std::unique_ptr<DIR, decltype(&closedir)> d(opendir("/proc"), closedir); + if (!d) { printf("error opening /proc: %s\n", strerror(errno)); return -1; } - while ((de = readdir(d)) != 0) { - int i; - for (i = 0; de->d_name[i] != '\0' && isdigit(de->d_name[i]); ++i); - if (de->d_name[i]) continue; - - // de->d_name[i] is numeric - - char path[FILENAME_MAX]; - strcpy(path, "/proc/"); - strcat(path, de->d_name); - strcat(path, "/fd/"); + struct dirent* de; + while ((de = readdir(d.get())) != 0) { + unsigned int pid; + if (!android::base::ParseUint(de->d_name, &pid)) { + continue; + } + std::string path = android::base::StringPrintf("/proc/%s/fd/", de->d_name); - DIR* fdd; struct dirent* fdde; - fdd = opendir(path); - if (fdd == NULL) { - printf("error opening %s: %s\n", path, strerror(errno)); + std::unique_ptr<DIR, decltype(&closedir)> fdd(opendir(path.c_str()), closedir); + if (!fdd) { + printf("error opening %s: %s\n", path.c_str(), strerror(errno)); continue; } - while ((fdde = readdir(fdd)) != 0) { - char fd_path[FILENAME_MAX]; + while ((fdde = readdir(fdd.get())) != 0) { + std::string fd_path = path + fdde->d_name; char link[FILENAME_MAX]; - strcpy(fd_path, path); - strcat(fd_path, fdde->d_name); - int count; - count = readlink(fd_path, link, sizeof(link)-1); + int count = readlink(fd_path.c_str(), link, sizeof(link)-1); if (count >= 0) { link[count] = '\0'; - - // This is inefficient, but it should only matter if there are - // lots of files in /cache, and lots of them are open (neither - // of which should be true, especially in recovery). if (strncmp(link, "/cache/", 7) == 0) { - int j; - for (j = 0; j < file_count; ++j) { - if (files[j] && strcmp(files[j], link) == 0) { - printf("%s is open by %s\n", link, de->d_name); - free(files[j]); - files[j] = NULL; - } + if (files->erase(link) > 0) { + printf("%s is open by %s\n", link, de->d_name); } } } } - closedir(fdd); } - closedir(d); - return 0; } -int FindExpendableFiles(char*** names, int* entries) { - DIR* d; - struct dirent* de; - int size = 32; - *entries = 0; - *names = reinterpret_cast<char**>(malloc(size * sizeof(char*))); - - char path[FILENAME_MAX]; - +static std::set<std::string> FindExpendableFiles() { + std::set<std::string> files; // We're allowed to delete unopened regular files in any of these // directories. const char* dirs[2] = {"/cache", "/cache/recovery/otatest"}; for (size_t i = 0; i < sizeof(dirs)/sizeof(dirs[0]); ++i) { - d = opendir(dirs[i]); - if (d == NULL) { + std::unique_ptr<DIR, decltype(&closedir)> d(opendir(dirs[i]), closedir); + if (!d) { printf("error opening %s: %s\n", dirs[i], strerror(errno)); continue; } // Look for regular files in the directory (not in any subdirectories). - while ((de = readdir(d)) != 0) { - strcpy(path, dirs[i]); - strcat(path, "/"); - strcat(path, de->d_name); + struct dirent* de; + while ((de = readdir(d.get())) != 0) { + std::string path = std::string(dirs[i]) + "/" + de->d_name; // We can't delete CACHE_TEMP_SOURCE; if it's there we might have // restarted during installation and could be depending on it to // be there. - if (strcmp(path, CACHE_TEMP_SOURCE) == 0) continue; + if (path == CACHE_TEMP_SOURCE) { + continue; + } struct stat st; - if (stat(path, &st) == 0 && S_ISREG(st.st_mode)) { - if (*entries >= size) { - size *= 2; - *names = reinterpret_cast<char**>(realloc(*names, size * sizeof(char*))); - } - (*names)[(*entries)++] = strdup(path); + if (stat(path.c_str(), &st) == 0 && S_ISREG(st.st_mode)) { + files.insert(path); } } - - closedir(d); } - printf("%d regular files in deletable directories\n", *entries); - - if (EliminateOpenFiles(*names, *entries) < 0) { - return -1; + printf("%zu regular files in deletable directories\n", files.size()); + if (EliminateOpenFiles(&files) < 0) { + return std::set<std::string>(); } - - return 0; + return files; } int MakeFreeSpaceOnCache(size_t bytes_needed) { @@ -147,15 +118,8 @@ int MakeFreeSpaceOnCache(size_t bytes_needed) { if (free_now >= bytes_needed) { return 0; } - - char** names; - int entries; - - if (FindExpendableFiles(&names, &entries) < 0) { - return -1; - } - - if (entries == 0) { + std::set<std::string> files = FindExpendableFiles(); + if (files.empty()) { // nothing we can delete to free up space! printf("no files can be deleted to free space on /cache\n"); return -1; @@ -167,20 +131,13 @@ int MakeFreeSpaceOnCache(size_t bytes_needed) { // // Instead, we'll be dumb. - int i; - for (i = 0; i < entries && free_now < bytes_needed; ++i) { - if (names[i]) { - unlink(names[i]); - free_now = FreeSpaceForFile("/cache"); - printf("deleted %s; now %zu bytes free\n", names[i], free_now); - free(names[i]); + for (const auto& file : files) { + unlink(file.c_str()); + free_now = FreeSpaceForFile("/cache"); + printf("deleted %s; now %zu bytes free\n", file.c_str(), free_now); + if (free_now < bytes_needed) { + break; } } - - for (; i < entries; ++i) { - free(names[i]); - } - free(names); - return (free_now >= bytes_needed) ? 0 : -1; } diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp index f22502e38..2aa4a6862 100644 --- a/applypatch/imgdiff.cpp +++ b/applypatch/imgdiff.cpp @@ -407,7 +407,6 @@ unsigned char* ReadImage(const char* filename, while (pos < sz) { unsigned char* p = img+pos; - bool processed_deflate = false; if (sz - pos >= 4 && p[0] == 0x1f && p[1] == 0x8b && p[2] == 0x08 && // deflate compression @@ -461,28 +460,27 @@ unsigned char* ReadImage(const char* filename, strm.next_out = curr->data + curr->len; ret = inflate(&strm, Z_NO_FLUSH); if (ret < 0) { - if (!processed_deflate) { - // This is the first chunk, assume that it's just a spurious - // gzip header instead of a real one. - break; - } - printf("Error: inflate failed [%s] at file offset [%zu]\n" - "imgdiff only supports gzip kernel compression," - " did you try CONFIG_KERNEL_LZO?\n", + printf("Warning: inflate failed [%s] at offset [%zu]," + " treating as a normal chunk\n", strm.msg, chunk_offset); - free(img); - return NULL; + break; } curr->len = allocated - strm.avail_out; if (strm.avail_out == 0) { allocated *= 2; curr->data = reinterpret_cast<unsigned char*>(realloc(curr->data, allocated)); } - processed_deflate = true; } while (ret != Z_STREAM_END); curr->deflate_len = sz - strm.avail_in - pos; inflateEnd(&strm); + + if (ret < 0) { + free(curr->data); + *num_chunks -= 2; + continue; + } + pos += curr->deflate_len; p += curr->deflate_len; ++curr; @@ -598,7 +596,6 @@ int ReconstructDeflateChunk(ImageChunk* chunk) { return -1; } - size_t p = 0; unsigned char* out = reinterpret_cast<unsigned char*>(malloc(BUFFER_SIZE)); // We only check two combinations of encoder parameters: level 6 @@ -844,7 +841,6 @@ int main(int argc, char** argv) { } if (argc != 4) { - usage: printf("usage: %s [-z] [-b <bonus-file>] <src-img> <tgt-img> <patch-file>\n", argv[0]); return 2; diff --git a/applypatch/imgpatch.cpp b/applypatch/imgpatch.cpp index 26888f8ee..0ab995b30 100644 --- a/applypatch/imgpatch.cpp +++ b/applypatch/imgpatch.cpp @@ -21,23 +21,33 @@ #include <sys/cdefs.h> #include <sys/stat.h> #include <errno.h> -#include <malloc.h> #include <unistd.h> #include <string.h> +#include <vector> + #include "zlib.h" -#include "mincrypt/sha.h" +#include "openssl/sha.h" #include "applypatch.h" #include "imgdiff.h" #include "utils.h" +int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, + const unsigned char* patch_data, ssize_t patch_size, + SinkFn sink, void* token) { + Value patch = {VAL_BLOB, patch_size, + reinterpret_cast<char*>(const_cast<unsigned char*>(patch_data))}; + return ApplyImagePatch( + old_data, old_size, &patch, sink, token, nullptr, nullptr); +} + /* * Apply the patch given in 'patch_filename' to the source data given * by (old_data, old_size). Write the patched output to the 'output' * file, and update the SHA context with the output data as well. * Return 0 on success. */ -int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size __unused, +int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, const Value* patch, SinkFn sink, void* token, SHA_CTX* ctx, const Value* bonus_data) { @@ -80,6 +90,10 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size __unused, size_t src_len = Read8(normal_header+8); size_t patch_offset = Read8(normal_header+16); + if (src_start + src_len > static_cast<size_t>(old_size)) { + printf("source data too short\n"); + return -1; + } ApplyBSDiffPatch(old_data + src_start, src_len, patch, patch_offset, sink, token, ctx); } else if (type == CHUNK_RAW) { @@ -96,7 +110,7 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size __unused, printf("failed to read chunk %d raw data\n", i); return -1; } - if (ctx) SHA_update(ctx, patch->data + pos, data_len); + if (ctx) SHA1_Update(ctx, patch->data + pos, data_len); if (sink((unsigned char*)patch->data + pos, data_len, token) != data_len) { printf("failed to write chunk %d raw data\n", i); @@ -123,6 +137,11 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size __unused, int memLevel = Read4(deflate_header+52); int strategy = Read4(deflate_header+56); + if (src_start + src_len > static_cast<size_t>(old_size)) { + printf("source data too short\n"); + return -1; + } + // Decompress the source data; the chunk header tells us exactly // how big we expect it to be when decompressed. @@ -132,13 +151,7 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size __unused, // must be appended from the bonus_data value. size_t bonus_size = (i == 1 && bonus_data != NULL) ? bonus_data->size : 0; - unsigned char* expanded_source = reinterpret_cast<unsigned char*>(malloc(expanded_len)); - if (expanded_source == NULL) { - printf("failed to allocate %zu bytes for expanded_source\n", - expanded_len); - return -1; - } - + std::vector<unsigned char> expanded_source(expanded_len); z_stream strm; strm.zalloc = Z_NULL; strm.zfree = Z_NULL; @@ -146,7 +159,7 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size __unused, strm.avail_in = src_len; strm.next_in = (unsigned char*)(old_data + src_start); strm.avail_out = expanded_len; - strm.next_out = expanded_source; + strm.next_out = expanded_source.data(); int ret; ret = inflateInit2(&strm, -15); @@ -171,18 +184,21 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size __unused, inflateEnd(&strm); if (bonus_size) { - memcpy(expanded_source + (expanded_len - bonus_size), + memcpy(expanded_source.data() + (expanded_len - bonus_size), bonus_data->data, bonus_size); } // Next, apply the bsdiff patch (in memory) to the uncompressed // data. - unsigned char* uncompressed_target_data; - ssize_t uncompressed_target_size; - if (ApplyBSDiffPatchMem(expanded_source, expanded_len, + std::vector<unsigned char> uncompressed_target_data; + if (ApplyBSDiffPatchMem(expanded_source.data(), expanded_len, patch, patch_offset, - &uncompressed_target_data, - &uncompressed_target_size) != 0) { + &uncompressed_target_data) != 0) { + return -1; + } + if (uncompressed_target_data.size() != target_len) { + printf("expected target len to be %zu, but it's %zu\n", + target_len, uncompressed_target_data.size()); return -1; } @@ -190,40 +206,36 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size __unused, // we're done with the expanded_source data buffer, so we'll // reuse that memory to receive the output of deflate. - unsigned char* temp_data = expanded_source; - ssize_t temp_size = expanded_len; - if (temp_size < 32768) { - // ... unless the buffer is too small, in which case we'll - // allocate a fresh one. - free(temp_data); - temp_data = reinterpret_cast<unsigned char*>(malloc(32768)); - temp_size = 32768; + if (expanded_source.size() < 32768U) { + expanded_source.resize(32768U); } + std::vector<unsigned char>& temp_data = expanded_source; // now the deflate stream strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; - strm.avail_in = uncompressed_target_size; - strm.next_in = uncompressed_target_data; + strm.avail_in = uncompressed_target_data.size(); + strm.next_in = uncompressed_target_data.data(); ret = deflateInit2(&strm, level, method, windowBits, memLevel, strategy); + if (ret != Z_OK) { + printf("failed to init uncompressed data deflation: %d\n", ret); + return -1; + } do { - strm.avail_out = temp_size; - strm.next_out = temp_data; + strm.avail_out = temp_data.size(); + strm.next_out = temp_data.data(); ret = deflate(&strm, Z_FINISH); - ssize_t have = temp_size - strm.avail_out; + ssize_t have = temp_data.size() - strm.avail_out; - if (sink(temp_data, have, token) != have) { + if (sink(temp_data.data(), have, token) != have) { printf("failed to write %ld compressed bytes to output\n", (long)have); return -1; } - if (ctx) SHA_update(ctx, temp_data, have); + if (ctx) SHA1_Update(ctx, temp_data.data(), have); } while (ret != Z_STREAM_END); deflateEnd(&strm); - - free(temp_data); - free(uncompressed_target_data); } else { printf("patch chunk %d is unknown type %d\n", i, type); return -1; diff --git a/applypatch/include/applypatch/imgpatch.h b/applypatch/include/applypatch/imgpatch.h new file mode 100644 index 000000000..64d9aa9eb --- /dev/null +++ b/applypatch/include/applypatch/imgpatch.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _IMGPATCH_H +#define _IMGPATCH_H + +typedef ssize_t (*SinkFn)(const unsigned char*, ssize_t, void*); + +int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, + const unsigned char* patch_data, ssize_t patch_size, + SinkFn sink, void* token); + +#endif //_IMGPATCH_H diff --git a/applypatch/libimgpatch.pc b/applypatch/libimgpatch.pc new file mode 100644 index 000000000..e5002934f --- /dev/null +++ b/applypatch/libimgpatch.pc @@ -0,0 +1,6 @@ +# This file is for libimgpatch in Chrome OS. + +Name: libimgpatch +Description: Apply imgdiff patch +Version: 0.0.1 +Libs: -limgpatch -lbz2 -lz diff --git a/applypatch/main.cpp b/applypatch/main.cpp index 966d8b91f..9013760c4 100644 --- a/applypatch/main.cpp +++ b/applypatch/main.cpp @@ -19,9 +19,12 @@ #include <string.h> #include <unistd.h> +#include <memory> +#include <vector> + #include "applypatch.h" #include "edify/expr.h" -#include "mincrypt/sha.h" +#include "openssl/sha.h" static int CheckMode(int argc, char** argv) { if (argc < 3) { @@ -43,59 +46,34 @@ static int SpaceMode(int argc, char** argv) { return CacheSizeCheck(bytes); } -// Parse arguments (which should be of the form "<sha1>" or -// "<sha1>:<filename>" into the new parallel arrays *sha1s and -// *patches (loading file contents into the patches). Returns true on +// Parse arguments (which should be of the form "<sha1>:<filename>" +// into the new parallel arrays *sha1s and *files.Returns true on // success. -static bool ParsePatchArgs(int argc, char** argv, char*** sha1s, - Value*** patches, int* num_patches) { - *num_patches = argc; - *sha1s = reinterpret_cast<char**>(malloc(*num_patches * sizeof(char*))); - *patches = reinterpret_cast<Value**>(malloc(*num_patches * sizeof(Value*))); - memset(*patches, 0, *num_patches * sizeof(Value*)); - - uint8_t digest[SHA_DIGEST_SIZE]; +static bool ParsePatchArgs(int argc, char** argv, std::vector<char*>* sha1s, + std::vector<FileContents>* files) { + uint8_t digest[SHA_DIGEST_LENGTH]; - for (int i = 0; i < *num_patches; ++i) { + for (int i = 0; i < argc; ++i) { char* colon = strchr(argv[i], ':'); - if (colon != NULL) { - *colon = '\0'; - ++colon; + if (colon == nullptr) { + printf("no ':' in patch argument \"%s\"\n", argv[i]); + return false; } - + *colon = '\0'; + ++colon; if (ParseSha1(argv[i], digest) != 0) { printf("failed to parse sha1 \"%s\"\n", argv[i]); return false; } - (*sha1s)[i] = argv[i]; - if (colon == NULL) { - (*patches)[i] = NULL; - } else { - FileContents fc; - if (LoadFileContents(colon, &fc) != 0) { - goto abort; - } - (*patches)[i] = reinterpret_cast<Value*>(malloc(sizeof(Value))); - (*patches)[i]->type = VAL_BLOB; - (*patches)[i]->size = fc.size; - (*patches)[i]->data = reinterpret_cast<char*>(fc.data); + sha1s->push_back(argv[i]); + FileContents fc; + if (LoadFileContents(colon, &fc) != 0) { + return false; } + files->push_back(std::move(fc)); } - return true; - - abort: - for (int i = 0; i < *num_patches; ++i) { - Value* p = (*patches)[i]; - if (p != NULL) { - free(p->data); - free(p); - } - } - free(*sha1s); - free(*patches); - return false; } static int FlashMode(const char* src_filename, const char* tgt_filename, @@ -104,17 +82,19 @@ static int FlashMode(const char* src_filename, const char* tgt_filename, } static int PatchMode(int argc, char** argv) { - Value* bonus = NULL; + FileContents bonusFc; + Value bonusValue; + Value* bonus = nullptr; + if (argc >= 3 && strcmp(argv[1], "-b") == 0) { - FileContents fc; - if (LoadFileContents(argv[2], &fc) != 0) { + if (LoadFileContents(argv[2], &bonusFc) != 0) { printf("failed to load bonus file %s\n", argv[2]); return 1; } - bonus = reinterpret_cast<Value*>(malloc(sizeof(Value))); + bonus = &bonusValue; bonus->type = VAL_BLOB; - bonus->size = fc.size; - bonus->data = (char*)fc.data; + bonus->size = bonusFc.data.size(); + bonus->data = reinterpret_cast<char*>(bonusFc.data.data()); argc -= 2; argv += 2; } @@ -132,41 +112,29 @@ static int PatchMode(int argc, char** argv) { // If no <src-sha1>:<patch> is provided, it is in flash mode. if (argc == 5) { - if (bonus != NULL) { + if (bonus != nullptr) { printf("bonus file not supported in flash mode\n"); return 1; } return FlashMode(argv[1], argv[2], argv[3], target_size); } - - - char** sha1s; - Value** patches; - int num_patches; - if (!ParsePatchArgs(argc-5, argv+5, &sha1s, &patches, &num_patches)) { + std::vector<char*> sha1s; + std::vector<FileContents> files; + if (!ParsePatchArgs(argc-5, argv+5, &sha1s, &files)) { printf("failed to parse patch args\n"); return 1; } - - int result = applypatch(argv[1], argv[2], argv[3], target_size, - num_patches, sha1s, patches, bonus); - - int i; - for (i = 0; i < num_patches; ++i) { - Value* p = patches[i]; - if (p != NULL) { - free(p->data); - free(p); - } - } - if (bonus) { - free(bonus->data); - free(bonus); + std::vector<Value> patches(files.size()); + std::vector<Value*> patch_ptrs(files.size()); + for (size_t i = 0; i < files.size(); ++i) { + patches[i].type = VAL_BLOB; + patches[i].size = files[i].data.size(); + patches[i].data = reinterpret_cast<char*>(files[i].data.data()); + patch_ptrs[i] = &patches[i]; } - free(sha1s); - free(patches); - - return result; + return applypatch(argv[1], argv[2], argv[3], target_size, + patch_ptrs.size(), sha1s.data(), + patch_ptrs.data(), bonus); } // This program applies binary patches to files in a way that is safe diff --git a/bootloader.cpp b/bootloader.cpp index 600d238f5..d80c5e793 100644 --- a/bootloader.cpp +++ b/bootloader.cpp @@ -14,28 +14,33 @@ * limitations under the License. */ -#include <fs_mgr.h> -#include "bootloader.h" -#include "common.h" -#include "mtdutils/mtdutils.h" -#include "roots.h" - #include <errno.h> +#include <fcntl.h> +#include <inttypes.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> +#include <sys/types.h> #include <unistd.h> -static int get_bootloader_message_mtd(struct bootloader_message *out, const Volume* v); -static int set_bootloader_message_mtd(const struct bootloader_message *in, const Volume* v); -static int get_bootloader_message_block(struct bootloader_message *out, const Volume* v); -static int set_bootloader_message_block(const struct bootloader_message *in, const Volume* v); +#include <fs_mgr.h> + +#include "bootloader.h" +#include "common.h" +#include "mtdutils/mtdutils.h" +#include "roots.h" +#include "unique_fd.h" + +static int get_bootloader_message_mtd(bootloader_message* out, const Volume* v); +static int set_bootloader_message_mtd(const bootloader_message* in, const Volume* v); +static int get_bootloader_message_block(bootloader_message* out, const Volume* v); +static int set_bootloader_message_block(const bootloader_message* in, const Volume* v); -int get_bootloader_message(struct bootloader_message *out) { +int get_bootloader_message(bootloader_message* out) { Volume* v = volume_for_path("/misc"); - if (v == NULL) { - LOGE("Cannot load volume /misc!\n"); - return -1; + if (v == nullptr) { + LOGE("Cannot load volume /misc!\n"); + return -1; } if (strcmp(v->fs_type, "mtd") == 0) { return get_bootloader_message_mtd(out, v); @@ -46,11 +51,11 @@ int get_bootloader_message(struct bootloader_message *out) { return -1; } -int set_bootloader_message(const struct bootloader_message *in) { +int set_bootloader_message(const bootloader_message* in) { Volume* v = volume_for_path("/misc"); - if (v == NULL) { - LOGE("Cannot load volume /misc!\n"); - return -1; + if (v == nullptr) { + LOGE("Cannot load volume /misc!\n"); + return -1; } if (strcmp(v->fs_type, "mtd") == 0) { return set_bootloader_message_mtd(in, v); @@ -68,69 +73,69 @@ int set_bootloader_message(const struct bootloader_message *in) { static const int MISC_PAGES = 3; // number of pages to save static const int MISC_COMMAND_PAGE = 1; // bootloader command is this page -static int get_bootloader_message_mtd(struct bootloader_message *out, +static int get_bootloader_message_mtd(bootloader_message* out, const Volume* v) { size_t write_size; mtd_scan_partitions(); - const MtdPartition *part = mtd_find_partition_by_name(v->blk_device); - if (part == NULL || mtd_partition_info(part, NULL, NULL, &write_size)) { - LOGE("Can't find %s\n", v->blk_device); + const MtdPartition* part = mtd_find_partition_by_name(v->blk_device); + if (part == nullptr || mtd_partition_info(part, nullptr, nullptr, &write_size)) { + LOGE("failed to find \"%s\"\n", v->blk_device); return -1; } - MtdReadContext *read = mtd_read_partition(part); - if (read == NULL) { - LOGE("Can't open %s\n(%s)\n", v->blk_device, strerror(errno)); + MtdReadContext* read = mtd_read_partition(part); + if (read == nullptr) { + LOGE("failed to open \"%s\": %s\n", v->blk_device, strerror(errno)); return -1; } const ssize_t size = write_size * MISC_PAGES; char data[size]; ssize_t r = mtd_read_data(read, data, size); - if (r != size) LOGE("Can't read %s\n(%s)\n", v->blk_device, strerror(errno)); + if (r != size) LOGE("failed to read \"%s\": %s\n", v->blk_device, strerror(errno)); mtd_read_close(read); if (r != size) return -1; memcpy(out, &data[write_size * MISC_COMMAND_PAGE], sizeof(*out)); return 0; } -static int set_bootloader_message_mtd(const struct bootloader_message *in, +static int set_bootloader_message_mtd(const bootloader_message* in, const Volume* v) { size_t write_size; mtd_scan_partitions(); - const MtdPartition *part = mtd_find_partition_by_name(v->blk_device); - if (part == NULL || mtd_partition_info(part, NULL, NULL, &write_size)) { - LOGE("Can't find %s\n", v->blk_device); + const MtdPartition* part = mtd_find_partition_by_name(v->blk_device); + if (part == nullptr || mtd_partition_info(part, nullptr, nullptr, &write_size)) { + LOGE("failed to find \"%s\"\n", v->blk_device); return -1; } - MtdReadContext *read = mtd_read_partition(part); - if (read == NULL) { - LOGE("Can't open %s\n(%s)\n", v->blk_device, strerror(errno)); + MtdReadContext* read = mtd_read_partition(part); + if (read == nullptr) { + LOGE("failed to open \"%s\": %s\n", v->blk_device, strerror(errno)); return -1; } ssize_t size = write_size * MISC_PAGES; char data[size]; ssize_t r = mtd_read_data(read, data, size); - if (r != size) LOGE("Can't read %s\n(%s)\n", v->blk_device, strerror(errno)); + if (r != size) LOGE("failed to read \"%s\": %s\n", v->blk_device, strerror(errno)); mtd_read_close(read); if (r != size) return -1; memcpy(&data[write_size * MISC_COMMAND_PAGE], in, sizeof(*in)); - MtdWriteContext *write = mtd_write_partition(part); - if (write == NULL) { - LOGE("Can't open %s\n(%s)\n", v->blk_device, strerror(errno)); + MtdWriteContext* write = mtd_write_partition(part); + if (write == nullptr) { + LOGE("failed to open \"%s\": %s\n", v->blk_device, strerror(errno)); return -1; } if (mtd_write_data(write, data, size) != size) { - LOGE("Can't write %s\n(%s)\n", v->blk_device, strerror(errno)); + LOGE("failed to write \"%s\": %s\n", v->blk_device, strerror(errno)); mtd_write_close(write); return -1; } if (mtd_write_close(write)) { - LOGE("Can't finish %s\n(%s)\n", v->blk_device, strerror(errno)); + LOGE("failed to finish \"%s\": %s\n", v->blk_device, strerror(errno)); return -1; } @@ -146,57 +151,67 @@ static int set_bootloader_message_mtd(const struct bootloader_message *in, static void wait_for_device(const char* fn) { int tries = 0; int ret; - struct stat buf; do { ++tries; + struct stat buf; ret = stat(fn, &buf); - if (ret) { - printf("stat %s try %d: %s\n", fn, tries, strerror(errno)); + if (ret == -1) { + printf("failed to stat \"%s\" try %d: %s\n", fn, tries, strerror(errno)); sleep(1); } } while (ret && tries < 10); + if (ret) { - printf("failed to stat %s\n", fn); + printf("failed to stat \"%s\"\n", fn); } } -static int get_bootloader_message_block(struct bootloader_message *out, +static int get_bootloader_message_block(bootloader_message* out, const Volume* v) { wait_for_device(v->blk_device); FILE* f = fopen(v->blk_device, "rb"); - if (f == NULL) { - LOGE("Can't open %s\n(%s)\n", v->blk_device, strerror(errno)); + if (f == nullptr) { + LOGE("failed to open \"%s\": %s\n", v->blk_device, strerror(errno)); return -1; } - struct bootloader_message temp; + bootloader_message temp; int count = fread(&temp, sizeof(temp), 1, f); if (count != 1) { - LOGE("Failed reading %s\n(%s)\n", v->blk_device, strerror(errno)); + LOGE("failed to read \"%s\": %s\n", v->blk_device, strerror(errno)); return -1; } if (fclose(f) != 0) { - LOGE("Failed closing %s\n(%s)\n", v->blk_device, strerror(errno)); + LOGE("failed to close \"%s\": %s\n", v->blk_device, strerror(errno)); return -1; } memcpy(out, &temp, sizeof(temp)); return 0; } -static int set_bootloader_message_block(const struct bootloader_message *in, +static int set_bootloader_message_block(const bootloader_message* in, const Volume* v) { wait_for_device(v->blk_device); - FILE* f = fopen(v->blk_device, "wb"); - if (f == NULL) { - LOGE("Can't open %s\n(%s)\n", v->blk_device, strerror(errno)); + unique_fd fd(open(v->blk_device, O_WRONLY | O_SYNC)); + if (fd.get() == -1) { + LOGE("failed to open \"%s\": %s\n", v->blk_device, strerror(errno)); return -1; } - int count = fwrite(in, sizeof(*in), 1, f); - if (count != 1) { - LOGE("Failed writing %s\n(%s)\n", v->blk_device, strerror(errno)); - return -1; + + size_t written = 0; + const uint8_t* start = reinterpret_cast<const uint8_t*>(in); + size_t total = sizeof(*in); + while (written < total) { + ssize_t wrote = TEMP_FAILURE_RETRY(write(fd.get(), start + written, total - written)); + if (wrote == -1) { + LOGE("failed to write %" PRId64 " bytes: %s\n", + static_cast<off64_t>(written), strerror(errno)); + return -1; + } + written += wrote; } - if (fclose(f) != 0) { - LOGE("Failed closing %s\n(%s)\n", v->blk_device, strerror(errno)); + + if (fsync(fd.get()) == -1) { + LOGE("failed to fsync \"%s\": %s\n", v->blk_device, strerror(errno)); return -1; } return 0; diff --git a/edify/lexer.ll b/edify/lexer.ll index fb2933bee..b764d1699 100644 --- a/edify/lexer.ll +++ b/edify/lexer.ll @@ -16,6 +16,7 @@ */ #include <string.h> +#include <string> #include "expr.h" #include "yydefs.h" @@ -25,9 +26,7 @@ int gLine = 1; int gColumn = 1; int gPos = 0; -// TODO: enforce MAX_STRING_LEN during lexing -char string_buffer[MAX_STRING_LEN]; -char* string_pos; +std::string string_buffer; #define ADVANCE do {yylloc.start=gPos; yylloc.end=gPos+yyleng; \ gColumn+=yyleng; gPos+=yyleng;} while(0) @@ -43,7 +42,7 @@ char* string_pos; \" { BEGIN(STR); - string_pos = string_buffer; + string_buffer.clear(); yylloc.start = gPos; ++gColumn; ++gPos; @@ -54,36 +53,35 @@ char* string_pos; ++gColumn; ++gPos; BEGIN(INITIAL); - *string_pos = '\0'; - yylval.str = strdup(string_buffer); + yylval.str = strdup(string_buffer.c_str()); yylloc.end = gPos; return STRING; } - \\n { gColumn += yyleng; gPos += yyleng; *string_pos++ = '\n'; } - \\t { gColumn += yyleng; gPos += yyleng; *string_pos++ = '\t'; } - \\\" { gColumn += yyleng; gPos += yyleng; *string_pos++ = '\"'; } - \\\\ { gColumn += yyleng; gPos += yyleng; *string_pos++ = '\\'; } + \\n { gColumn += yyleng; gPos += yyleng; string_buffer.push_back('\n'); } + \\t { gColumn += yyleng; gPos += yyleng; string_buffer.push_back('\t'); } + \\\" { gColumn += yyleng; gPos += yyleng; string_buffer.push_back('\"'); } + \\\\ { gColumn += yyleng; gPos += yyleng; string_buffer.push_back('\\'); } \\x[0-9a-fA-F]{2} { gColumn += yyleng; gPos += yyleng; int val; sscanf(yytext+2, "%x", &val); - *string_pos++ = val; + string_buffer.push_back(static_cast<char>(val)); } \n { ++gLine; ++gPos; gColumn = 1; - *string_pos++ = yytext[0]; + string_buffer.push_back(yytext[0]); } . { ++gColumn; ++gPos; - *string_pos++ = yytext[0]; + string_buffer.push_back(yytext[0]); } } diff --git a/edify/main.cpp b/edify/main.cpp index b1baa0b13..6007a3d58 100644 --- a/edify/main.cpp +++ b/edify/main.cpp @@ -18,6 +18,8 @@ #include <stdlib.h> #include <string.h> +#include <string> + #include "expr.h" #include "parser.h" @@ -151,6 +153,9 @@ int test() { expect("greater_than_int(x, 3)", "", &errors); expect("greater_than_int(3, x)", "", &errors); + // big string + expect(std::string(8192, 's').c_str(), std::string(8192, 's').c_str(), &errors); + printf("\n"); return errors; diff --git a/install.cpp b/install.cpp index 7d88ed72a..33c1f5498 100644 --- a/install.cpp +++ b/install.cpp @@ -23,6 +23,8 @@ #include <sys/wait.h> #include <unistd.h> +#include <vector> + #include "common.h" #include "install.h" #include "mincrypt/rsa.h" @@ -122,20 +124,20 @@ try_update_binary(const char* path, ZipArchive* zip, bool* wipe_cache) { // - the name of the package zip file. // - const char** args = (const char**)malloc(sizeof(char*) * 5); + const char* args[5]; args[0] = binary; args[1] = EXPAND(RECOVERY_API_VERSION); // defined in Android.mk - char* temp = (char*)malloc(10); - sprintf(temp, "%d", pipefd[1]); + char temp[16]; + snprintf(temp, sizeof(temp), "%d", pipefd[1]); args[2] = temp; - args[3] = (char*)path; + args[3] = path; args[4] = NULL; pid_t pid = fork(); if (pid == 0) { umask(022); close(pipefd[0]); - execv(binary, (char* const*)args); + execv(binary, const_cast<char**>(args)); fprintf(stdout, "E:Can't run %s (%s)\n", binary, strerror(errno)); _exit(-1); } @@ -221,19 +223,16 @@ really_install_package(const char *path, bool* wipe_cache, bool needs_mount) return INSTALL_CORRUPT; } - int numKeys; - Certificate* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys); - if (loadedKeys == NULL) { + std::vector<Certificate> loadedKeys; + if (!load_keys(PUBLIC_KEYS_FILE, loadedKeys)) { LOGE("Failed to load keys\n"); return INSTALL_CORRUPT; } - LOGI("%d key(s) loaded from %s\n", numKeys, PUBLIC_KEYS_FILE); + LOGI("%zu key(s) loaded from %s\n", loadedKeys.size(), PUBLIC_KEYS_FILE); ui->Print("Verifying update package...\n"); - int err; - err = verify_file(map.addr, map.length, loadedKeys, numKeys); - free(loadedKeys); + int err = verify_file(map.addr, map.length, loadedKeys); LOGI("verify_file returned %d\n", err); if (err != VERIFY_SUCCESS) { LOGE("signature verification failed\n"); diff --git a/minadbd/services.cpp b/minadbd/services.cpp index d25648fb4..658a43f36 100644 --- a/minadbd/services.cpp +++ b/minadbd/services.cpp @@ -35,11 +35,10 @@ struct stinfo { void *cookie; }; -void* service_bootstrap_func(void* x) { +void service_bootstrap_func(void* x) { stinfo* sti = reinterpret_cast<stinfo*>(x); sti->func(sti->fd, sti->cookie); free(sti); - return 0; } static void sideload_host_service(int sfd, void* data) { diff --git a/minui/resources.cpp b/minui/resources.cpp index 63a0dff28..fdbe554fe 100644 --- a/minui/resources.cpp +++ b/minui/resources.cpp @@ -28,6 +28,7 @@ #include <linux/fb.h> #include <linux/kd.h> +#include <vector> #include <png.h> #include "minui.h" @@ -398,18 +399,13 @@ int res_create_localized_alpha_surface(const char* name, png_infop info_ptr = NULL; png_uint_32 width, height; png_byte channels; - unsigned char* row; png_uint_32 y; + std::vector<unsigned char> row; *pSurface = NULL; if (locale == NULL) { - surface = malloc_surface(0); - surface->width = 0; - surface->height = 0; - surface->row_bytes = 0; - surface->pixel_bytes = 1; - goto exit; + return result; } result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels); @@ -420,13 +416,13 @@ int res_create_localized_alpha_surface(const char* name, goto exit; } - row = reinterpret_cast<unsigned char*>(malloc(width)); + row.resize(width); for (y = 0; y < height; ++y) { - png_read_row(png_ptr, row, NULL); + png_read_row(png_ptr, row.data(), NULL); int w = (row[1] << 8) | row[0]; int h = (row[3] << 8) | row[2]; int len = row[4]; - char* loc = (char*)row+5; + char* loc = reinterpret_cast<char*>(&row[5]); if (y+1+h >= height || matches_locale(loc, locale)) { printf(" %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y); @@ -443,8 +439,8 @@ int res_create_localized_alpha_surface(const char* name, int i; for (i = 0; i < h; ++i, ++y) { - png_read_row(png_ptr, row, NULL); - memcpy(surface->data + i*w, row, w); + png_read_row(png_ptr, row.data(), NULL); + memcpy(surface->data + i*w, row.data(), w); } *pSurface = reinterpret_cast<GRSurface*>(surface); @@ -452,7 +448,7 @@ int res_create_localized_alpha_surface(const char* name, } else { int i; for (i = 0; i < h; ++i, ++y) { - png_read_row(png_ptr, row, NULL); + png_read_row(png_ptr, row.data(), NULL); } } } diff --git a/minzip/Android.mk b/minzip/Android.mk index 22eabfbb1..3d36fd64e 100644 --- a/minzip/Android.mk +++ b/minzip/Android.mk @@ -4,7 +4,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ Hash.c \ SysUtil.c \ - DirUtil.c \ + DirUtil.cpp \ Inlines.c \ Zip.c diff --git a/minzip/DirUtil.c b/minzip/DirUtil.cpp index 97cb2e0ee..823b6ed2f 100644 --- a/minzip/DirUtil.c +++ b/minzip/DirUtil.cpp @@ -24,6 +24,8 @@ #include <dirent.h> #include <limits.h> +#include <string> + #include "DirUtil.h" typedef enum { DMISSING, DDIR, DILLEGAL } DirStatus; @@ -66,43 +68,25 @@ dirCreateHierarchy(const char *path, int mode, errno = ENOENT; return -1; } - - /* Allocate a path that we can modify; stick a slash on - * the end to make things easier. - */ - size_t pathLen = strlen(path); - char *cpath = (char *)malloc(pathLen + 2); - if (cpath == NULL) { - errno = ENOMEM; - return -1; - } - memcpy(cpath, path, pathLen); + // Allocate a path that we can modify; stick a slash on + // the end to make things easier. + std::string cpath = path; if (stripFileName) { - /* Strip everything after the last slash. - */ - char *c = cpath + pathLen - 1; - while (c != cpath && *c != '/') { - c--; - } - if (c == cpath) { - //xxx test this path - /* No directory component. Act like the path was empty. - */ + // Strip everything after the last slash. + size_t pos = cpath.rfind('/'); + if (pos == std::string::npos) { errno = ENOENT; - free(cpath); return -1; } - c[1] = '\0'; // Terminate after the slash we found. + cpath.resize(pos + 1); } else { - /* Make sure that the path ends in a slash. - */ - cpath[pathLen] = '/'; - cpath[pathLen + 1] = '\0'; + // Make sure that the path ends in a slash. + cpath.push_back('/'); } /* See if it already exists. */ - ds = getPathDirStatus(cpath); + ds = getPathDirStatus(cpath.c_str()); if (ds == DDIR) { return 0; } else if (ds == DILLEGAL) { @@ -112,7 +96,8 @@ dirCreateHierarchy(const char *path, int mode, /* Walk up the path from the root and make each level. * If a directory already exists, no big deal. */ - char *p = cpath; + const char *path_start = &cpath[0]; + char *p = &cpath[0]; while (*p != '\0') { /* Skip any slashes, watching out for the end of the string. */ @@ -135,12 +120,11 @@ dirCreateHierarchy(const char *path, int mode, /* Check this part of the path and make a new directory * if necessary. */ - ds = getPathDirStatus(cpath); + ds = getPathDirStatus(path_start); if (ds == DILLEGAL) { /* Could happen if some other process/thread is * messing with the filesystem. */ - free(cpath); return -1; } else if (ds == DMISSING) { int err; @@ -148,11 +132,11 @@ dirCreateHierarchy(const char *path, int mode, char *secontext = NULL; if (sehnd) { - selabel_lookup(sehnd, &secontext, cpath, mode); + selabel_lookup(sehnd, &secontext, path_start, mode); setfscreatecon(secontext); } - err = mkdir(cpath, mode); + err = mkdir(path_start, mode); if (secontext) { freecon(secontext); @@ -160,22 +144,17 @@ dirCreateHierarchy(const char *path, int mode, } if (err != 0) { - free(cpath); return -1; } - if (timestamp != NULL && utime(cpath, timestamp)) { - free(cpath); + if (timestamp != NULL && utime(path_start, timestamp)) { return -1; } } // else, this directory already exists. - - /* Repair the path and continue. - */ + + // Repair the path and continue. *p = '/'; } - free(cpath); - return 0; } diff --git a/otafault/Android.mk b/otafault/Android.mk new file mode 100644 index 000000000..75617a146 --- /dev/null +++ b/otafault/Android.mk @@ -0,0 +1,58 @@ +# Copyright 2015 The ANdroid Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific languae governing permissions and +# limitations under the License. + +LOCAL_PATH := $(call my-dir) + +empty := +space := $(empty) $(empty) +comma := , + +ifneq ($(TARGET_INJECT_FAULTS),) +TARGET_INJECT_FAULTS := $(subst $(comma),$(space),$(strip $(TARGET_INJECT_FAULTS))) +endif + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := ota_io.cpp +LOCAL_MODULE_TAGS := eng +LOCAL_MODULE := libotafault +LOCAL_CLANG := true + +ifneq ($(TARGET_INJECT_FAULTS),) +$(foreach ft,$(TARGET_INJECT_FAULTS),\ + $(eval LOCAL_CFLAGS += -DTARGET_$(ft)_FAULT=$(TARGET_$(ft)_FAULT_FILE))) +LOCAL_CFLAGS += -Wno-unused-parameter +LOCAL_CFLAGS += -DTARGET_INJECT_FAULTS +endif + +LOCAL_STATIC_LIBRARIES := libc + +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := ota_io.cpp test.cpp +LOCAL_MODULE_TAGS := tests +LOCAL_MODULE := otafault_test +LOCAL_STATIC_LIBRARIES := libc +LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_CFLAGS += -Wno-unused-parameter -Wno-writable-strings + +ifneq ($(TARGET_INJECT_FAULTS),) +$(foreach ft,$(TARGET_INJECT_FAULTS),\ + $(eval LOCAL_CFLAGS += -DTARGET_$(ft)_FAULT=$(TARGET_$(ft)_FAULT_FILE))) +LOCAL_CFLAGS += -DTARGET_INJECT_FAULTS +endif + +include $(BUILD_EXECUTABLE) diff --git a/otafault/ota_io.cpp b/otafault/ota_io.cpp new file mode 100644 index 000000000..02e80f9bf --- /dev/null +++ b/otafault/ota_io.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined (TARGET_INJECT_FAULTS) +#include <map> +#endif + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "ota_io.h" + +#if defined (TARGET_INJECT_FAULTS) +static std::map<int, const char*> FilenameCache; +static std::string FaultFileName = +#if defined (TARGET_READ_FAULT) + TARGET_READ_FAULT; +#elif defined (TARGET_WRITE_FAULT) + TARGET_WRITE_FAULT; +#elif defined (TARGET_FSYNC_FAULT) + TARGET_FSYNC_FAULT; +#endif // defined (TARGET_READ_FAULT) +#endif // defined (TARGET_INJECT_FAULTS) + +int ota_open(const char* path, int oflags) { +#if defined (TARGET_INJECT_FAULTS) + // Let the caller handle errors; we do not care if open succeeds or fails + int fd = open(path, oflags); + FilenameCache[fd] = path; + return fd; +#else + return open(path, oflags); +#endif +} + +int ota_open(const char* path, int oflags, mode_t mode) { +#if defined (TARGET_INJECT_FAULTS) + int fd = open(path, oflags, mode); + FilenameCache[fd] = path; + return fd; +#else + return open(path, oflags, mode); +#endif +} + +FILE* ota_fopen(const char* path, const char* mode) { +#if defined (TARGET_INJECT_FAULTS) + FILE* fh = fopen(path, mode); + FilenameCache[(intptr_t)fh] = path; + return fh; +#else + return fopen(path, mode); +#endif +} + +int ota_close(int fd) { +#if defined (TARGET_INJECT_FAULTS) + // descriptors can be reused, so make sure not to leave them in the cahce + FilenameCache.erase(fd); +#endif + return close(fd); +} + +int ota_fclose(FILE* fh) { +#if defined (TARGET_INJECT_FAULTS) + FilenameCache.erase((intptr_t)fh); +#endif + return fclose(fh); +} + +size_t ota_fread(void* ptr, size_t size, size_t nitems, FILE* stream) { +#if defined (TARGET_READ_FAULT) + if (FilenameCache.find((intptr_t)stream) != FilenameCache.end() + && FilenameCache[(intptr_t)stream] == FaultFileName) { + FaultFileName = ""; + errno = EIO; + return 0; + } else { + return fread(ptr, size, nitems, stream); + } +#else + return fread(ptr, size, nitems, stream); +#endif +} + +ssize_t ota_read(int fd, void* buf, size_t nbyte) { +#if defined (TARGET_READ_FAULT) + if (FilenameCache.find(fd) != FilenameCache.end() + && FilenameCache[fd] == FaultFileName) { + FaultFileName = ""; + errno = EIO; + return -1; + } else { + return read(fd, buf, nbyte); + } +#else + return read(fd, buf, nbyte); +#endif +} + +size_t ota_fwrite(const void* ptr, size_t size, size_t count, FILE* stream) { +#if defined (TARGET_WRITE_FAULT) + if (FilenameCache.find((intptr_t)stream) != FilenameCache.end() + && FilenameCache[(intptr_t)stream] == FaultFileName) { + FaultFileName = ""; + errno = EIO; + return 0; + } else { + return fwrite(ptr, size, count, stream); + } +#else + return fwrite(ptr, size, count, stream); +#endif +} + +ssize_t ota_write(int fd, const void* buf, size_t nbyte) { +#if defined (TARGET_WRITE_FAULT) + if (FilenameCache.find(fd) != FilenameCache.end() + && FilenameCache[fd] == FaultFileName) { + FaultFileName = ""; + errno = EIO; + return -1; + } else { + return write(fd, buf, nbyte); + } +#else + return write(fd, buf, nbyte); +#endif +} + +int ota_fsync(int fd) { +#if defined (TARGET_FSYNC_FAULT) + if (FilenameCache.find(fd) != FilenameCache.end() + && FilenameCache[fd] == FaultFileName) { + FaultFileName = ""; + errno = EIO; + return -1; + } else { + return fsync(fd); + } +#else + return fsync(fd); +#endif +} diff --git a/otafault/ota_io.h b/otafault/ota_io.h new file mode 100644 index 000000000..641a5ae0a --- /dev/null +++ b/otafault/ota_io.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Provide a series of proxy functions for basic file accessors. + * The behavior of these functions can be changed to return different + * errors under a variety of conditions. + */ + +#ifndef _UPDATER_OTA_IO_H_ +#define _UPDATER_OTA_IO_H_ + +#include <stdio.h> +#include <sys/stat.h> + +int ota_open(const char* path, int oflags); + +int ota_open(const char* path, int oflags, mode_t mode); + +FILE* ota_fopen(const char* filename, const char* mode); + +int ota_close(int fd); + +int ota_fclose(FILE* fh); + +size_t ota_fread(void* ptr, size_t size, size_t nitems, FILE* stream); + +ssize_t ota_read(int fd, void* buf, size_t nbyte); + +size_t ota_fwrite(const void* ptr, size_t size, size_t count, FILE* stream); + +ssize_t ota_write(int fd, const void* buf, size_t nbyte); + +int ota_fsync(int fd); + +#endif diff --git a/otafault/test.cpp b/otafault/test.cpp new file mode 100644 index 000000000..a0f731517 --- /dev/null +++ b/otafault/test.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> + +#include "ota_io.h" + +int main(int argc, char **argv) { + int fd = open("testdata/test.file", O_RDWR); + char buf[8]; + char *out = "321"; + int readv = ota_read(fd, buf, 4); + printf("Read returned %d\n", readv); + int writev = ota_write(fd, out, 4); + printf("Write returned %d\n", writev); + return 0; +} diff --git a/print_sha1.h b/print_sha1.h index 9e37c5fe3..fa3d7e009 100644 --- a/print_sha1.h +++ b/print_sha1.h @@ -20,9 +20,9 @@ #include <stdint.h> #include <string> -#include "mincrypt/sha.h" +#include "openssl/sha.h" -static std::string print_sha1(const uint8_t sha1[SHA_DIGEST_SIZE], size_t len) { +static std::string print_sha1(const uint8_t sha1[SHA_DIGEST_LENGTH], size_t len) { const char* hex = "0123456789abcdef"; std::string result = ""; for (size_t i = 0; i < len; ++i) { @@ -32,11 +32,11 @@ static std::string print_sha1(const uint8_t sha1[SHA_DIGEST_SIZE], size_t len) { return result; } -static std::string print_sha1(const uint8_t sha1[SHA_DIGEST_SIZE]) { - return print_sha1(sha1, SHA_DIGEST_SIZE); +static std::string print_sha1(const uint8_t sha1[SHA_DIGEST_LENGTH]) { + return print_sha1(sha1, SHA_DIGEST_LENGTH); } -static std::string short_sha1(const uint8_t sha1[SHA_DIGEST_SIZE]) { +static std::string short_sha1(const uint8_t sha1[SHA_DIGEST_LENGTH]) { return print_sha1(sha1, 4); } diff --git a/tests/Android.mk b/tests/Android.mk index 4ce00b457..262fb8bfd 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -16,11 +16,40 @@ LOCAL_PATH := $(call my-dir) +# Unit tests include $(CLEAR_VARS) LOCAL_CLANG := true +LOCAL_MODULE := recovery_unit_test LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_STATIC_LIBRARIES := libverifier -LOCAL_SRC_FILES := asn1_decoder_test.cpp -LOCAL_MODULE := asn1_decoder_test -LOCAL_C_INCLUDES := $(LOCAL_PATH)/.. +LOCAL_SRC_FILES := unit/asn1_decoder_test.cpp +LOCAL_C_INCLUDES := bootable/recovery +include $(BUILD_NATIVE_TEST) + +# Component tests +include $(CLEAR_VARS) +LOCAL_CLANG := true +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk +LOCAL_MODULE := recovery_component_test +LOCAL_C_INCLUDES := bootable/recovery +LOCAL_SRC_FILES := component/verifier_test.cpp +LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_STATIC_LIBRARIES := \ + libbase \ + libverifier \ + libmincrypt \ + libminui \ + libminzip \ + libcutils \ + libc + +testdata_out_path := $(TARGET_OUT_DATA_NATIVE_TESTS)/recovery +testdata_files := $(call find-subdir-files, testdata/*) + +GEN := $(addprefix $(testdata_out_path)/, $(testdata_files)) +$(GEN): PRIVATE_PATH := $(LOCAL_PATH) +$(GEN): PRIVATE_CUSTOM_TOOL = cp $< $@ +$(GEN): $(testdata_out_path)/% : $(LOCAL_PATH)/% + $(transform-generated-source) +LOCAL_GENERATED_SOURCES += $(GEN) include $(BUILD_NATIVE_TEST) diff --git a/verifier_test.cpp b/tests/component/verifier_test.cpp index 21633dc20..d6f1e0bb9 100644 --- a/verifier_test.cpp +++ b/tests/component/verifier_test.cpp @@ -1,13 +1,13 @@ /* * Copyright (C) 2009 The Android Open Source Project * - * Licensed under the Apache License, Version 2.0 (the "License"); + * Licensed under the Apache License, version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software + * Unless required by applicable law or agree to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and @@ -16,19 +16,33 @@ #include <errno.h> #include <fcntl.h> -#include <stdarg.h> +#include <gtest/gtest.h> #include <stdio.h> #include <stdlib.h> -#include <string.h> #include <sys/types.h> #include <sys/stat.h> +#include <memory> +#include <string> +#include <vector> + +#include <android-base/stringprintf.h> + #include "common.h" -#include "verifier.h" -#include "ui.h" #include "mincrypt/sha.h" #include "mincrypt/sha256.h" #include "minzip/SysUtil.h" +#include "ui.h" +#include "verifier.h" + +#if defined(__LP64__) +#define NATIVE_TEST_PATH "/nativetest64" +#else +#define NATIVE_TEST_PATH "/nativetest" +#endif + +static const char* DATA_PATH = getenv("ANDROID_DATA"); +static const char* TESTDATA_PATH = "/recovery/testdata/"; // This is build/target/product/security/testkey.x509.pem after being // dumped out by dumpkey.jar. @@ -120,19 +134,17 @@ ECPublicKey test_ec_key = RecoveryUI* ui = NULL; -// verifier expects to find a UI object; we provide one that does -// nothing but print. -class FakeUI : public RecoveryUI { +class MockUI : public RecoveryUI { void Init() { } void SetStage(int, int) { } void SetLocale(const char*) { } - void SetBackground(Icon icon) { } + void SetBackground(Icon /*icon*/) { } - void SetProgressType(ProgressType determinate) { } - void ShowProgress(float portion, float seconds) { } - void SetProgress(float fraction) { } + void SetProgressType(ProgressType /*determinate*/) { } + void ShowProgress(float /*portion*/, float /*seconds*/) { } + void SetProgress(float /*fraction*/) { } - void ShowText(bool visible) { } + void ShowText(bool /*visible*/) { } bool IsTextVisible() { return false; } bool WasTextEverVisible() { return false; } void Print(const char* fmt, ...) { @@ -149,9 +161,10 @@ class FakeUI : public RecoveryUI { } void ShowFile(const char*) { } - void StartMenu(const char* const * headers, const char* const * items, - int initial_selection) { } - int SelectMenu(int sel) { return 0; } + void StartMenu(const char* const* /*headers*/, + const char* const* /*items*/, + int /*initial_selection*/) { } + int SelectMenu(int /*sel*/) { return 0; } void EndMenu() { } }; @@ -163,100 +176,93 @@ ui_print(const char* format, ...) { va_end(ap); } -static Certificate* add_certificate(Certificate** certsp, int* num_keys, - Certificate::KeyType key_type) { - int i = *num_keys; - *num_keys = *num_keys + 1; - *certsp = (Certificate*) realloc(*certsp, *num_keys * sizeof(Certificate)); - Certificate* certs = *certsp; - certs[i].rsa = NULL; - certs[i].ec = NULL; - certs[i].key_type = key_type; - certs[i].hash_len = SHA_DIGEST_SIZE; - return &certs[i]; -} - -int main(int argc, char **argv) { - if (argc < 2) { - fprintf(stderr, "Usage: %s [-sha256] [-ec | -f4 | -file <keys>] <package>\n", argv[0]); - return 2; - } - Certificate* certs = NULL; - int num_keys = 0; +class VerifierTest : public testing::TestWithParam<std::vector<std::string>> { + public: + MemMapping memmap; + std::vector<Certificate> certs; - int argn = 1; - while (argn < argc) { - if (strcmp(argv[argn], "-sha256") == 0) { - if (num_keys == 0) { - fprintf(stderr, "May only specify -sha256 after key type\n"); - return 2; + virtual void SetUp() { + std::vector<std::string> args = GetParam(); + std::string package = android::base::StringPrintf("%s%s%s%s", DATA_PATH, NATIVE_TEST_PATH, + TESTDATA_PATH, args[0].c_str()); + for (auto it = ++(args.cbegin()); it != args.cend(); ++it) { + if (it->substr(it->length() - 3, it->length()) == "256") { + if (certs.empty()) { + FAIL() << "May only specify -sha256 after key type\n"; + } + certs.back().hash_len = SHA256_DIGEST_SIZE; + } else if (*it == "ec") { + certs.emplace_back(SHA_DIGEST_SIZE, Certificate::EC, + nullptr, std::unique_ptr<ECPublicKey>(new ECPublicKey(test_ec_key))); + } else if (*it == "e3") { + certs.emplace_back(SHA_DIGEST_SIZE, Certificate::RSA, + std::unique_ptr<RSAPublicKey>(new RSAPublicKey(test_key)), nullptr); + } else if (*it == "f4") { + certs.emplace_back(SHA_DIGEST_SIZE, Certificate::RSA, + std::unique_ptr<RSAPublicKey>(new RSAPublicKey(test_f4_key)), nullptr); } - ++argn; - Certificate* cert = &certs[num_keys - 1]; - cert->hash_len = SHA256_DIGEST_SIZE; - } else if (strcmp(argv[argn], "-ec") == 0) { - ++argn; - Certificate* cert = add_certificate(&certs, &num_keys, Certificate::EC); - cert->ec = &test_ec_key; - } else if (strcmp(argv[argn], "-e3") == 0) { - ++argn; - Certificate* cert = add_certificate(&certs, &num_keys, Certificate::RSA); - cert->rsa = &test_key; - } else if (strcmp(argv[argn], "-f4") == 0) { - ++argn; - Certificate* cert = add_certificate(&certs, &num_keys, Certificate::RSA); - cert->rsa = &test_f4_key; - } else if (strcmp(argv[argn], "-file") == 0) { - if (certs != NULL) { - fprintf(stderr, "Cannot specify -file with other certs specified\n"); - return 2; - } - ++argn; - certs = load_keys(argv[argn], &num_keys); - ++argn; - } else if (argv[argn][0] == '-') { - fprintf(stderr, "Unknown argument %s\n", argv[argn]); - return 2; - } else { - break; + } + if (certs.empty()) { + certs.emplace_back(SHA_DIGEST_SIZE, Certificate::RSA, + std::unique_ptr<RSAPublicKey>(new RSAPublicKey(test_key)), nullptr); + } + if (sysMapFile(package.c_str(), &memmap) != 0) { + FAIL() << "Failed to mmap " << package << ": " << strerror(errno) << "\n"; } } - if (argn == argc) { - fprintf(stderr, "Must specify package to verify\n"); - return 2; + static void SetUpTestCase() { + ui = new MockUI(); } +}; - if (num_keys == 0) { - certs = (Certificate*) calloc(1, sizeof(Certificate)); - if (certs == NULL) { - fprintf(stderr, "Failure allocating memory for default certificate\n"); - return 1; - } - certs->key_type = Certificate::RSA; - certs->rsa = &test_key; - certs->ec = NULL; - certs->hash_len = SHA_DIGEST_SIZE; - num_keys = 1; - } +class VerifierSuccessTest : public VerifierTest { +}; - ui = new FakeUI(); +class VerifierFailureTest : public VerifierTest { +}; - MemMapping map; - if (sysMapFile(argv[argn], &map) != 0) { - fprintf(stderr, "failed to mmap %s: %s\n", argv[argn], strerror(errno)); - return 4; - } +TEST_P(VerifierSuccessTest, VerifySucceed) { + ASSERT_EQ(verify_file(memmap.addr, memmap.length, certs), VERIFY_SUCCESS); +} - int result = verify_file(map.addr, map.length, certs, num_keys); - if (result == VERIFY_SUCCESS) { - printf("VERIFIED\n"); - return 0; - } else if (result == VERIFY_FAILURE) { - printf("NOT VERIFIED\n"); - return 1; - } else { - printf("bad return value\n"); - return 3; - } +TEST_P(VerifierFailureTest, VerifyFailure) { + ASSERT_EQ(verify_file(memmap.addr, memmap.length, certs), VERIFY_FAILURE); } + +INSTANTIATE_TEST_CASE_P(SingleKeySuccess, VerifierSuccessTest, + ::testing::Values( + std::vector<std::string>({"otasigned.zip", "e3"}), + std::vector<std::string>({"otasigned_f4.zip", "f4"}), + std::vector<std::string>({"otasigned_sha256.zip", "e3", "sha256"}), + std::vector<std::string>({"otasigned_f4_sha256.zip", "f4", "sha256"}), + std::vector<std::string>({"otasigned_ecdsa_sha256.zip", "ec", "sha256"}))); + +INSTANTIATE_TEST_CASE_P(MultiKeySuccess, VerifierSuccessTest, + ::testing::Values( + std::vector<std::string>({"otasigned.zip", "f4", "e3"}), + std::vector<std::string>({"otasigned_f4.zip", "ec", "f4"}), + std::vector<std::string>({"otasigned_sha256.zip", "ec", "e3", "e3", "sha256"}), + std::vector<std::string>({"otasigned_f4_sha256.zip", "ec", "sha256", "e3", "f4", "sha256"}), + std::vector<std::string>({"otasigned_ecdsa_sha256.zip", "f4", "sha256", "e3", "ec", "sha256"}))); + +INSTANTIATE_TEST_CASE_P(WrongKey, VerifierFailureTest, + ::testing::Values( + std::vector<std::string>({"otasigned.zip", "f4"}), + std::vector<std::string>({"otasigned_f4.zip", "e3"}), + std::vector<std::string>({"otasigned_ecdsa_sha256.zip", "e3", "sha256"}))); + +INSTANTIATE_TEST_CASE_P(WrongHash, VerifierFailureTest, + ::testing::Values( + std::vector<std::string>({"otasigned.zip", "e3", "sha256"}), + std::vector<std::string>({"otasigned_f4.zip", "f4", "sha256"}), + std::vector<std::string>({"otasigned_sha256.zip"}), + std::vector<std::string>({"otasigned_f4_sha256.zip", "f4"}), + std::vector<std::string>({"otasigned_ecdsa_sha256.zip"}))); + +INSTANTIATE_TEST_CASE_P(BadPackage, VerifierFailureTest, + ::testing::Values( + std::vector<std::string>({"random.zip"}), + std::vector<std::string>({"fake-eocd.zip"}), + std::vector<std::string>({"alter-metadata.zip"}), + std::vector<std::string>({"alter-footer.zip"}))); diff --git a/testdata/alter-footer.zip b/tests/testdata/alter-footer.zip Binary files differindex f497ec000..f497ec000 100644 --- a/testdata/alter-footer.zip +++ b/tests/testdata/alter-footer.zip diff --git a/testdata/alter-metadata.zip b/tests/testdata/alter-metadata.zip Binary files differindex 1c71fbc49..1c71fbc49 100644 --- a/testdata/alter-metadata.zip +++ b/tests/testdata/alter-metadata.zip diff --git a/testdata/fake-eocd.zip b/tests/testdata/fake-eocd.zip Binary files differindex 15dc0a946..15dc0a946 100644 --- a/testdata/fake-eocd.zip +++ b/tests/testdata/fake-eocd.zip diff --git a/testdata/jarsigned.zip b/tests/testdata/jarsigned.zip Binary files differindex 8b1ef8bdd..8b1ef8bdd 100644 --- a/testdata/jarsigned.zip +++ b/tests/testdata/jarsigned.zip diff --git a/testdata/otasigned.zip b/tests/testdata/otasigned.zip Binary files differindex a6bc53e41..a6bc53e41 100644 --- a/testdata/otasigned.zip +++ b/tests/testdata/otasigned.zip diff --git a/testdata/otasigned_ecdsa_sha256.zip b/tests/testdata/otasigned_ecdsa_sha256.zip Binary files differindex 999fcdd0f..999fcdd0f 100644 --- a/testdata/otasigned_ecdsa_sha256.zip +++ b/tests/testdata/otasigned_ecdsa_sha256.zip diff --git a/testdata/otasigned_f4.zip b/tests/testdata/otasigned_f4.zip Binary files differindex dd1e4dd40..dd1e4dd40 100644 --- a/testdata/otasigned_f4.zip +++ b/tests/testdata/otasigned_f4.zip diff --git a/testdata/otasigned_f4_sha256.zip b/tests/testdata/otasigned_f4_sha256.zip Binary files differindex 3af408c40..3af408c40 100644 --- a/testdata/otasigned_f4_sha256.zip +++ b/tests/testdata/otasigned_f4_sha256.zip diff --git a/testdata/otasigned_sha256.zip b/tests/testdata/otasigned_sha256.zip Binary files differindex 0ed4409b3..0ed4409b3 100644 --- a/testdata/otasigned_sha256.zip +++ b/tests/testdata/otasigned_sha256.zip diff --git a/testdata/random.zip b/tests/testdata/random.zip Binary files differindex 18c0b3b9f..18c0b3b9f 100644 --- a/testdata/random.zip +++ b/tests/testdata/random.zip diff --git a/testdata/test_f4.pk8 b/tests/testdata/test_f4.pk8 Binary files differindex 3052613c5..3052613c5 100644 --- a/testdata/test_f4.pk8 +++ b/tests/testdata/test_f4.pk8 diff --git a/testdata/test_f4.x509.pem b/tests/testdata/test_f4.x509.pem index 814abcf99..814abcf99 100644 --- a/testdata/test_f4.x509.pem +++ b/tests/testdata/test_f4.x509.pem diff --git a/testdata/test_f4_sha256.x509.pem b/tests/testdata/test_f4_sha256.x509.pem index 9d5376b45..9d5376b45 100644 --- a/testdata/test_f4_sha256.x509.pem +++ b/tests/testdata/test_f4_sha256.x509.pem diff --git a/testdata/testkey.pk8 b/tests/testdata/testkey.pk8 Binary files differindex 586c1bd5c..586c1bd5c 100644 --- a/testdata/testkey.pk8 +++ b/tests/testdata/testkey.pk8 diff --git a/testdata/testkey.x509.pem b/tests/testdata/testkey.x509.pem index e242d83e2..e242d83e2 100644 --- a/testdata/testkey.x509.pem +++ b/tests/testdata/testkey.x509.pem diff --git a/testdata/testkey_ecdsa.pk8 b/tests/testdata/testkey_ecdsa.pk8 Binary files differindex 9a521c8cf..9a521c8cf 100644 --- a/testdata/testkey_ecdsa.pk8 +++ b/tests/testdata/testkey_ecdsa.pk8 diff --git a/testdata/testkey_ecdsa.x509.pem b/tests/testdata/testkey_ecdsa.x509.pem index b12283645..b12283645 100644 --- a/testdata/testkey_ecdsa.x509.pem +++ b/tests/testdata/testkey_ecdsa.x509.pem diff --git a/testdata/testkey_sha256.x509.pem b/tests/testdata/testkey_sha256.x509.pem index 002ce8968..002ce8968 100644 --- a/testdata/testkey_sha256.x509.pem +++ b/tests/testdata/testkey_sha256.x509.pem diff --git a/testdata/unsigned.zip b/tests/testdata/unsigned.zip Binary files differindex 24e3eadac..24e3eadac 100644 --- a/testdata/unsigned.zip +++ b/tests/testdata/unsigned.zip diff --git a/tests/asn1_decoder_test.cpp b/tests/unit/asn1_decoder_test.cpp index af96d87d2..af96d87d2 100644 --- a/tests/asn1_decoder_test.cpp +++ b/tests/unit/asn1_decoder_test.cpp diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp index de7e48182..705744eb6 100644 --- a/uncrypt/uncrypt.cpp +++ b/uncrypt/uncrypt.cpp @@ -42,6 +42,7 @@ #include <errno.h> #include <fcntl.h> #include <inttypes.h> +#include <libgen.h> #include <linux/fs.h> #include <stdarg.h> #include <stdio.h> @@ -52,9 +53,13 @@ #include <sys/types.h> #include <unistd.h> +#include <algorithm> #include <memory> +#include <vector> #include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/stringprintf.h> #include <android-base/strings.h> #include <cutils/android_reboot.h> #include <cutils/properties.h> @@ -63,59 +68,39 @@ #define LOG_TAG "uncrypt" #include <log/log.h> +#include "bootloader.h" #include "unique_fd.h" #define WINDOW_SIZE 5 -static const std::string cache_block_map = "/cache/recovery/block.map"; -static const std::string status_file = "/cache/recovery/uncrypt_status"; -static const std::string uncrypt_file = "/cache/recovery/uncrypt_file"; +static const std::string CACHE_BLOCK_MAP = "/cache/recovery/block.map"; +static const std::string COMMAND_FILE = "/cache/recovery/command"; +static const std::string STATUS_FILE = "/cache/recovery/uncrypt_status"; +static const std::string UNCRYPT_PATH_FILE = "/cache/recovery/uncrypt_file"; static struct fstab* fstab = NULL; static int write_at_offset(unsigned char* buffer, size_t size, int wfd, off64_t offset) { if (TEMP_FAILURE_RETRY(lseek64(wfd, offset, SEEK_SET)) == -1) { - ALOGE("error seeking to offset %" PRId64 ": %s\n", offset, strerror(errno)); + ALOGE("error seeking to offset %" PRId64 ": %s", offset, strerror(errno)); return -1; } - size_t written = 0; - while (written < size) { - ssize_t wrote = TEMP_FAILURE_RETRY(write(wfd, buffer + written, size - written)); - if (wrote == -1) { - ALOGE("error writing offset %" PRId64 ": %s\n", - offset + static_cast<off64_t>(written), strerror(errno)); - return -1; - } - written += wrote; + if (!android::base::WriteFully(wfd, buffer, size)) { + ALOGE("error writing offset %" PRId64 ": %s", offset, strerror(errno)); + return -1; } return 0; } -static void add_block_to_ranges(int** ranges, int* range_alloc, int* range_used, int new_block) { - // If the current block start is < 0, set the start to the new - // block. (This only happens for the very first block of the very - // first range.) - if ((*ranges)[*range_used*2-2] < 0) { - (*ranges)[*range_used*2-2] = new_block; - (*ranges)[*range_used*2-1] = new_block; - } - - if (new_block == (*ranges)[*range_used*2-1]) { +static void add_block_to_ranges(std::vector<int>& ranges, int new_block) { + if (!ranges.empty() && new_block == ranges.back()) { // If the new block comes immediately after the current range, // all we have to do is extend the current range. - ++(*ranges)[*range_used*2-1]; + ++ranges.back(); } else { // We need to start a new range. - - // If there isn't enough room in the array, we need to expand it. - if (*range_used >= *range_alloc) { - *range_alloc *= 2; - *ranges = reinterpret_cast<int*>(realloc(*ranges, *range_alloc * 2 * sizeof(int))); - } - - ++*range_used; - (*ranges)[*range_used*2-2] = new_block; - (*ranges)[*range_used*2-1] = new_block+1; + ranges.push_back(new_block); + ranges.push_back(new_block + 1); } } @@ -125,13 +110,13 @@ static struct fstab* read_fstab() { // The fstab path is always "/fstab.${ro.hardware}". char fstab_path[PATH_MAX+1] = "/fstab."; if (!property_get("ro.hardware", fstab_path+strlen(fstab_path), "")) { - ALOGE("failed to get ro.hardware\n"); + ALOGE("failed to get ro.hardware"); return NULL; } fstab = fs_mgr_read_fstab(fstab_path); if (!fstab) { - ALOGE("failed to read %s\n", fstab_path); + ALOGE("failed to read %s", fstab_path); return NULL; } @@ -168,77 +153,77 @@ static const char* find_block_device(const char* path, bool* encryptable, bool* } // Parse uncrypt_file to find the update package name. -static bool find_uncrypt_package(std::string& package_name) -{ - if (!android::base::ReadFileToString(uncrypt_file, &package_name)) { - ALOGE("failed to open \"%s\": %s\n", uncrypt_file.c_str(), strerror(errno)); +static bool find_uncrypt_package(const std::string& uncrypt_path_file, std::string* package_name) { + CHECK(package_name != nullptr); + std::string uncrypt_path; + if (!android::base::ReadFileToString(uncrypt_path_file, &uncrypt_path)) { + ALOGE("failed to open \"%s\": %s", uncrypt_path_file.c_str(), strerror(errno)); return false; } // Remove the trailing '\n' if present. - package_name = android::base::Trim(package_name); - + *package_name = android::base::Trim(uncrypt_path); return true; } static int produce_block_map(const char* path, const char* map_file, const char* blk_dev, bool encrypted, int status_fd) { - int mapfd = open(map_file, O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR); - if (mapfd == -1) { - ALOGE("failed to open %s\n", map_file); + std::string err; + if (!android::base::RemoveFileIfExists(map_file, &err)) { + ALOGE("failed to remove the existing map file %s: %s", map_file, err.c_str()); + return -1; + } + std::string tmp_map_file = std::string(map_file) + ".tmp"; + unique_fd mapfd(open(tmp_map_file.c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR)); + if (!mapfd) { + ALOGE("failed to open %s: %s\n", tmp_map_file.c_str(), strerror(errno)); return -1; } - std::unique_ptr<FILE, int(*)(FILE*)> mapf(fdopen(mapfd, "w"), fclose); // Make sure we can write to the status_file. if (!android::base::WriteStringToFd("0\n", status_fd)) { - ALOGE("failed to update \"%s\"\n", status_file.c_str()); + ALOGE("failed to update \"%s\"\n", STATUS_FILE.c_str()); return -1; } struct stat sb; if (stat(path, &sb) != 0) { - ALOGE("failed to stat %s\n", path); + ALOGE("failed to stat %s", path); return -1; } - ALOGI(" block size: %ld bytes\n", static_cast<long>(sb.st_blksize)); + ALOGI(" block size: %ld bytes", static_cast<long>(sb.st_blksize)); int blocks = ((sb.st_size-1) / sb.st_blksize) + 1; - ALOGI(" file size: %" PRId64 " bytes, %d blocks\n", sb.st_size, blocks); + ALOGI(" file size: %" PRId64 " bytes, %d blocks", sb.st_size, blocks); - int range_alloc = 1; - int range_used = 1; - int* ranges = reinterpret_cast<int*>(malloc(range_alloc * 2 * sizeof(int))); - ranges[0] = -1; - ranges[1] = -1; + std::vector<int> ranges; - fprintf(mapf.get(), "%s\n%" PRId64 " %ld\n", - blk_dev, sb.st_size, static_cast<long>(sb.st_blksize)); + std::string s = android::base::StringPrintf("%s\n%" PRId64 " %ld\n", + blk_dev, sb.st_size, static_cast<long>(sb.st_blksize)); + if (!android::base::WriteStringToFd(s, mapfd.get())) { + ALOGE("failed to write %s: %s", tmp_map_file.c_str(), strerror(errno)); + return -1; + } - unsigned char* buffers[WINDOW_SIZE]; + std::vector<std::vector<unsigned char>> buffers; if (encrypted) { - for (size_t i = 0; i < WINDOW_SIZE; ++i) { - buffers[i] = reinterpret_cast<unsigned char*>(malloc(sb.st_blksize)); - } + buffers.resize(WINDOW_SIZE, std::vector<unsigned char>(sb.st_blksize)); } int head_block = 0; int head = 0, tail = 0; - int fd = open(path, O_RDONLY); - unique_fd fd_holder(fd); - if (fd == -1) { - ALOGE("failed to open fd for reading: %s\n", strerror(errno)); + unique_fd fd(open(path, O_RDONLY)); + if (!fd) { + ALOGE("failed to open %s for reading: %s", path, strerror(errno)); return -1; } - int wfd = -1; - unique_fd wfd_holder(wfd); + unique_fd wfd(-1); if (encrypted) { wfd = open(blk_dev, O_WRONLY); - wfd_holder = unique_fd(wfd); - if (wfd == -1) { - ALOGE("failed to open fd for writing: %s\n", strerror(errno)); + if (!wfd) { + ALOGE("failed to open fd for writing: %s", strerror(errno)); return -1; } } @@ -256,13 +241,13 @@ static int produce_block_map(const char* path, const char* map_file, const char* if ((tail+1) % WINDOW_SIZE == head) { // write out head buffer int block = head_block; - if (ioctl(fd, FIBMAP, &block) != 0) { - ALOGE("failed to find block %d\n", head_block); + if (ioctl(fd.get(), FIBMAP, &block) != 0) { + ALOGE("failed to find block %d", head_block); return -1; } - add_block_to_ranges(&ranges, &range_alloc, &range_used, block); + add_block_to_ranges(ranges, block); if (encrypted) { - if (write_at_offset(buffers[head], sb.st_blksize, wfd, + if (write_at_offset(buffers[head].data(), sb.st_blksize, wfd.get(), static_cast<off64_t>(sb.st_blksize) * block) != 0) { return -1; } @@ -273,17 +258,13 @@ static int produce_block_map(const char* path, const char* map_file, const char* // read next block to tail if (encrypted) { - size_t so_far = 0; - while (so_far < static_cast<size_t>(sb.st_blksize) && pos < sb.st_size) { - ssize_t this_read = - TEMP_FAILURE_RETRY(read(fd, buffers[tail] + so_far, sb.st_blksize - so_far)); - if (this_read == -1) { - ALOGE("failed to read: %s\n", strerror(errno)); - return -1; - } - so_far += this_read; - pos += this_read; + size_t to_read = static_cast<size_t>( + std::min(static_cast<off64_t>(sb.st_blksize), sb.st_size - pos)); + if (!android::base::ReadFully(fd.get(), buffers[tail].data(), to_read)) { + ALOGE("failed to read: %s", strerror(errno)); + return -1; } + pos += to_read; } else { // If we're not encrypting; we don't need to actually read // anything, just skip pos forward as if we'd read a @@ -296,13 +277,13 @@ static int produce_block_map(const char* path, const char* map_file, const char* while (head != tail) { // write out head buffer int block = head_block; - if (ioctl(fd, FIBMAP, &block) != 0) { - ALOGE("failed to find block %d\n", head_block); + if (ioctl(fd.get(), FIBMAP, &block) != 0) { + ALOGE("failed to find block %d", head_block); return -1; } - add_block_to_ranges(&ranges, &range_alloc, &range_used, block); + add_block_to_ranges(ranges, block); if (encrypted) { - if (write_at_offset(buffers[head], sb.st_blksize, wfd, + if (write_at_offset(buffers[head].data(), sb.st_blksize, wfd.get(), static_cast<off64_t>(sb.st_blksize) * block) != 0) { return -1; } @@ -311,54 +292,118 @@ static int produce_block_map(const char* path, const char* map_file, const char* ++head_block; } - fprintf(mapf.get(), "%d\n", range_used); - for (int i = 0; i < range_used; ++i) { - fprintf(mapf.get(), "%d %d\n", ranges[i*2], ranges[i*2+1]); + if (!android::base::WriteStringToFd( + android::base::StringPrintf("%zu\n", ranges.size() / 2), mapfd.get())) { + ALOGE("failed to write %s: %s", tmp_map_file.c_str(), strerror(errno)); + return -1; + } + for (size_t i = 0; i < ranges.size(); i += 2) { + if (!android::base::WriteStringToFd( + android::base::StringPrintf("%d %d\n", ranges[i], ranges[i+1]), mapfd.get())) { + ALOGE("failed to write %s: %s", tmp_map_file.c_str(), strerror(errno)); + return -1; + } } - if (fsync(mapfd) == -1) { - ALOGE("failed to fsync \"%s\": %s\n", map_file, strerror(errno)); + if (fsync(mapfd.get()) == -1) { + ALOGE("failed to fsync \"%s\": %s", tmp_map_file.c_str(), strerror(errno)); return -1; } + if (close(mapfd.get() == -1)) { + ALOGE("failed to close %s: %s", tmp_map_file.c_str(), strerror(errno)); + return -1; + } + mapfd = -1; + if (encrypted) { - if (fsync(wfd) == -1) { - ALOGE("failed to fsync \"%s\": %s\n", blk_dev, strerror(errno)); + if (fsync(wfd.get()) == -1) { + ALOGE("failed to fsync \"%s\": %s", blk_dev, strerror(errno)); return -1; } + if (close(wfd.get()) == -1) { + ALOGE("failed to close %s: %s", blk_dev, strerror(errno)); + return -1; + } + wfd = -1; } + if (rename(tmp_map_file.c_str(), map_file) == -1) { + ALOGE("failed to rename %s to %s: %s", tmp_map_file.c_str(), map_file, strerror(errno)); + return -1; + } + // Sync dir to make rename() result written to disk. + std::string file_name = map_file; + std::string dir_name = dirname(&file_name[0]); + unique_fd dfd(open(dir_name.c_str(), O_RDONLY | O_DIRECTORY)); + if (!dfd) { + ALOGE("failed to open dir %s: %s", dir_name.c_str(), strerror(errno)); + return -1; + } + if (fsync(dfd.get()) == -1) { + ALOGE("failed to fsync %s: %s", dir_name.c_str(), strerror(errno)); + return -1; + } + if (close(dfd.get() == -1)) { + ALOGE("failed to close %s: %s", dir_name.c_str(), strerror(errno)); + return -1; + } + dfd = -1; return 0; } -static void wipe_misc() { - ALOGI("removing old commands from misc"); +static std::string get_misc_blk_device() { + struct fstab* fstab = read_fstab(); + if (fstab == nullptr) { + return ""; + } for (int i = 0; i < fstab->num_entries; ++i) { - struct fstab_rec* v = &fstab->recs[i]; - if (!v->mount_point) continue; - if (strcmp(v->mount_point, "/misc") == 0) { - int fd = open(v->blk_device, O_WRONLY | O_SYNC); - unique_fd fd_holder(fd); - - uint8_t zeroes[1088]; // sizeof(bootloader_message) from recovery - memset(zeroes, 0, sizeof(zeroes)); - - size_t written = 0; - size_t size = sizeof(zeroes); - while (written < size) { - ssize_t w = TEMP_FAILURE_RETRY(write(fd, zeroes, size-written)); - if (w == -1) { - ALOGE("zero write failed: %s\n", strerror(errno)); - return; - } else { - written += w; - } - } - if (fsync(fd) == -1) { - ALOGE("failed to fsync \"%s\": %s\n", v->blk_device, strerror(errno)); - return; - } + fstab_rec* v = &fstab->recs[i]; + if (v->mount_point != nullptr && strcmp(v->mount_point, "/misc") == 0) { + return v->blk_device; } } + return ""; +} + +static int read_bootloader_message(bootloader_message* out) { + std::string misc_blk_device = get_misc_blk_device(); + if (misc_blk_device.empty()) { + ALOGE("failed to find /misc partition."); + return -1; + } + unique_fd fd(open(misc_blk_device.c_str(), O_RDONLY)); + if (!fd) { + ALOGE("failed to open %s: %s", misc_blk_device.c_str(), strerror(errno)); + return -1; + } + if (!android::base::ReadFully(fd.get(), out, sizeof(*out))) { + ALOGE("failed to read %s: %s", misc_blk_device.c_str(), strerror(errno)); + return -1; + } + return 0; +} + +static int write_bootloader_message(const bootloader_message* in) { + std::string misc_blk_device = get_misc_blk_device(); + if (misc_blk_device.empty()) { + ALOGE("failed to find /misc partition."); + return -1; + } + unique_fd fd(open(misc_blk_device.c_str(), O_WRONLY | O_SYNC)); + if (!fd) { + ALOGE("failed to open %s: %s", misc_blk_device.c_str(), strerror(errno)); + return -1; + } + if (!android::base::WriteFully(fd.get(), in, sizeof(*in))) { + ALOGE("failed to write %s: %s", misc_blk_device.c_str(), strerror(errno)); + return -1; + } + // TODO: O_SYNC and fsync() duplicates each other? + if (fsync(fd.get()) == -1) { + ALOGE("failed to fsync %s: %s", misc_blk_device.c_str(), strerror(errno)); + return -1; + } + return 0; } static void reboot_to_recovery() { @@ -370,7 +415,7 @@ static void reboot_to_recovery() { ALOGE("reboot didn't succeed?"); } -int uncrypt(const char* input_path, const char* map_file, int status_fd) { +static int uncrypt(const char* input_path, const char* map_file, int status_fd) { ALOGI("update package is \"%s\"", input_path); @@ -397,8 +442,8 @@ int uncrypt(const char* input_path, const char* map_file, int status_fd) { // If the filesystem it's on isn't encrypted, we only produce the // block map, we don't rewrite the file contents (it would be // pointless to do so). - ALOGI("encryptable: %s\n", encryptable ? "yes" : "no"); - ALOGI(" encrypted: %s\n", encrypted ? "yes" : "no"); + ALOGI("encryptable: %s", encryptable ? "yes" : "no"); + ALOGI(" encrypted: %s", encrypted ? "yes" : "no"); // Recovery supports installing packages from 3 paths: /cache, // /data, and /sdcard. (On a particular device, other locations @@ -417,56 +462,113 @@ int uncrypt(const char* input_path, const char* map_file, int status_fd) { return 0; } -int main(int argc, char** argv) { - - if (argc != 3 && argc != 1 && (argc == 2 && strcmp(argv[1], "--reboot") != 0)) { - fprintf(stderr, "usage: %s [--reboot] [<transform_path> <map_file>]\n", argv[0]); - return 2; +static int uncrypt_wrapper(const char* input_path, const char* map_file, + const std::string& status_file) { + // The pipe has been created by the system server. + unique_fd status_fd(open(status_file.c_str(), O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR)); + if (!status_fd) { + ALOGE("failed to open pipe \"%s\": %s", status_file.c_str(), strerror(errno)); + return 1; } - // When uncrypt is started with "--reboot", it wipes misc and reboots. - // Otherwise it uncrypts the package and writes the block map. - if (argc == 2) { - if (read_fstab() == NULL) { - return 1; - } - wipe_misc(); - reboot_to_recovery(); - } else { - // The pipe has been created by the system server. - int status_fd = open(status_file.c_str(), O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR); - if (status_fd == -1) { - ALOGE("failed to open pipe \"%s\": %s\n", status_file.c_str(), strerror(errno)); + std::string package; + if (input_path == nullptr) { + if (!find_uncrypt_package(UNCRYPT_PATH_FILE, &package)) { + android::base::WriteStringToFd("-1\n", status_fd.get()); return 1; } - unique_fd status_fd_holder(status_fd); + input_path = package.c_str(); + } + CHECK(map_file != nullptr); + int status = uncrypt(input_path, map_file, status_fd.get()); + if (status != 0) { + android::base::WriteStringToFd("-1\n", status_fd.get()); + return 1; + } + android::base::WriteStringToFd("100\n", status_fd.get()); + return 0; +} + +static int clear_bcb(const std::string& status_file) { + unique_fd status_fd(open(status_file.c_str(), O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR)); + if (!status_fd) { + ALOGE("failed to open pipe \"%s\": %s", status_file.c_str(), strerror(errno)); + return 1; + } + bootloader_message boot = {}; + if (write_bootloader_message(&boot) != 0) { + android::base::WriteStringToFd("-1\n", status_fd.get()); + return 1; + } + android::base::WriteStringToFd("100\n", status_fd.get()); + return 0; +} - std::string package; - const char* input_path; - const char* map_file; +static int setup_bcb(const std::string& command_file, const std::string& status_file) { + unique_fd status_fd(open(status_file.c_str(), O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR)); + if (!status_fd) { + ALOGE("failed to open pipe \"%s\": %s", status_file.c_str(), strerror(errno)); + return 1; + } + std::string content; + if (!android::base::ReadFileToString(command_file, &content)) { + ALOGE("failed to read \"%s\": %s", command_file.c_str(), strerror(errno)); + android::base::WriteStringToFd("-1\n", status_fd.get()); + return 1; + } + bootloader_message boot = {}; + strlcpy(boot.command, "boot-recovery", sizeof(boot.command)); + strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery)); + strlcat(boot.recovery, content.c_str(), sizeof(boot.recovery)); + if (write_bootloader_message(&boot) != 0) { + ALOGE("failed to set bootloader message"); + android::base::WriteStringToFd("-1\n", status_fd.get()); + return 1; + } + android::base::WriteStringToFd("100\n", status_fd.get()); + return 0; +} + +static int read_bcb() { + bootloader_message boot; + if (read_bootloader_message(&boot) != 0) { + ALOGE("failed to get bootloader message"); + return 1; + } + printf("bcb command: %s\n", boot.command); + printf("bcb recovery:\n%s\n", boot.recovery); + return 0; +} +static void usage(const char* exename) { + fprintf(stderr, "Usage of %s:\n", exename); + fprintf(stderr, "%s [<package_path> <map_file>] Uncrypt ota package.\n", exename); + fprintf(stderr, "%s --reboot Clear BCB data and reboot to recovery.\n", exename); + fprintf(stderr, "%s --clear-bcb Clear BCB data in misc partition.\n", exename); + fprintf(stderr, "%s --setup-bcb Setup BCB data by command file.\n", exename); + fprintf(stderr, "%s --read-bcb Read BCB data from misc partition.\n", exename); +} + +int main(int argc, char** argv) { + if (argc == 2) { + if (strcmp(argv[1], "--reboot") == 0) { + reboot_to_recovery(); + } else if (strcmp(argv[1], "--clear-bcb") == 0) { + return clear_bcb(STATUS_FILE); + } else if (strcmp(argv[1], "--setup-bcb") == 0) { + return setup_bcb(COMMAND_FILE, STATUS_FILE); + } else if (strcmp(argv[1], "--read-bcb") == 0) { + return read_bcb(); + } + } else if (argc == 1 || argc == 3) { + const char* input_path = nullptr; + const char* map_file = CACHE_BLOCK_MAP.c_str(); if (argc == 3) { - // when command-line args are given this binary is being used - // for debugging. input_path = argv[1]; map_file = argv[2]; - } else { - if (!find_uncrypt_package(package)) { - android::base::WriteStringToFd("-1\n", status_fd); - return 1; - } - input_path = package.c_str(); - map_file = cache_block_map.c_str(); } - - int status = uncrypt(input_path, map_file, status_fd); - if (status != 0) { - android::base::WriteStringToFd("-1\n", status_fd); - return 1; - } - - android::base::WriteStringToFd("100\n", status_fd); + return uncrypt_wrapper(input_path, map_file, STATUS_FILE); } - - return 0; + usage(argv[0]); + return 2; } diff --git a/uncrypt/uncrypt.rc b/uncrypt/uncrypt.rc index 5f4c47936..b07c1dada 100644 --- a/uncrypt/uncrypt.rc +++ b/uncrypt/uncrypt.rc @@ -7,3 +7,13 @@ service pre-recovery /system/bin/uncrypt --reboot class main disabled oneshot + +service setup-bcb /system/bin/uncrypt --setup-bcb + class main + disabled + oneshot + +service clear-bcb /system/bin/uncrypt --clear-bcb + class main + disabled + oneshot
\ No newline at end of file diff --git a/updater/Android.mk b/updater/Android.mk index dcf437474..d7aa613e9 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -45,8 +45,8 @@ LOCAL_STATIC_LIBRARIES += \ endif LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UPDATER_LIBS) $(TARGET_RECOVERY_UPDATER_EXTRA_LIBS) -LOCAL_STATIC_LIBRARIES += libapplypatch libbase libedify libmtdutils libminzip libz -LOCAL_STATIC_LIBRARIES += libmincrypt libbz +LOCAL_STATIC_LIBRARIES += libapplypatch libbase libotafault libedify libmtdutils libminzip libz +LOCAL_STATIC_LIBRARIES += libbz LOCAL_STATIC_LIBRARIES += libcutils liblog libc LOCAL_STATIC_LIBRARIES += libselinux tune2fs_static_libraries := \ diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index c6daf7db5..44de4e031 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -43,8 +43,9 @@ #include "applypatch/applypatch.h" #include "edify/expr.h" #include "install.h" -#include "mincrypt/sha.h" +#include "openssl/sha.h" #include "minzip/Hash.h" +#include "otafault/ota_io.h" #include "print_sha1.h" #include "unique_fd.h" #include "updater.h" @@ -139,7 +140,7 @@ static bool range_overlaps(const RangeSet& r1, const RangeSet& r2) { static int read_all(int fd, uint8_t* data, size_t size) { size_t so_far = 0; while (so_far < size) { - ssize_t r = TEMP_FAILURE_RETRY(read(fd, data+so_far, size-so_far)); + ssize_t r = TEMP_FAILURE_RETRY(ota_read(fd, data+so_far, size-so_far)); if (r == -1) { fprintf(stderr, "read failed: %s\n", strerror(errno)); return -1; @@ -156,7 +157,7 @@ static int read_all(int fd, std::vector<uint8_t>& buffer, size_t size) { static int write_all(int fd, const uint8_t* data, size_t size) { size_t written = 0; while (written < size) { - ssize_t w = TEMP_FAILURE_RETRY(write(fd, data+written, size-written)); + ssize_t w = TEMP_FAILURE_RETRY(ota_write(fd, data+written, size-written)); if (w == -1) { fprintf(stderr, "write failed: %s\n", strerror(errno)); return -1; @@ -407,10 +408,10 @@ static int LoadSrcTgtVersion1(CommandParameters& params, RangeSet& tgt, size_t& static int VerifyBlocks(const std::string& expected, const std::vector<uint8_t>& buffer, const size_t blocks, bool printerror) { - uint8_t digest[SHA_DIGEST_SIZE]; + uint8_t digest[SHA_DIGEST_LENGTH]; const uint8_t* data = buffer.data(); - SHA_hash(data, blocks * BLOCKSIZE, digest); + SHA1(data, blocks * BLOCKSIZE, digest); std::string hexdigest = print_sha1(digest); @@ -553,7 +554,7 @@ static int LoadStash(const std::string& base, const std::string& id, bool verify return -1; } - int fd = TEMP_FAILURE_RETRY(open(fn.c_str(), O_RDONLY)); + int fd = TEMP_FAILURE_RETRY(ota_open(fn.c_str(), O_RDONLY)); unique_fd fd_holder(fd); if (fd == -1) { @@ -610,7 +611,7 @@ static int WriteStash(const std::string& base, const std::string& id, int blocks fprintf(stderr, " writing %d blocks to %s\n", blocks, cn.c_str()); - int fd = TEMP_FAILURE_RETRY(open(fn.c_str(), O_WRONLY | O_CREAT | O_TRUNC, STASH_FILE_MODE)); + int fd = TEMP_FAILURE_RETRY(ota_open(fn.c_str(), O_WRONLY | O_CREAT | O_TRUNC, STASH_FILE_MODE)); unique_fd fd_holder(fd); if (fd == -1) { @@ -622,7 +623,7 @@ static int WriteStash(const std::string& base, const std::string& id, int blocks return -1; } - if (fsync(fd) == -1) { + if (ota_fsync(fd) == -1) { fprintf(stderr, "fsync \"%s\" failed: %s\n", fn.c_str(), strerror(errno)); return -1; } @@ -634,7 +635,7 @@ static int WriteStash(const std::string& base, const std::string& id, int blocks } std::string dname = GetStashFileName(base, "", ""); - int dfd = TEMP_FAILURE_RETRY(open(dname.c_str(), O_RDONLY | O_DIRECTORY)); + int dfd = TEMP_FAILURE_RETRY(ota_open(dname.c_str(), O_RDONLY | O_DIRECTORY)); unique_fd dfd_holder(dfd); if (dfd == -1) { @@ -642,7 +643,7 @@ static int WriteStash(const std::string& base, const std::string& id, int blocks return -1; } - if (fsync(dfd) == -1) { + if (ota_fsync(dfd) == -1) { fprintf(stderr, "fsync \"%s\" failed: %s\n", dname.c_str(), strerror(errno)); return -1; } @@ -662,10 +663,8 @@ static int CreateStash(State* state, int maxblocks, const char* blockdev, std::s // Stash directory should be different for each partition to avoid conflicts // when updating multiple partitions at the same time, so we use the hash of // the block device name as the base directory - SHA_CTX ctx; - SHA_init(&ctx); - SHA_update(&ctx, blockdev, strlen(blockdev)); - const uint8_t* digest = SHA_final(&ctx); + uint8_t digest[SHA_DIGEST_LENGTH]; + SHA1(reinterpret_cast<const uint8_t*>(blockdev), strlen(blockdev), digest); base = print_sha1(digest); std::string dirname = GetStashFileName(base, "", ""); @@ -1348,7 +1347,7 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, int /* arg return StringValue(strdup("")); } - params.fd = TEMP_FAILURE_RETRY(open(blockdev_filename->data, O_RDWR)); + params.fd = TEMP_FAILURE_RETRY(ota_open(blockdev_filename->data, O_RDWR)); unique_fd fd_holder(params.fd); if (params.fd == -1) { @@ -1467,7 +1466,7 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, int /* arg } if (params.canwrite) { - if (fsync(params.fd) == -1) { + if (ota_fsync(params.fd) == -1) { fprintf(stderr, "fsync failed: %s\n", strerror(errno)); goto pbiudone; } @@ -1492,7 +1491,7 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, int /* arg rc = 0; pbiudone: - if (fsync(params.fd) == -1) { + if (ota_fsync(params.fd) == -1) { fprintf(stderr, "fsync failed: %s\n", strerror(errno)); } // params.fd will be automatically closed because of the fd_holder above. @@ -1616,7 +1615,7 @@ Value* RangeSha1Fn(const char* name, State* state, int /* argc */, Expr* argv[]) return StringValue(strdup("")); } - int fd = open(blockdev_filename->data, O_RDWR); + int fd = ota_open(blockdev_filename->data, O_RDWR); unique_fd fd_holder(fd); if (fd < 0) { ErrorAbort(state, "open \"%s\" failed: %s", blockdev_filename->data, strerror(errno)); @@ -1627,7 +1626,7 @@ Value* RangeSha1Fn(const char* name, State* state, int /* argc */, Expr* argv[]) parse_range(ranges->data, rs); SHA_CTX ctx; - SHA_init(&ctx); + SHA1_Init(&ctx); std::vector<uint8_t> buffer(BLOCKSIZE); for (size_t i = 0; i < rs.count; ++i) { @@ -1643,10 +1642,11 @@ Value* RangeSha1Fn(const char* name, State* state, int /* argc */, Expr* argv[]) return StringValue(strdup("")); } - SHA_update(&ctx, buffer.data(), BLOCKSIZE); + SHA1_Update(&ctx, buffer.data(), BLOCKSIZE); } } - const uint8_t* digest = SHA_final(&ctx); + uint8_t digest[SHA_DIGEST_LENGTH]; + SHA1_Final(digest, &ctx); return StringValue(strdup(print_sha1(digest).c_str())); } @@ -1669,7 +1669,7 @@ Value* CheckFirstBlockFn(const char* name, State* state, int argc, Expr* argv[]) return StringValue(strdup("")); } - int fd = open(arg_filename->data, O_RDONLY); + int fd = ota_open(arg_filename->data, O_RDONLY); unique_fd fd_holder(fd); if (fd == -1) { ErrorAbort(state, "open \"%s\" failed: %s", arg_filename->data, strerror(errno)); diff --git a/updater/install.cpp b/updater/install.cpp index b09086964..413e147a1 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -34,6 +34,9 @@ #include <linux/xattr.h> #include <inttypes.h> +#include <memory> +#include <vector> + #include <android-base/parseint.h> #include <android-base/strings.h> #include <android-base/stringprintf.h> @@ -44,10 +47,11 @@ #include "cutils/misc.h" #include "cutils/properties.h" #include "edify/expr.h" -#include "mincrypt/sha.h" +#include "openssl/sha.h" #include "minzip/DirUtil.h" #include "mtdutils/mounts.h" #include "mtdutils/mtdutils.h" +#include "otafault/ota_io.h" #include "updater.h" #include "install.h" #include "tune2fs.h" @@ -91,10 +95,10 @@ void uiPrintf(State* state, const char* format, ...) { // Take a sha-1 digest and return it as a newly-allocated hex string. char* PrintSha1(const uint8_t* digest) { - char* buffer = reinterpret_cast<char*>(malloc(SHA_DIGEST_SIZE*2 + 1)); + char* buffer = reinterpret_cast<char*>(malloc(SHA_DIGEST_LENGTH*2 + 1)); const char* alphabet = "0123456789abcdef"; size_t i; - for (i = 0; i < SHA_DIGEST_SIZE; ++i) { + for (i = 0; i < SHA_DIGEST_LENGTH; ++i) { buffer[i*2] = alphabet[(digest[i] >> 4) & 0xf]; buffer[i*2+1] = alphabet[digest[i] & 0xf]; } @@ -439,8 +443,7 @@ Value* DeleteFn(const char* name, State* state, int argc, Expr* argv[]) { for (int i = 0; i < argc; ++i) { paths[i] = Evaluate(state, argv[i]); if (paths[i] == NULL) { - int j; - for (j = 0; j < i; ++i) { + for (int j = 0; j < i; ++j) { free(paths[j]); } free(paths); @@ -555,18 +558,18 @@ Value* PackageExtractFileFn(const char* name, State* state, } { - int fd = TEMP_FAILURE_RETRY(open(dest_path, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, + int fd = TEMP_FAILURE_RETRY(ota_open(dest_path, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR)); if (fd == -1) { printf("%s: can't open %s for write: %s\n", name, dest_path, strerror(errno)); goto done2; } success = mzExtractZipEntryToFile(za, entry, fd); - if (fsync(fd) == -1) { + if (ota_fsync(fd) == -1) { printf("fsync of \"%s\" failed: %s\n", dest_path, strerror(errno)); success = false; } - if (close(fd) == -1) { + if (ota_close(fd) == -1) { printf("close of \"%s\" failed: %s\n", dest_path, strerror(errno)); success = false; } @@ -581,13 +584,13 @@ Value* PackageExtractFileFn(const char* name, State* state, // as the result. char* zip_path; + if (ReadArgs(state, argv, 1, &zip_path) < 0) return NULL; + Value* v = reinterpret_cast<Value*>(malloc(sizeof(Value))); v->type = VAL_BLOB; v->size = -1; v->data = NULL; - if (ReadArgs(state, argv, 1, &zip_path) < 0) return NULL; - ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; const ZipEntry* entry = mzFindZipEntry(za, zip_path); if (entry == NULL) { @@ -993,21 +996,21 @@ Value* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) { } FILE* f; - f = fopen(filename, "rb"); + f = ota_fopen(filename, "rb"); if (f == NULL) { ErrorAbort(state, "%s: failed to open %s: %s", name, filename, strerror(errno)); goto done; } - if (fread(buffer, 1, st.st_size, f) != static_cast<size_t>(st.st_size)) { + if (ota_fread(buffer, 1, st.st_size, f) != static_cast<size_t>(st.st_size)) { ErrorAbort(state, "%s: failed to read %lld bytes from %s", name, (long long)st.st_size+1, filename); - fclose(f); + ota_fclose(f); goto done; } buffer[st.st_size] = '\0'; - fclose(f); + ota_fclose(f); char* line; line = strtok(buffer, "\n"); @@ -1102,7 +1105,7 @@ Value* WriteRawImageFn(const char* name, State* state, int argc, Expr* argv[]) { if (contents->type == VAL_STRING) { // we're given a filename as the contents char* filename = contents->data; - FILE* f = fopen(filename, "rb"); + FILE* f = ota_fopen(filename, "rb"); if (f == NULL) { printf("%s: can't open %s: %s\n", name, filename, strerror(errno)); result = strdup(""); @@ -1112,12 +1115,12 @@ Value* WriteRawImageFn(const char* name, State* state, int argc, Expr* argv[]) { success = true; char* buffer = reinterpret_cast<char*>(malloc(BUFSIZ)); int read; - while (success && (read = fread(buffer, 1, BUFSIZ, f)) > 0) { + while (success && (read = ota_fread(buffer, 1, BUFSIZ, f)) > 0) { int wrote = mtd_write_data(ctx, buffer, read); success = success && (wrote == read); } free(buffer); - fclose(f); + ota_fclose(f); } else { // we're given a blob as the contents ssize_t wrote = mtd_write_data(ctx, contents->data, contents->size); @@ -1193,44 +1196,40 @@ Value* ApplyPatchFn(const char* name, State* state, int argc, Expr* argv[]) { } int patchcount = (argc-4) / 2; - Value** patches = ReadValueVarArgs(state, argc-4, argv+4); + std::unique_ptr<Value*, decltype(&free)> arg_values(ReadValueVarArgs(state, argc-4, argv+4), + free); + if (!arg_values) { + return nullptr; + } + std::vector<std::unique_ptr<Value, decltype(&FreeValue)>> patch_shas; + std::vector<std::unique_ptr<Value, decltype(&FreeValue)>> patches; + // Protect values by unique_ptrs first to get rid of memory leak. + for (int i = 0; i < patchcount * 2; i += 2) { + patch_shas.emplace_back(arg_values.get()[i], FreeValue); + patches.emplace_back(arg_values.get()[i+1], FreeValue); + } - int i; - for (i = 0; i < patchcount; ++i) { - if (patches[i*2]->type != VAL_STRING) { + for (int i = 0; i < patchcount; ++i) { + if (patch_shas[i]->type != VAL_STRING) { ErrorAbort(state, "%s(): sha-1 #%d is not string", name, i); - break; + return nullptr; } - if (patches[i*2+1]->type != VAL_BLOB) { + if (patches[i]->type != VAL_BLOB) { ErrorAbort(state, "%s(): patch #%d is not blob", name, i); - break; - } - } - if (i != patchcount) { - for (i = 0; i < patchcount*2; ++i) { - FreeValue(patches[i]); + return nullptr; } - free(patches); - return NULL; } - char** patch_sha_str = reinterpret_cast<char**>(malloc(patchcount * sizeof(char*))); - for (i = 0; i < patchcount; ++i) { - patch_sha_str[i] = patches[i*2]->data; - patches[i*2]->data = NULL; - FreeValue(patches[i*2]); - patches[i] = patches[i*2+1]; + std::vector<char*> patch_sha_str; + std::vector<Value*> patch_ptrs; + for (int i = 0; i < patchcount; ++i) { + patch_sha_str.push_back(patch_shas[i]->data); + patch_ptrs.push_back(patches[i].get()); } int result = applypatch(source_filename, target_filename, target_sha1, target_size, - patchcount, patch_sha_str, patches, NULL); - - for (i = 0; i < patchcount; ++i) { - FreeValue(patches[i]); - } - free(patch_sha_str); - free(patches); + patchcount, patch_sha_str.data(), patch_ptrs.data(), NULL); return StringValue(strdup(result == 0 ? "t" : "")); } @@ -1349,24 +1348,27 @@ Value* Sha1CheckFn(const char* name, State* state, int argc, Expr* argv[]) { return ErrorAbort(state, "%s() expects at least 1 arg", name); } - Value** args = ReadValueVarArgs(state, argc, argv); - if (args == NULL) { - return NULL; + std::unique_ptr<Value*, decltype(&free)> arg_values(ReadValueVarArgs(state, argc, argv), free); + if (arg_values == nullptr) { + return nullptr; + } + std::vector<std::unique_ptr<Value, decltype(&FreeValue)>> args; + for (int i = 0; i < argc; ++i) { + args.emplace_back(arg_values.get()[i], FreeValue); } if (args[0]->size < 0) { return StringValue(strdup("")); } - uint8_t digest[SHA_DIGEST_SIZE]; - SHA_hash(args[0]->data, args[0]->size, digest); - FreeValue(args[0]); + uint8_t digest[SHA_DIGEST_LENGTH]; + SHA1(reinterpret_cast<uint8_t*>(args[0]->data), args[0]->size, digest); if (argc == 1) { return StringValue(PrintSha1(digest)); } int i; - uint8_t* arg_digest = reinterpret_cast<uint8_t*>(malloc(SHA_DIGEST_SIZE)); + uint8_t arg_digest[SHA_DIGEST_LENGTH]; for (i = 1; i < argc; ++i) { if (args[i]->type != VAL_STRING) { printf("%s(): arg %d is not a string; skipping", @@ -1375,22 +1377,16 @@ Value* Sha1CheckFn(const char* name, State* state, int argc, Expr* argv[]) { // Warn about bad args and skip them. printf("%s(): error parsing \"%s\" as sha-1; skipping", name, args[i]->data); - } else if (memcmp(digest, arg_digest, SHA_DIGEST_SIZE) == 0) { + } else if (memcmp(digest, arg_digest, SHA_DIGEST_LENGTH) == 0) { break; } - FreeValue(args[i]); } if (i >= argc) { // Didn't match any of the hex strings; return false. return StringValue(strdup("")); } - // Found a match; free all the remaining arguments and return the - // matched one. - int j; - for (j = i+1; j < argc; ++j) { - FreeValue(args[j]); - } - return args[i]; + // Found a match. + return args[i].release(); } // Read a local file and return its contents (the Value* returned @@ -1402,21 +1398,22 @@ Value* ReadFileFn(const char* name, State* state, int argc, Expr* argv[]) { char* filename; if (ReadArgs(state, argv, 1, &filename) < 0) return NULL; - Value* v = reinterpret_cast<Value*>(malloc(sizeof(Value))); + Value* v = static_cast<Value*>(malloc(sizeof(Value))); + if (v == nullptr) { + return nullptr; + } v->type = VAL_BLOB; + v->size = -1; + v->data = nullptr; FileContents fc; if (LoadFileContents(filename, &fc) != 0) { - free(filename); - v->size = -1; - v->data = NULL; - free(fc.data); - return v; + v->data = static_cast<char*>(malloc(fc.data.size())); + if (v->data != nullptr) { + memcpy(v->data, fc.data.data(), fc.data.size()); + v->size = fc.data.size(); + } } - - v->size = fc.size; - v->data = (char*)fc.data; - free(filename); return v; } @@ -1443,10 +1440,10 @@ Value* RebootNowFn(const char* name, State* state, int argc, Expr* argv[]) { // zero out the 'command' field of the bootloader message. memset(buffer, 0, sizeof(((struct bootloader_message*)0)->command)); - FILE* f = fopen(filename, "r+b"); + FILE* f = ota_fopen(filename, "r+b"); fseek(f, offsetof(struct bootloader_message, command), SEEK_SET); - fwrite(buffer, sizeof(((struct bootloader_message*)0)->command), 1, f); - fclose(f); + ota_fwrite(buffer, sizeof(((struct bootloader_message*)0)->command), 1, f); + ota_fclose(f); free(filename); strcpy(buffer, "reboot,"); @@ -1485,7 +1482,7 @@ Value* SetStageFn(const char* name, State* state, int argc, Expr* argv[]) { // bootloader message that the main recovery uses to save its // arguments in case of the device restarting midway through // package installation. - FILE* f = fopen(filename, "r+b"); + FILE* f = ota_fopen(filename, "r+b"); fseek(f, offsetof(struct bootloader_message, stage), SEEK_SET); int to_write = strlen(stagestr)+1; int max_size = sizeof(((struct bootloader_message*)0)->stage); @@ -1493,8 +1490,8 @@ Value* SetStageFn(const char* name, State* state, int argc, Expr* argv[]) { to_write = max_size; stagestr[max_size-1] = 0; } - fwrite(stagestr, to_write, 1, f); - fclose(f); + ota_fwrite(stagestr, to_write, 1, f); + ota_fclose(f); free(stagestr); return StringValue(filename); @@ -1511,10 +1508,10 @@ Value* GetStageFn(const char* name, State* state, int argc, Expr* argv[]) { if (ReadArgs(state, argv, 1, &filename) < 0) return NULL; char buffer[sizeof(((struct bootloader_message*)0)->stage)]; - FILE* f = fopen(filename, "rb"); + FILE* f = ota_fopen(filename, "rb"); fseek(f, offsetof(struct bootloader_message, stage), SEEK_SET); - fread(buffer, sizeof(buffer), 1, f); - fclose(f); + ota_fread(buffer, sizeof(buffer), 1, f); + ota_fclose(f); buffer[sizeof(buffer)-1] = '\0'; return StringValue(strdup(buffer)); @@ -1531,13 +1528,13 @@ Value* WipeBlockDeviceFn(const char* name, State* state, int argc, Expr* argv[]) size_t len; android::base::ParseUint(len_str, &len); - int fd = open(filename, O_WRONLY, 0644); + int fd = ota_open(filename, O_WRONLY, 0644); int success = wipe_block_device(fd, len); free(filename); free(len_str); - close(fd); + ota_close(fd); return StringValue(strdup(success ? "t" : "")); } diff --git a/verifier.cpp b/verifier.cpp index 61e5adf0b..9a2d60c66 100644 --- a/verifier.cpp +++ b/verifier.cpp @@ -113,7 +113,7 @@ static bool read_pkcs7(uint8_t* pkcs7_der, size_t pkcs7_der_len, uint8_t** sig_d // or no key matches the signature). int verify_file(unsigned char* addr, size_t length, - const Certificate* pKeys, unsigned int numKeys) { + const std::vector<Certificate>& keys) { ui->SetProgress(0.0); // An archive with a whole-file signature will end in six bytes: @@ -176,8 +176,7 @@ int verify_file(unsigned char* addr, size_t length, return VERIFY_FAILURE; } - size_t i; - for (i = 4; i < eocd_size-3; ++i) { + for (size_t i = 4; i < eocd_size-3; ++i) { if (eocd[i ] == 0x50 && eocd[i+1] == 0x4b && eocd[i+2] == 0x05 && eocd[i+3] == 0x06) { // if the sequence $50 $4b $05 $06 appears anywhere after @@ -193,8 +192,8 @@ int verify_file(unsigned char* addr, size_t length, bool need_sha1 = false; bool need_sha256 = false; - for (i = 0; i < numKeys; ++i) { - switch (pKeys[i].hash_len) { + for (const auto& key : keys) { + switch (key.hash_len) { case SHA_DIGEST_SIZE: need_sha1 = true; break; case SHA256_DIGEST_SIZE: need_sha256 = true; break; } @@ -225,7 +224,7 @@ int verify_file(unsigned char* addr, size_t length, const uint8_t* sha1 = SHA_final(&sha1_ctx); const uint8_t* sha256 = SHA256_final(&sha256_ctx); - uint8_t* sig_der = NULL; + uint8_t* sig_der = nullptr; size_t sig_der_length = 0; size_t signature_size = signature_start - FOOTER_SIZE; @@ -240,9 +239,10 @@ int verify_file(unsigned char* addr, size_t length, * any key can match, we need to try each before determining a verification * failure has happened. */ - for (i = 0; i < numKeys; ++i) { + size_t i = 0; + for (const auto& key : keys) { const uint8_t* hash; - switch (pKeys[i].hash_len) { + switch (key.hash_len) { case SHA_DIGEST_SIZE: hash = sha1; break; case SHA256_DIGEST_SIZE: hash = sha256; break; default: continue; @@ -250,15 +250,15 @@ int verify_file(unsigned char* addr, size_t length, // The 6 bytes is the "(signature_start) $ff $ff (comment_size)" that // the signing tool appends after the signature itself. - if (pKeys[i].key_type == Certificate::RSA) { + if (key.key_type == Certificate::RSA) { if (sig_der_length < RSANUMBYTES) { // "signature" block isn't big enough to contain an RSA block. LOGI("signature is too short for RSA key %zu\n", i); continue; } - if (!RSA_verify(pKeys[i].rsa, sig_der, RSANUMBYTES, - hash, pKeys[i].hash_len)) { + if (!RSA_verify(key.rsa.get(), sig_der, RSANUMBYTES, + hash, key.hash_len)) { LOGI("failed to verify against RSA key %zu\n", i); continue; } @@ -266,8 +266,8 @@ int verify_file(unsigned char* addr, size_t length, LOGI("whole-file signature verified against RSA key %zu\n", i); free(sig_der); return VERIFY_SUCCESS; - } else if (pKeys[i].key_type == Certificate::EC - && pKeys[i].hash_len == SHA256_DIGEST_SIZE) { + } else if (key.key_type == Certificate::EC + && key.hash_len == SHA256_DIGEST_SIZE) { p256_int r, s; if (!dsa_sig_unpack(sig_der, sig_der_length, &r, &s)) { LOGI("Not a DSA signature block for EC key %zu\n", i); @@ -276,7 +276,7 @@ int verify_file(unsigned char* addr, size_t length, p256_int p256_hash; p256_from_bin(hash, &p256_hash); - if (!p256_ecdsa_verify(&(pKeys[i].ec->x), &(pKeys[i].ec->y), + if (!p256_ecdsa_verify(&(key.ec->x), &(key.ec->y), &p256_hash, &r, &s)) { LOGI("failed to verify against EC key %zu\n", i); continue; @@ -286,8 +286,9 @@ int verify_file(unsigned char* addr, size_t length, free(sig_der); return VERIFY_SUCCESS; } else { - LOGI("Unknown key type %d\n", pKeys[i].key_type); + LOGI("Unknown key type %d\n", key.key_type); } + i++; } free(sig_der); LOGE("failed to verify whole-file signature\n"); @@ -323,140 +324,122 @@ int verify_file(unsigned char* addr, size_t length, // 4: 2048-bit RSA key with e=65537 and SHA-256 hash // 5: 256-bit EC key using the NIST P-256 curve parameters and SHA-256 hash // -// Returns NULL if the file failed to parse, or if it contain zero keys. -Certificate* -load_keys(const char* filename, int* numKeys) { - Certificate* out = NULL; - *numKeys = 0; - - FILE* f = fopen(filename, "r"); - if (f == NULL) { +// Returns true on success, and appends the found keys (at least one) to certs. +// Otherwise returns false if the file failed to parse, or if it contains zero +// keys. The contents in certs would be unspecified on failure. +bool load_keys(const char* filename, std::vector<Certificate>& certs) { + std::unique_ptr<FILE, decltype(&fclose)> f(fopen(filename, "r"), fclose); + if (!f) { LOGE("opening %s: %s\n", filename, strerror(errno)); - goto exit; + return false; } - { - int i; - bool done = false; - while (!done) { - ++*numKeys; - out = (Certificate*)realloc(out, *numKeys * sizeof(Certificate)); - Certificate* cert = out + (*numKeys - 1); - memset(cert, '\0', sizeof(Certificate)); - - char start_char; - if (fscanf(f, " %c", &start_char) != 1) goto exit; - if (start_char == '{') { - // a version 1 key has no version specifier. - cert->key_type = Certificate::RSA; - cert->rsa = (RSAPublicKey*)malloc(sizeof(RSAPublicKey)); - cert->rsa->exponent = 3; - cert->hash_len = SHA_DIGEST_SIZE; - } else if (start_char == 'v') { - int version; - if (fscanf(f, "%d {", &version) != 1) goto exit; - switch (version) { - case 2: - cert->key_type = Certificate::RSA; - cert->rsa = (RSAPublicKey*)malloc(sizeof(RSAPublicKey)); - cert->rsa->exponent = 65537; - cert->hash_len = SHA_DIGEST_SIZE; - break; - case 3: - cert->key_type = Certificate::RSA; - cert->rsa = (RSAPublicKey*)malloc(sizeof(RSAPublicKey)); - cert->rsa->exponent = 3; - cert->hash_len = SHA256_DIGEST_SIZE; - break; - case 4: - cert->key_type = Certificate::RSA; - cert->rsa = (RSAPublicKey*)malloc(sizeof(RSAPublicKey)); - cert->rsa->exponent = 65537; - cert->hash_len = SHA256_DIGEST_SIZE; - break; - case 5: - cert->key_type = Certificate::EC; - cert->ec = (ECPublicKey*)calloc(1, sizeof(ECPublicKey)); - cert->hash_len = SHA256_DIGEST_SIZE; - break; - default: - goto exit; - } + while (true) { + certs.emplace_back(0, Certificate::RSA, nullptr, nullptr); + Certificate& cert = certs.back(); + + char start_char; + if (fscanf(f.get(), " %c", &start_char) != 1) return false; + if (start_char == '{') { + // a version 1 key has no version specifier. + cert.key_type = Certificate::RSA; + cert.rsa = std::unique_ptr<RSAPublicKey>(new RSAPublicKey); + cert.rsa->exponent = 3; + cert.hash_len = SHA_DIGEST_SIZE; + } else if (start_char == 'v') { + int version; + if (fscanf(f.get(), "%d {", &version) != 1) return false; + switch (version) { + case 2: + cert.key_type = Certificate::RSA; + cert.rsa = std::unique_ptr<RSAPublicKey>(new RSAPublicKey); + cert.rsa->exponent = 65537; + cert.hash_len = SHA_DIGEST_SIZE; + break; + case 3: + cert.key_type = Certificate::RSA; + cert.rsa = std::unique_ptr<RSAPublicKey>(new RSAPublicKey); + cert.rsa->exponent = 3; + cert.hash_len = SHA256_DIGEST_SIZE; + break; + case 4: + cert.key_type = Certificate::RSA; + cert.rsa = std::unique_ptr<RSAPublicKey>(new RSAPublicKey); + cert.rsa->exponent = 65537; + cert.hash_len = SHA256_DIGEST_SIZE; + break; + case 5: + cert.key_type = Certificate::EC; + cert.ec = std::unique_ptr<ECPublicKey>(new ECPublicKey); + cert.hash_len = SHA256_DIGEST_SIZE; + break; + default: + return false; } + } - if (cert->key_type == Certificate::RSA) { - RSAPublicKey* key = cert->rsa; - if (fscanf(f, " %i , 0x%x , { %u", - &(key->len), &(key->n0inv), &(key->n[0])) != 3) { - goto exit; - } - if (key->len != RSANUMWORDS) { - LOGE("key length (%d) does not match expected size\n", key->len); - goto exit; - } - for (i = 1; i < key->len; ++i) { - if (fscanf(f, " , %u", &(key->n[i])) != 1) goto exit; - } - if (fscanf(f, " } , { %u", &(key->rr[0])) != 1) goto exit; - for (i = 1; i < key->len; ++i) { - if (fscanf(f, " , %u", &(key->rr[i])) != 1) goto exit; - } - fscanf(f, " } } "); - - LOGI("read key e=%d hash=%d\n", key->exponent, cert->hash_len); - } else if (cert->key_type == Certificate::EC) { - ECPublicKey* key = cert->ec; - int key_len; - unsigned int byte; - uint8_t x_bytes[P256_NBYTES]; - uint8_t y_bytes[P256_NBYTES]; - if (fscanf(f, " %i , { %u", &key_len, &byte) != 2) goto exit; - if (key_len != P256_NBYTES) { - LOGE("Key length (%d) does not match expected size %d\n", key_len, P256_NBYTES); - goto exit; - } - x_bytes[P256_NBYTES - 1] = byte; - for (i = P256_NBYTES - 2; i >= 0; --i) { - if (fscanf(f, " , %u", &byte) != 1) goto exit; - x_bytes[i] = byte; - } - if (fscanf(f, " } , { %u", &byte) != 1) goto exit; - y_bytes[P256_NBYTES - 1] = byte; - for (i = P256_NBYTES - 2; i >= 0; --i) { - if (fscanf(f, " , %u", &byte) != 1) goto exit; - y_bytes[i] = byte; - } - fscanf(f, " } } "); - p256_from_bin(x_bytes, &key->x); - p256_from_bin(y_bytes, &key->y); - } else { - LOGE("Unknown key type %d\n", cert->key_type); - goto exit; + if (cert.key_type == Certificate::RSA) { + RSAPublicKey* key = cert.rsa.get(); + if (fscanf(f.get(), " %i , 0x%x , { %u", &(key->len), &(key->n0inv), + &(key->n[0])) != 3) { + return false; } - - // if the line ends in a comma, this file has more keys. - switch (fgetc(f)) { - case ',': - // more keys to come. - break; - - case EOF: - done = true; - break; - - default: - LOGE("unexpected character between keys\n"); - goto exit; + if (key->len != RSANUMWORDS) { + LOGE("key length (%d) does not match expected size\n", key->len); + return false; + } + for (int i = 1; i < key->len; ++i) { + if (fscanf(f.get(), " , %u", &(key->n[i])) != 1) return false; + } + if (fscanf(f.get(), " } , { %u", &(key->rr[0])) != 1) return false; + for (int i = 1; i < key->len; ++i) { + if (fscanf(f.get(), " , %u", &(key->rr[i])) != 1) return false; + } + fscanf(f.get(), " } } "); + + LOGI("read key e=%d hash=%d\n", key->exponent, cert.hash_len); + } else if (cert.key_type == Certificate::EC) { + ECPublicKey* key = cert.ec.get(); + int key_len; + unsigned int byte; + uint8_t x_bytes[P256_NBYTES]; + uint8_t y_bytes[P256_NBYTES]; + if (fscanf(f.get(), " %i , { %u", &key_len, &byte) != 2) return false; + if (key_len != P256_NBYTES) { + LOGE("Key length (%d) does not match expected size %d\n", key_len, P256_NBYTES); + return false; + } + x_bytes[P256_NBYTES - 1] = byte; + for (int i = P256_NBYTES - 2; i >= 0; --i) { + if (fscanf(f.get(), " , %u", &byte) != 1) return false; + x_bytes[i] = byte; + } + if (fscanf(f.get(), " } , { %u", &byte) != 1) return false; + y_bytes[P256_NBYTES - 1] = byte; + for (int i = P256_NBYTES - 2; i >= 0; --i) { + if (fscanf(f.get(), " , %u", &byte) != 1) return false; + y_bytes[i] = byte; } + fscanf(f.get(), " } } "); + p256_from_bin(x_bytes, &key->x); + p256_from_bin(y_bytes, &key->y); + } else { + LOGE("Unknown key type %d\n", cert.key_type); + return false; } - } - fclose(f); - return out; + // if the line ends in a comma, this file has more keys. + int ch = fgetc(f.get()); + if (ch == ',') { + // more keys to come. + continue; + } else if (ch == EOF) { + break; + } else { + LOGE("unexpected character between keys\n"); + return false; + } + } -exit: - if (f) fclose(f); - free(out); - *numKeys = 0; - return NULL; + return true; } diff --git a/verifier.h b/verifier.h index 15f8d98e4..4eafc7565 100644 --- a/verifier.h +++ b/verifier.h @@ -17,6 +17,9 @@ #ifndef _RECOVERY_VERIFIER_H #define _RECOVERY_VERIFIER_H +#include <memory> +#include <vector> + #include "mincrypt/p256.h" #include "mincrypt/rsa.h" @@ -25,17 +28,25 @@ typedef struct { p256_int y; } ECPublicKey; -typedef struct { +struct Certificate { typedef enum { RSA, EC, } KeyType; + Certificate(int hash_len_, KeyType key_type_, + std::unique_ptr<RSAPublicKey>&& rsa_, + std::unique_ptr<ECPublicKey>&& ec_) : + hash_len(hash_len_), + key_type(key_type_), + rsa(std::move(rsa_)), + ec(std::move(ec_)) { } + int hash_len; // SHA_DIGEST_SIZE (SHA-1) or SHA256_DIGEST_SIZE (SHA-256) KeyType key_type; - RSAPublicKey* rsa; - ECPublicKey* ec; -} Certificate; + std::unique_ptr<RSAPublicKey> rsa; + std::unique_ptr<ECPublicKey> ec; +}; /* addr and length define a an update package file that has been * loaded (or mmap'ed, or whatever) into memory. Verify that the file @@ -43,9 +54,9 @@ typedef struct { * one of the constants below. */ int verify_file(unsigned char* addr, size_t length, - const Certificate *pKeys, unsigned int numKeys); + const std::vector<Certificate>& keys); -Certificate* load_keys(const char* filename, int* numKeys); +bool load_keys(const char* filename, std::vector<Certificate>& certs); #define VERIFY_SUCCESS 0 #define VERIFY_FAILURE 1 diff --git a/verifier_test.sh b/verifier_test.sh deleted file mode 100755 index 4761cef4a..000000000 --- a/verifier_test.sh +++ /dev/null @@ -1,121 +0,0 @@ -#!/bin/bash -# -# A test suite for recovery's package signature verifier. Run in a -# client where you have done envsetup, lunch, etc. -# -# TODO: find some way to get this run regularly along with the rest of -# the tests. - -EMULATOR_PORT=5580 -DATA_DIR=$ANDROID_BUILD_TOP/bootable/recovery/testdata - -WORK_DIR=/data/local/tmp - -# set to 0 to use a device instead -USE_EMULATOR=0 - -# ------------------------ - -if [ "$USE_EMULATOR" == 1 ]; then - emulator -wipe-data -noaudio -no-window -port $EMULATOR_PORT & - pid_emulator=$! - ADB="adb -s emulator-$EMULATOR_PORT " -else - ADB="adb -d " -fi - -echo "waiting to connect to device" -$ADB wait-for-device - -# run a command on the device; exit with the exit status of the device -# command. -run_command() { - $ADB shell "$@" \; echo \$? | awk '{if (b) {print a}; a=$0; b=1} END {exit a}' -} - -testname() { - echo - echo "::: testing $1 :::" - testname="$1" -} - -fail() { - echo - echo FAIL: $testname - echo - [ "$open_pid" == "" ] || kill $open_pid - [ "$pid_emulator" == "" ] || kill $pid_emulator - exit 1 -} - - -cleanup() { - # not necessary if we're about to kill the emulator, but nice for - # running on real devices or already-running emulators. - run_command rm $WORK_DIR/verifier_test - run_command rm $WORK_DIR/package.zip - - [ "$pid_emulator" == "" ] || kill $pid_emulator -} - -$ADB push $ANDROID_PRODUCT_OUT/system/bin/verifier_test \ - $WORK_DIR/verifier_test - -expect_succeed() { - testname "$1 (should succeed)" - $ADB push $DATA_DIR/$1 $WORK_DIR/package.zip - shift - run_command $WORK_DIR/verifier_test "$@" $WORK_DIR/package.zip || fail -} - -expect_fail() { - testname "$1 (should fail)" - $ADB push $DATA_DIR/$1 $WORK_DIR/package.zip - shift - run_command $WORK_DIR/verifier_test "$@" $WORK_DIR/package.zip && fail -} - -# not signed at all -expect_fail unsigned.zip -# signed in the pre-donut way -expect_fail jarsigned.zip - -# success cases -expect_succeed otasigned.zip -e3 -expect_succeed otasigned_f4.zip -f4 -expect_succeed otasigned_sha256.zip -e3 -sha256 -expect_succeed otasigned_f4_sha256.zip -f4 -sha256 -expect_succeed otasigned_ecdsa_sha256.zip -ec -sha256 - -# success with multiple keys -expect_succeed otasigned.zip -f4 -e3 -expect_succeed otasigned_f4.zip -ec -f4 -expect_succeed otasigned_sha256.zip -ec -e3 -e3 -sha256 -expect_succeed otasigned_f4_sha256.zip -ec -sha256 -e3 -f4 -sha256 -expect_succeed otasigned_ecdsa_sha256.zip -f4 -sha256 -e3 -ec -sha256 - -# verified against different key -expect_fail otasigned.zip -f4 -expect_fail otasigned_f4.zip -e3 -expect_fail otasigned_ecdsa_sha256.zip -e3 -sha256 - -# verified against right key but wrong hash algorithm -expect_fail otasigned.zip -e3 -sha256 -expect_fail otasigned_f4.zip -f4 -sha256 -expect_fail otasigned_sha256.zip -expect_fail otasigned_f4_sha256.zip -f4 -expect_fail otasigned_ecdsa_sha256.zip - -# various other cases -expect_fail random.zip -expect_fail fake-eocd.zip -expect_fail alter-metadata.zip -expect_fail alter-footer.zip - -# --------------- cleanup ---------------------- - -cleanup - -echo -echo PASS -echo |