Add checking for sparse file format
Sparse file can come from an untrusted source.
Need more checking to ensure that it is not a malformed
file and would not cause any OOB read access.
Update fuzz test for decoding also.
Test: adb reboot fastboot
fuzzy_fastboot --gtest_filter=Fuzz.Sparse*
fuzzy_fastboot --gtest_filter=Conformance.Sparse*
sparse_fuzzer
Bug: 212705418
Change-Id: I7622df307bb00e59faaba8bb2c67cb474cffed8e
diff --git a/libsparse/output_file.cpp b/libsparse/output_file.cpp
index b2c5407..cb5d730 100644
--- a/libsparse/output_file.cpp
+++ b/libsparse/output_file.cpp
@@ -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 {
@@ -391,13 +393,29 @@
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;
@@ -460,12 +478,12 @@
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;
@@ -512,13 +530,15 @@
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;