Loading fastboot/device/flashing.cpp +4 −2 Original line number Diff line number Diff line Loading @@ -119,9 +119,11 @@ int WriteCallback(void* priv, const void* data, size_t len) { } int FlashSparseData(int fd, std::vector<char>& downloaded_data) { struct sparse_file* file = sparse_file_import_buf(downloaded_data.data(), true, false); struct sparse_file* file = sparse_file_import_buf(downloaded_data.data(), downloaded_data.size(), true, false); if (!file) { return -ENOENT; // Invalid sparse format return -EINVAL; } return sparse_file_callback(file, false, false, WriteCallback, reinterpret_cast<void*>(fd)); } Loading fastboot/fuzzy_fastboot/main.cpp +57 −0 Original line number Diff line number Diff line Loading @@ -977,6 +977,63 @@ TEST_F(Fuzz, SparseZeroLength) { } } TEST_F(Fuzz, SparseZeroBlkSize) { // handcrafted malform sparse file with zero as block size const std::vector<char> buf = { '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c', '\x00', '\x00', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xc2', '\xca', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x10', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44' }; ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command"; ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed"; // It can either reject this download or reject it during flash if (HandleResponse() != DEVICE_FAIL) { EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL) << "Flashing a zero block size in sparse file should fail"; } } TEST_F(Fuzz, SparseTrimmed) { // handcrafted malform sparse file which is trimmed const std::vector<char> buf = { '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c', '\x00', '\x00', '\x10', '\x00', '\x00', '\x00', '\x00', '\x08', '\x00', '\x01', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xc1', '\xca', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x80', '\x11', '\x22', '\x33', '\x44' }; ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command"; ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed"; // It can either reject this download or reject it during flash if (HandleResponse() != DEVICE_FAIL) { EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL) << "Flashing a trimmed sparse file should fail"; } } TEST_F(Fuzz, SparseInvalidChurk) { // handcrafted malform sparse file with invalid churk const std::vector<char> buf = { '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c', '\x00', '\x00', '\x10', '\x00', '\x00', '\x00', '\x00', '\x08', '\x00', '\x01', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xc1', '\xca', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x10', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44' }; ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command"; ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed"; // It can either reject this download or reject it during flash if (HandleResponse() != DEVICE_FAIL) { EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL) << "Flashing a sparse file with invalid churk should fail"; } } TEST_F(Fuzz, SparseTooManyChunks) { SparseWrapper sparse(4096, 4096); // 1 block, but we send two chunks that will use 2 blocks ASSERT_TRUE(*sparse) << "Sparse image creation failed"; Loading libsparse/Android.bp +3 −1 Original line number Diff line number Diff line Loading @@ -96,12 +96,14 @@ python_binary_host { cc_fuzz { name: "sparse_fuzzer", host_supported: false, host_supported: true, srcs: [ "sparse_fuzzer.cpp", ], static_libs: [ "libsparse", "libbase", "libz", "liblog", ], } libsparse/include/sparse/sparse.h +2 −16 Original line number Diff line number Diff line Loading @@ -243,21 +243,6 @@ int sparse_file_foreach_chunk(struct sparse_file *s, bool sparse, bool crc, */ int sparse_file_read(struct sparse_file *s, int fd, bool sparse, bool crc); /** * sparse_file_read_buf - read a buffer into a sparse file cookie * * @s - sparse file cookie * @buf - buffer to read from * @crc - verify the crc of a file in the Android sparse file format * * Reads a buffer into a sparse file cookie. The buffer must remain * valid until the sparse file cookie is freed. If crc is true, the * crc of the sparse file will be verified. * * Returns 0 on success, negative errno on error. */ int sparse_file_read_buf(struct sparse_file *s, char *buf, bool crc); /** * sparse_file_import - import an existing sparse file * Loading @@ -277,6 +262,7 @@ struct sparse_file *sparse_file_import(int fd, bool verbose, bool crc); * sparse_file_import_buf - import an existing sparse file from a buffer * * @buf - buffer to read from * @len - length of buffer * @verbose - print verbose errors while reading the sparse file * @crc - verify the crc of a file in the Android sparse file format * Loading @@ -286,7 +272,7 @@ struct sparse_file *sparse_file_import(int fd, bool verbose, bool crc); * * Returns a new sparse file cookie on success, NULL on error. */ struct sparse_file *sparse_file_import_buf(char* buf, bool verbose, bool crc); struct sparse_file* sparse_file_import_buf(char* buf, size_t len, bool verbose, bool crc); /** * sparse_file_import_auto - import an existing sparse or normal file Loading libsparse/output_file.cpp +27 −7 Original line number Diff line number Diff line Loading @@ -54,6 +54,8 @@ #define SPARSE_HEADER_LEN (sizeof(sparse_header_t)) #define CHUNK_HEADER_LEN (sizeof(chunk_header_t)) #define FILL_ZERO_BUFSIZE (2 * 1024 * 1024) #define container_of(inner, outer_t, elem) ((outer_t*)((char*)(inner)-offsetof(outer_t, elem))) struct output_file_ops { Loading Loading @@ -391,13 +393,29 @@ static int write_sparse_data_chunk(struct output_file* out, uint64_t len, void* ret = out->ops->write(out, data, len); if (ret < 0) return -1; if (zero_len) { ret = out->ops->write(out, out->zero_buf, zero_len); if (ret < 0) return -1; uint64_t len = zero_len; uint64_t write_len; while (len) { write_len = std::min(len, (uint64_t)FILL_ZERO_BUFSIZE); ret = out->ops->write(out, out->zero_buf, write_len); if (ret < 0) { return ret; } len -= write_len; } } if (out->use_crc) { out->crc32 = sparse_crc32(out->crc32, data, len); if (zero_len) out->crc32 = sparse_crc32(out->crc32, out->zero_buf, zero_len); if (zero_len) { uint64_t len = zero_len; uint64_t write_len; while (len) { write_len = std::min(len, (uint64_t)FILL_ZERO_BUFSIZE); out->crc32 = sparse_crc32(out->crc32, out->zero_buf, write_len); len -= write_len; } } } out->cur_out_ptr += rnd_up_len; Loading Loading @@ -460,12 +478,12 @@ static int write_normal_fill_chunk(struct output_file* out, uint64_t len, uint32 uint64_t write_len; /* Initialize fill_buf with the fill_val */ for (i = 0; i < out->block_size / sizeof(uint32_t); i++) { for (i = 0; i < FILL_ZERO_BUFSIZE / sizeof(uint32_t); i++) { out->fill_buf[i] = fill_val; } while (len) { write_len = std::min(len, (uint64_t)out->block_size); write_len = std::min(len, (uint64_t)FILL_ZERO_BUFSIZE); ret = out->ops->write(out, out->fill_buf, write_len); if (ret < 0) { return ret; Loading Loading @@ -512,13 +530,15 @@ static int output_file_init(struct output_file* out, int block_size, int64_t len out->crc32 = 0; out->use_crc = crc; out->zero_buf = reinterpret_cast<char*>(calloc(block_size, 1)); // don't use sparse format block size as it can takes up to 32GB out->zero_buf = reinterpret_cast<char*>(calloc(FILL_ZERO_BUFSIZE, 1)); if (!out->zero_buf) { error_errno("malloc zero_buf"); return -ENOMEM; } out->fill_buf = reinterpret_cast<uint32_t*>(calloc(block_size, 1)); // don't use sparse format block size as it can takes up to 32GB out->fill_buf = reinterpret_cast<uint32_t*>(calloc(FILL_ZERO_BUFSIZE, 1)); if (!out->fill_buf) { error_errno("malloc fill_buf"); ret = -ENOMEM; Loading Loading
fastboot/device/flashing.cpp +4 −2 Original line number Diff line number Diff line Loading @@ -119,9 +119,11 @@ int WriteCallback(void* priv, const void* data, size_t len) { } int FlashSparseData(int fd, std::vector<char>& downloaded_data) { struct sparse_file* file = sparse_file_import_buf(downloaded_data.data(), true, false); struct sparse_file* file = sparse_file_import_buf(downloaded_data.data(), downloaded_data.size(), true, false); if (!file) { return -ENOENT; // Invalid sparse format return -EINVAL; } return sparse_file_callback(file, false, false, WriteCallback, reinterpret_cast<void*>(fd)); } Loading
fastboot/fuzzy_fastboot/main.cpp +57 −0 Original line number Diff line number Diff line Loading @@ -977,6 +977,63 @@ TEST_F(Fuzz, SparseZeroLength) { } } TEST_F(Fuzz, SparseZeroBlkSize) { // handcrafted malform sparse file with zero as block size const std::vector<char> buf = { '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c', '\x00', '\x00', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xc2', '\xca', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x10', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44' }; ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command"; ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed"; // It can either reject this download or reject it during flash if (HandleResponse() != DEVICE_FAIL) { EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL) << "Flashing a zero block size in sparse file should fail"; } } TEST_F(Fuzz, SparseTrimmed) { // handcrafted malform sparse file which is trimmed const std::vector<char> buf = { '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c', '\x00', '\x00', '\x10', '\x00', '\x00', '\x00', '\x00', '\x08', '\x00', '\x01', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xc1', '\xca', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x80', '\x11', '\x22', '\x33', '\x44' }; ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command"; ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed"; // It can either reject this download or reject it during flash if (HandleResponse() != DEVICE_FAIL) { EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL) << "Flashing a trimmed sparse file should fail"; } } TEST_F(Fuzz, SparseInvalidChurk) { // handcrafted malform sparse file with invalid churk const std::vector<char> buf = { '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c', '\x00', '\x00', '\x10', '\x00', '\x00', '\x00', '\x00', '\x08', '\x00', '\x01', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xc1', '\xca', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x10', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44' }; ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command"; ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed"; // It can either reject this download or reject it during flash if (HandleResponse() != DEVICE_FAIL) { EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL) << "Flashing a sparse file with invalid churk should fail"; } } TEST_F(Fuzz, SparseTooManyChunks) { SparseWrapper sparse(4096, 4096); // 1 block, but we send two chunks that will use 2 blocks ASSERT_TRUE(*sparse) << "Sparse image creation failed"; Loading
libsparse/Android.bp +3 −1 Original line number Diff line number Diff line Loading @@ -96,12 +96,14 @@ python_binary_host { cc_fuzz { name: "sparse_fuzzer", host_supported: false, host_supported: true, srcs: [ "sparse_fuzzer.cpp", ], static_libs: [ "libsparse", "libbase", "libz", "liblog", ], }
libsparse/include/sparse/sparse.h +2 −16 Original line number Diff line number Diff line Loading @@ -243,21 +243,6 @@ int sparse_file_foreach_chunk(struct sparse_file *s, bool sparse, bool crc, */ int sparse_file_read(struct sparse_file *s, int fd, bool sparse, bool crc); /** * sparse_file_read_buf - read a buffer into a sparse file cookie * * @s - sparse file cookie * @buf - buffer to read from * @crc - verify the crc of a file in the Android sparse file format * * Reads a buffer into a sparse file cookie. The buffer must remain * valid until the sparse file cookie is freed. If crc is true, the * crc of the sparse file will be verified. * * Returns 0 on success, negative errno on error. */ int sparse_file_read_buf(struct sparse_file *s, char *buf, bool crc); /** * sparse_file_import - import an existing sparse file * Loading @@ -277,6 +262,7 @@ struct sparse_file *sparse_file_import(int fd, bool verbose, bool crc); * sparse_file_import_buf - import an existing sparse file from a buffer * * @buf - buffer to read from * @len - length of buffer * @verbose - print verbose errors while reading the sparse file * @crc - verify the crc of a file in the Android sparse file format * Loading @@ -286,7 +272,7 @@ struct sparse_file *sparse_file_import(int fd, bool verbose, bool crc); * * Returns a new sparse file cookie on success, NULL on error. */ struct sparse_file *sparse_file_import_buf(char* buf, bool verbose, bool crc); struct sparse_file* sparse_file_import_buf(char* buf, size_t len, bool verbose, bool crc); /** * sparse_file_import_auto - import an existing sparse or normal file Loading
libsparse/output_file.cpp +27 −7 Original line number Diff line number Diff line Loading @@ -54,6 +54,8 @@ #define SPARSE_HEADER_LEN (sizeof(sparse_header_t)) #define CHUNK_HEADER_LEN (sizeof(chunk_header_t)) #define FILL_ZERO_BUFSIZE (2 * 1024 * 1024) #define container_of(inner, outer_t, elem) ((outer_t*)((char*)(inner)-offsetof(outer_t, elem))) struct output_file_ops { Loading Loading @@ -391,13 +393,29 @@ static int write_sparse_data_chunk(struct output_file* out, uint64_t len, void* ret = out->ops->write(out, data, len); if (ret < 0) return -1; if (zero_len) { ret = out->ops->write(out, out->zero_buf, zero_len); if (ret < 0) return -1; uint64_t len = zero_len; uint64_t write_len; while (len) { write_len = std::min(len, (uint64_t)FILL_ZERO_BUFSIZE); ret = out->ops->write(out, out->zero_buf, write_len); if (ret < 0) { return ret; } len -= write_len; } } if (out->use_crc) { out->crc32 = sparse_crc32(out->crc32, data, len); if (zero_len) out->crc32 = sparse_crc32(out->crc32, out->zero_buf, zero_len); if (zero_len) { uint64_t len = zero_len; uint64_t write_len; while (len) { write_len = std::min(len, (uint64_t)FILL_ZERO_BUFSIZE); out->crc32 = sparse_crc32(out->crc32, out->zero_buf, write_len); len -= write_len; } } } out->cur_out_ptr += rnd_up_len; Loading Loading @@ -460,12 +478,12 @@ static int write_normal_fill_chunk(struct output_file* out, uint64_t len, uint32 uint64_t write_len; /* Initialize fill_buf with the fill_val */ for (i = 0; i < out->block_size / sizeof(uint32_t); i++) { for (i = 0; i < FILL_ZERO_BUFSIZE / sizeof(uint32_t); i++) { out->fill_buf[i] = fill_val; } while (len) { write_len = std::min(len, (uint64_t)out->block_size); write_len = std::min(len, (uint64_t)FILL_ZERO_BUFSIZE); ret = out->ops->write(out, out->fill_buf, write_len); if (ret < 0) { return ret; Loading Loading @@ -512,13 +530,15 @@ static int output_file_init(struct output_file* out, int block_size, int64_t len out->crc32 = 0; out->use_crc = crc; out->zero_buf = reinterpret_cast<char*>(calloc(block_size, 1)); // don't use sparse format block size as it can takes up to 32GB out->zero_buf = reinterpret_cast<char*>(calloc(FILL_ZERO_BUFSIZE, 1)); if (!out->zero_buf) { error_errno("malloc zero_buf"); return -ENOMEM; } out->fill_buf = reinterpret_cast<uint32_t*>(calloc(block_size, 1)); // don't use sparse format block size as it can takes up to 32GB out->fill_buf = reinterpret_cast<uint32_t*>(calloc(FILL_ZERO_BUFSIZE, 1)); if (!out->fill_buf) { error_errno("malloc fill_buf"); ret = -ENOMEM; Loading