diff --git a/applypatch/Android.mk b/applypatch/Android.mk index 8be5c36b..a7412d23 100644 --- a/applypatch/Android.mk +++ b/applypatch/Android.mk @@ -21,8 +21,7 @@ LOCAL_SRC_FILES := \ applypatch.cpp \ bspatch.cpp \ freecache.cpp \ - imgpatch.cpp \ - utils.cpp + imgpatch.cpp LOCAL_MODULE := libapplypatch LOCAL_MODULE_TAGS := eng LOCAL_C_INCLUDES := \ @@ -46,8 +45,7 @@ include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ bspatch.cpp \ - imgpatch.cpp \ - utils.cpp + imgpatch.cpp LOCAL_MODULE := libimgpatch LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/include \ @@ -56,6 +54,7 @@ LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include LOCAL_STATIC_LIBRARIES := \ libcrypto \ libbspatch \ + libbase \ libbz \ libz LOCAL_CFLAGS := \ @@ -68,8 +67,7 @@ include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ bspatch.cpp \ - imgpatch.cpp \ - utils.cpp + imgpatch.cpp LOCAL_MODULE := libimgpatch LOCAL_MODULE_HOST_OS := linux LOCAL_C_INCLUDES := \ @@ -79,6 +77,7 @@ LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include LOCAL_STATIC_LIBRARIES := \ libcrypto \ libbspatch \ + libbase \ libbz \ libz LOCAL_CFLAGS := \ @@ -123,9 +122,7 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_CFLAGS := -Werror include $(BUILD_EXECUTABLE) -libimgdiff_src_files := \ - imgdiff.cpp \ - utils.cpp +libimgdiff_src_files := imgdiff.cpp # libbsdiff is compiled with -D_FILE_OFFSET_BITS=64. libimgdiff_cflags := \ diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp index fba74e83..41d73ab9 100644 --- a/applypatch/imgdiff.cpp +++ b/applypatch/imgdiff.cpp @@ -145,12 +145,22 @@ #include #include -#include "utils.h" - using android::base::get_unaligned; static constexpr auto BUFFER_SIZE = 0x8000; +// If we use this function to write the offset and length (type size_t), their values should not +// exceed 2^63; because the signed bit will be casted away. +static inline bool Write8(int fd, int64_t value) { + return android::base::WriteFully(fd, &value, sizeof(int64_t)); +} + +// Similarly, the value should not exceed 2^31 if we are casting from size_t (e.g. target chunk +// size). +static inline bool Write4(int fd, int32_t value) { + return android::base::WriteFully(fd, &value, sizeof(int32_t)); +} + class ImageChunk { public: static constexpr auto WINDOWBITS = -15; // 32kb window; negative to indicate a raw stream. @@ -163,11 +173,12 @@ class ImageChunk { start_(start), input_file_ptr_(file_content), raw_data_len_(raw_data_len), - entry_name_(""), compress_level_(6), source_start_(0), source_len_(0), - source_uncompressed_len_(0) {} + source_uncompressed_len_(0) { + CHECK(file_content != nullptr) << "input file container can't be nullptr"; + } int GetType() const { return type_; @@ -199,7 +210,8 @@ class ImageChunk { } size_t GetHeaderSize(size_t patch_size) const; - size_t WriteHeaderToFile(FILE* f, const std::vector patch, size_t offset); + // Return the offset of the next patch into the patch data. + size_t WriteHeaderToFd(int fd, const std::vector& patch, size_t offset); /* * Cause a gzip chunk to be treated as a normal chunk (ie, as a blob @@ -222,9 +234,9 @@ class ImageChunk { void MergeAdjacentNormal(const ImageChunk& other); private: - int type_; // CHUNK_NORMAL, CHUNK_DEFLATE, CHUNK_RAW - size_t start_; // offset of chunk in the original input file - const std::vector* input_file_ptr_; // pointer to the full content of original input file + int type_; // CHUNK_NORMAL, CHUNK_DEFLATE, CHUNK_RAW + size_t start_; // offset of chunk in the original input file + const std::vector* input_file_ptr_; // ptr to the full content of original input file size_t raw_data_len_; // --- for CHUNK_DEFLATE chunks only: --- @@ -280,11 +292,11 @@ void ImageChunk::SetSourceInfo(const ImageChunk& src) { } void ImageChunk::SetEntryName(std::string entryname) { - entry_name_ = entryname; + entry_name_ = std::move(entryname); } void ImageChunk::SetUncompressedData(std::vector data) { - uncompressed_data_ = data; + uncompressed_data_ = std::move(data); } bool ImageChunk::SetBonusData(const std::vector& bonus_data) { @@ -295,7 +307,7 @@ bool ImageChunk::SetBonusData(const std::vector& bonus_data) { return true; } -// Convert CHUNK_NORMAL & CHUNK_DEFLATE to CHUNK_RAW if the terget size is +// Convert CHUNK_NORMAL & CHUNK_DEFLATE to CHUNK_RAW if the target size is // smaller. Also take the header size into account during size comparison. bool ImageChunk::ChangeChunkToRaw(size_t patch_size) { if (type_ == CHUNK_RAW) { @@ -310,6 +322,7 @@ bool ImageChunk::ChangeChunkToRaw(size_t patch_size) { void ImageChunk::ChangeDeflateChunkToNormal() { if (type_ != CHUNK_DEFLATE) return; type_ = CHUNK_NORMAL; + entry_name_.clear(); uncompressed_data_.clear(); } @@ -317,7 +330,7 @@ void ImageChunk::ChangeDeflateChunkToNormal() { // header_type 4 bytes // CHUNK_NORMAL 8*3 = 24 bytes // CHUNK_DEFLATE 8*5 + 4*5 = 60 bytes -// CHUNK_RAW 4 bytes +// CHUNK_RAW 4 bytes + patch_size size_t ImageChunk::GetHeaderSize(size_t patch_size) const { switch (type_) { case CHUNK_NORMAL: @@ -327,43 +340,43 @@ size_t ImageChunk::GetHeaderSize(size_t patch_size) const { case CHUNK_RAW: return 4 + 4 + patch_size; default: - printf("unexpected chunk type: %d\n", type_); // should not reach here. - CHECK(false); + CHECK(false) << "unexpected chunk type: " << type_; // Should not reach here. return 0; } } -size_t ImageChunk::WriteHeaderToFile(FILE* f, const std::vector patch, size_t offset) { - Write4(type_, f); +size_t ImageChunk::WriteHeaderToFd(int fd, const std::vector& patch, size_t offset) { + Write4(fd, type_); switch (type_) { case CHUNK_NORMAL: printf("normal (%10zu, %10zu) %10zu\n", start_, raw_data_len_, patch.size()); - Write8(source_start_, f); - Write8(source_len_, f); - Write8(offset, f); + Write8(fd, static_cast(source_start_)); + Write8(fd, static_cast(source_len_)); + Write8(fd, static_cast(offset)); return offset + patch.size(); case CHUNK_DEFLATE: printf("deflate (%10zu, %10zu) %10zu %s\n", start_, raw_data_len_, patch.size(), entry_name_.c_str()); - Write8(source_start_, f); - Write8(source_len_, f); - Write8(offset, f); - Write8(source_uncompressed_len_, f); - Write8(uncompressed_data_.size(), f); - Write4(compress_level_, f); - Write4(METHOD, f); - Write4(WINDOWBITS, f); - Write4(MEMLEVEL, f); - Write4(STRATEGY, f); + Write8(fd, static_cast(source_start_)); + Write8(fd, static_cast(source_len_)); + Write8(fd, static_cast(offset)); + Write8(fd, static_cast(source_uncompressed_len_)); + Write8(fd, static_cast(uncompressed_data_.size())); + Write4(fd, compress_level_); + Write4(fd, METHOD); + Write4(fd, WINDOWBITS); + Write4(fd, MEMLEVEL); + Write4(fd, STRATEGY); return offset + patch.size(); case CHUNK_RAW: printf("raw (%10zu, %10zu)\n", start_, raw_data_len_); - Write4(patch.size(), f); - fwrite(patch.data(), 1, patch.size(), f); + Write4(fd, static_cast(patch.size())); + if (!android::base::WriteFully(fd, patch.data(), patch.size())) { + CHECK(false) << "failed to write " << patch.size() <<" bytes patch"; + } return offset; default: - printf("unexpected chunk type: %d\n", type_); - CHECK(false); + CHECK(false) << "unexpected chunk type: " << type_; return offset; } } @@ -480,20 +493,21 @@ static bool GetZipFileSize(const std::vector& zip_file, size_t* input_f static bool ReadZip(const char* filename, std::vector* chunks, std::vector* zip_file, bool include_pseudo_chunk) { - CHECK(zip_file != nullptr); + CHECK(chunks != nullptr && zip_file != nullptr); + + android::base::unique_fd fd(open(filename, O_RDONLY)); + if (fd == -1) { + printf("failed to open \"%s\" %s\n", filename, strerror(errno)); + return false; + } struct stat st; - if (stat(filename, &st) != 0) { + if (fstat(fd, &st) != 0) { printf("failed to stat \"%s\": %s\n", filename, strerror(errno)); return false; } size_t sz = static_cast(st.st_size); zip_file->resize(sz); - android::base::unique_fd fd(open(filename, O_RDONLY)); - if (fd == -1) { - printf("failed to open \"%s\" %s\n", filename, strerror(errno)); - return false; - } if (!android::base::ReadFully(fd, zip_file->data(), sz)) { printf("failed to read \"%s\" %s\n", filename, strerror(errno)); return false; @@ -596,20 +610,21 @@ static bool ReadZip(const char* filename, std::vector* chunks, // Read the given file and break it up into chunks, and putting the data in to a vector. static bool ReadImage(const char* filename, std::vector* chunks, std::vector* img) { - CHECK(img != nullptr); + CHECK(chunks != nullptr && img != nullptr); + + android::base::unique_fd fd(open(filename, O_RDONLY)); + if (fd == -1) { + printf("failed to open \"%s\" %s\n", filename, strerror(errno)); + return false; + } struct stat st; - if (stat(filename, &st) != 0) { + if (fstat(fd, &st) != 0) { printf("failed to stat \"%s\": %s\n", filename, strerror(errno)); return false; } size_t sz = static_cast(st.st_size); img->resize(sz); - android::base::unique_fd fd(open(filename, O_RDONLY)); - if (fd == -1) { - printf("failed to open \"%s\" %s\n", filename, strerror(errno)); - return false; - } if (!android::base::ReadFully(fd, img->data(), sz)) { printf("failed to read \"%s\" %s\n", filename, strerror(errno)); return false; @@ -618,9 +633,8 @@ static bool ReadImage(const char* filename, std::vector* chunks, size_t pos = 0; while (pos < sz) { - if (sz - pos >= 4 && img->at(pos) == 0x1f && img->at(pos + 1) == 0x8b && - img->at(pos + 2) == 0x08 && // deflate compression - img->at(pos + 3) == 0x00) { // no header flags + // 0x00 no header flags, 0x08 deflate compression, 0x1f8b gzip magic number + if (sz - pos >= 4 && get_unaligned(img->data() + pos) == 0x00088b1f) { // 'pos' is the offset of the start of a gzip chunk. size_t chunk_offset = pos; @@ -695,7 +709,7 @@ static bool ReadImage(const char* filename, std::vector* chunks, // the uncompressed data. Double-check to make sure that it // matches the size of the data we got when we actually did // the decompression. - size_t footer_size = Read4(img->data() + pos - 4); + size_t footer_size = get_unaligned(img->data() + pos - 4); if (footer_size != body.DataLengthForPatch()) { printf("Error: footer size %zu != decompressed size %zu\n", footer_size, body.GetRawDataLength()); @@ -708,9 +722,8 @@ static bool ReadImage(const char* filename, std::vector* chunks, // Scan forward until we find a gzip header. size_t data_len = 0; while (data_len + pos < sz) { - if (data_len + pos + 4 <= sz && img->at(pos + data_len) == 0x1f && - img->at(pos + data_len + 1) == 0x8b && img->at(pos + data_len + 2) == 0x08 && - img->at(pos + data_len + 3) == 0x00) { + if (data_len + pos + 4 <= sz && + get_unaligned(img->data() + pos + data_len) == 0x00088b1f) { break; } data_len++; @@ -759,13 +772,19 @@ static bool MakePatch(const ImageChunk* src, ImageChunk* tgt, std::vector(st.st_size); + // Change the chunk type to raw if the patch takes less space that way. if (tgt->ChangeChunkToRaw(sz)) { unlink(ptemp); size_t patch_size = tgt->DataLengthForPatch(); @@ -773,12 +792,6 @@ static bool MakePatch(const ImageChunk* src, ImageChunk* tgt, std::vectorDataForPatch(), tgt->DataForPatch() + patch_size, patch_data->begin()); return true; } - - android::base::unique_fd patch_fd(open(ptemp, O_RDONLY)); - if (patch_fd == -1) { - printf("failed to open %s: %s\n", ptemp, strerror(errno)); - return false; - } patch_data->resize(sz); if (!android::base::ReadFully(patch_fd, patch_data->data(), sz)) { printf("failed to read \"%s\" %s\n", ptemp, strerror(errno)); @@ -845,18 +858,19 @@ int imgdiff(int argc, const char** argv) { std::vector bonus_data; if (argc >= 3 && strcmp(argv[1], "-b") == 0) { - struct stat st; - if (stat(argv[2], &st) != 0) { - printf("failed to stat bonus file %s: %s\n", argv[2], strerror(errno)); - return 1; - } - size_t bonus_size = st.st_size; - bonus_data.resize(bonus_size); android::base::unique_fd fd(open(argv[2], O_RDONLY)); if (fd == -1) { printf("failed to open bonus file %s: %s\n", argv[2], strerror(errno)); return 1; } + struct stat st; + if (fstat(fd, &st) != 0) { + printf("failed to stat bonus file %s: %s\n", argv[2], strerror(errno)); + return 1; + } + + size_t bonus_size = st.st_size; + bonus_data.resize(bonus_size); if (!android::base::ReadFully(fd, bonus_data.data(), bonus_size)) { printf("failed to read bonus file %s: %s\n", argv[2], strerror(errno)); return 1; @@ -999,9 +1013,15 @@ int imgdiff(int argc, const char** argv) { ImageChunk* src; if (tgt_chunks[i].GetType() == CHUNK_DEFLATE && (src = FindChunkByName(tgt_chunks[i].GetEntryName(), src_chunks))) { - MakePatch(src, &tgt_chunks[i], &patch_data[i], nullptr); + if (!MakePatch(src, &tgt_chunks[i], &patch_data[i], nullptr)) { + printf("Failed to generate patch for target chunk %zu: ", i); + return 1; + } } else { - MakePatch(&src_chunks[0], &tgt_chunks[i], &patch_data[i], &bsdiff_cache); + if (!MakePatch(&src_chunks[0], &tgt_chunks[i], &patch_data[i], &bsdiff_cache)) { + printf("Failed to generate patch for target chunk %zu: ", i); + return 1; + } } } else { if (i == 1 && !bonus_data.empty()) { @@ -1009,7 +1029,10 @@ int imgdiff(int argc, const char** argv) { src_chunks[i].SetBonusData(bonus_data); } - MakePatch(&src_chunks[i], &tgt_chunks[i], &patch_data[i], nullptr); + if (!MakePatch(&src_chunks[i], &tgt_chunks[i], &patch_data[i], nullptr)) { + printf("Failed to generate patch for target chunk %zu: ", i); + return 1; + } } printf("patch %3zu is %zu bytes (of %zu)\n", i, patch_data[i].size(), src_chunks[i].GetRawDataLength()); @@ -1030,28 +1053,32 @@ int imgdiff(int argc, const char** argv) { size_t offset = total_header_size; - FILE* f = fopen(argv[3], "wb"); - if (f == nullptr) { + android::base::unique_fd patch_fd(open(argv[3], O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR)); + if (patch_fd == -1) { printf("failed to open \"%s\": %s\n", argv[3], strerror(errno)); + return 1; } // Write out the headers. - - fwrite("IMGDIFF2", 1, 8, f); - Write4(static_cast(tgt_chunks.size()), f); + if (!android::base::WriteStringToFd("IMGDIFF2", patch_fd)) { + printf("failed to write \"IMGDIFF2\" to \"%s\": %s\n", argv[3], strerror(errno)); + return 1; + } + Write4(patch_fd, static_cast(tgt_chunks.size())); for (size_t i = 0; i < tgt_chunks.size(); ++i) { printf("chunk %zu: ", i); - offset = tgt_chunks[i].WriteHeaderToFile(f, patch_data[i], offset); + offset = tgt_chunks[i].WriteHeaderToFd(patch_fd, patch_data[i], offset); } // Append each chunk's bsdiff patch, in order. for (size_t i = 0; i < tgt_chunks.size(); ++i) { if (tgt_chunks[i].GetType() != CHUNK_RAW) { - fwrite(patch_data[i].data(), 1, patch_data[i].size(), f); + if (!android::base::WriteFully(patch_fd, patch_data[i].data(), patch_data[i].size())) { + CHECK(false) << "failed to write " << patch_data[i].size() << " bytes patch for chunk " + << i; + } } } - fclose(f); - return 0; } diff --git a/applypatch/imgpatch.cpp b/applypatch/imgpatch.cpp index 8f4a2a42..adcc61fd 100644 --- a/applypatch/imgpatch.cpp +++ b/applypatch/imgpatch.cpp @@ -31,10 +31,17 @@ #include #include +#include #include #include -#include "utils.h" +static inline int64_t Read8(const void *address) { + return android::base::get_unaligned(address); +} + +static inline int32_t Read4(const void *address) { + return android::base::get_unaligned(address); +} int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, const unsigned char* patch_data, ssize_t patch_size, @@ -86,9 +93,9 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, const Value return -1; } - size_t src_start = Read8(normal_header); - size_t src_len = Read8(normal_header + 8); - size_t patch_offset = Read8(normal_header + 16); + size_t src_start = static_cast(Read8(normal_header)); + size_t src_len = static_cast(Read8(normal_header + 8)); + size_t patch_offset = static_cast(Read8(normal_header + 16)); if (src_start + src_len > static_cast(old_size)) { printf("source data too short\n"); @@ -125,11 +132,11 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, const Value return -1; } - size_t src_start = Read8(deflate_header); - size_t src_len = Read8(deflate_header + 8); - size_t patch_offset = Read8(deflate_header + 16); - size_t expanded_len = Read8(deflate_header + 24); - size_t target_len = Read8(deflate_header + 32); + size_t src_start = static_cast(Read8(deflate_header)); + size_t src_len = static_cast(Read8(deflate_header + 8)); + size_t patch_offset = static_cast(Read8(deflate_header + 16)); + size_t expanded_len = static_cast(Read8(deflate_header + 24)); + size_t target_len = static_cast(Read8(deflate_header + 32)); int level = Read4(deflate_header + 40); int method = Read4(deflate_header + 44); int windowBits = Read4(deflate_header + 48); diff --git a/applypatch/utils.cpp b/applypatch/utils.cpp deleted file mode 100644 index 450dc8d7..00000000 --- a/applypatch/utils.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2009 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 - -#include "utils.h" - -/** Write a 4-byte value to f in little-endian order. */ -void Write4(int value, FILE* f) { - fputc(value & 0xff, f); - fputc((value >> 8) & 0xff, f); - fputc((value >> 16) & 0xff, f); - fputc((value >> 24) & 0xff, f); -} - -/** Write an 8-byte value to f in little-endian order. */ -void Write8(int64_t value, FILE* f) { - fputc(value & 0xff, f); - fputc((value >> 8) & 0xff, f); - fputc((value >> 16) & 0xff, f); - fputc((value >> 24) & 0xff, f); - fputc((value >> 32) & 0xff, f); - fputc((value >> 40) & 0xff, f); - fputc((value >> 48) & 0xff, f); - fputc((value >> 56) & 0xff, f); -} - -int Read2(const void* pv) { - const unsigned char* p = reinterpret_cast(pv); - return (int)(((unsigned int)p[1] << 8) | - (unsigned int)p[0]); -} - -int Read4(const void* pv) { - const unsigned char* p = reinterpret_cast(pv); - return (int)(((unsigned int)p[3] << 24) | - ((unsigned int)p[2] << 16) | - ((unsigned int)p[1] << 8) | - (unsigned int)p[0]); -} - -int64_t Read8(const void* pv) { - const unsigned char* p = reinterpret_cast(pv); - return (int64_t)(((uint64_t)p[7] << 56) | - ((uint64_t)p[6] << 48) | - ((uint64_t)p[5] << 40) | - ((uint64_t)p[4] << 32) | - ((uint64_t)p[3] << 24) | - ((uint64_t)p[2] << 16) | - ((uint64_t)p[1] << 8) | - (uint64_t)p[0]); -} diff --git a/applypatch/utils.h b/applypatch/utils.h deleted file mode 100644 index c7c8e90e..00000000 --- a/applypatch/utils.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2009 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 _BUILD_TOOLS_APPLYPATCH_UTILS_H -#define _BUILD_TOOLS_APPLYPATCH_UTILS_H - -#include -#include - -// Read and write little-endian values of various sizes. - -void Write4(int value, FILE* f); -void Write8(int64_t value, FILE* f); -int Read2(const void* p); -int Read4(const void* p); -int64_t Read8(const void* p); - -#endif // _BUILD_TOOLS_APPLYPATCH_UTILS_H diff --git a/tests/component/imgdiff_test.cpp b/tests/component/imgdiff_test.cpp index be2dd385..2f648501 100644 --- a/tests/component/imgdiff_test.cpp +++ b/tests/component/imgdiff_test.cpp @@ -18,13 +18,14 @@ #include #include +#include #include #include #include #include #include -#include "applypatch/utils.h" +using android::base::get_unaligned; static ssize_t MemorySink(const unsigned char* data, ssize_t len, void* token) { std::string* s = static_cast(token); @@ -41,7 +42,7 @@ static void verify_patch_header(const std::string& patch, size_t* num_normal, si ASSERT_GE(size, 12U); ASSERT_EQ("IMGDIFF2", std::string(data, 8)); - const int num_chunks = Read4(data + 8); + const int num_chunks = get_unaligned(data + 8); ASSERT_GE(num_chunks, 0); size_t normal = 0; @@ -51,7 +52,7 @@ static void verify_patch_header(const std::string& patch, size_t* num_normal, si size_t pos = 12; for (int i = 0; i < num_chunks; ++i) { ASSERT_LE(pos + 4, size); - int type = Read4(data + pos); + int type = get_unaligned(data + pos); pos += 4; if (type == CHUNK_NORMAL) { pos += 24; @@ -59,7 +60,7 @@ static void verify_patch_header(const std::string& patch, size_t* num_normal, si normal++; } else if (type == CHUNK_RAW) { ASSERT_LE(pos + 4, size); - ssize_t data_len = Read4(data + pos); + ssize_t data_len = get_unaligned(data + pos); ASSERT_GT(data_len, 0); pos += 4 + data_len; ASSERT_LE(pos, size);