Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 638bca9b authored by Keith Mok's avatar Keith Mok Committed by Automerger Merge Worker
Browse files

Merge "Add checking for sparse file format" am: 4915f041

Original change: https://android-review.googlesource.com/c/platform/system/core/+/1949556

Change-Id: I1d424ce7272ae66a6915b070f4f93107415d60f6
parents 845292f2 4915f041
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -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));
}
+57 −0
Original line number Diff line number Diff line
@@ -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";
+3 −1
Original line number Diff line number Diff line
@@ -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",
    ],
}
+2 −16
Original line number Diff line number Diff line
@@ -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
 *
@@ -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
 *
@@ -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
+27 −7
Original line number Diff line number Diff line
@@ -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 @@ 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;
@@ -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;
@@ -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