Skip to content
This repository has been archived by the owner on Apr 27, 2024. It is now read-only.

Commit

Permalink
libsparse: Fix allocation failures on 32-bit systems.
Browse files Browse the repository at this point in the history
libsparse uses mapped files for length computation checks and writing
output data. The platform-tools package for Windows is 32-bit, and if
an embedded file in the stream is large enough, mapping will fail. In
theory, this failure mode could happen on 64-bit systems as well.

As a workaround, map files in chunks of 256MB instead. This is
implemented by adding a new "fd_chunk" callback to the sparse ops
struct.

Bug: 273933042
Bug: 268872725
Test: fastboot update on Windows
Google: 2495616
Change-Id: Ic40696b34a1d0951787c899db701fc2fa204eb18
Signed-off-by: CxDxVER <[email protected]>
  • Loading branch information
dvandercorp authored and CxDxVER committed Aug 29, 2023
1 parent 487c5fd commit f6b7d39
Showing 1 changed file with 98 additions and 4 deletions.
102 changes: 98 additions & 4 deletions libsparse/output_file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@

#define container_of(inner, outer_t, elem) ((outer_t*)((char*)(inner)-offsetof(outer_t, elem)))

static constexpr size_t kMaxMmapSize = 256 * 1024 * 1024;

struct output_file_ops {
int (*open)(struct output_file*, int fd);
int (*skip)(struct output_file*, int64_t);
Expand All @@ -71,6 +73,7 @@ struct sparse_file_ops {
int (*write_fill_chunk)(struct output_file* out, uint64_t len, uint32_t fill_val);
int (*write_skip_chunk)(struct output_file* out, uint64_t len);
int (*write_end_chunk)(struct output_file* out);
int (*write_fd_chunk)(struct output_file* out, uint64_t len, int fd, int64_t offset);
};

struct output_file {
Expand Down Expand Up @@ -318,6 +321,26 @@ int read_all(int fd, void* buf, size_t len) {
return 0;
}

template <typename T>
static bool write_fd_chunk_range(int fd, int64_t offset, uint64_t len, T callback) {
uint64_t bytes_written = 0;
int64_t current_offset = offset;
while (bytes_written < len) {
size_t mmap_size = std::min(static_cast<uint64_t>(kMaxMmapSize), len - bytes_written);
auto m = android::base::MappedFile::FromFd(fd, current_offset, mmap_size, PROT_READ);
if (!m) {
error("failed to mmap region of length %zu", mmap_size);
return false;
}
if (!callback(m->data(), mmap_size)) {
return false;
}
bytes_written += mmap_size;
current_offset += mmap_size;
}
return true;
}

static int write_sparse_skip_chunk(struct output_file* out, uint64_t skip_len) {
chunk_header_t chunk_header;
int ret;
Expand Down Expand Up @@ -424,6 +447,61 @@ static int write_sparse_data_chunk(struct output_file* out, uint64_t len, void*
return 0;
}

static int write_sparse_fd_chunk(struct output_file* out, uint64_t len, int fd, int64_t offset) {
chunk_header_t chunk_header;
uint64_t rnd_up_len, zero_len;
int ret;

/* Round up the data length to a multiple of the block size */
rnd_up_len = ALIGN(len, out->block_size);
zero_len = rnd_up_len - len;

/* Finally we can safely emit a chunk of data */
chunk_header.chunk_type = CHUNK_TYPE_RAW;
chunk_header.reserved1 = 0;
chunk_header.chunk_sz = rnd_up_len / out->block_size;
chunk_header.total_sz = CHUNK_HEADER_LEN + rnd_up_len;
ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));

if (ret < 0) return -1;
bool ok = write_fd_chunk_range(fd, offset, len, [&ret, out](char* data, size_t size) -> bool {
ret = out->ops->write(out, data, size);
if (ret < 0) return false;
if (out->use_crc) {
out->crc32 = sparse_crc32(out->crc32, data, size);
}
return true;
});
if (!ok) return -1;
if (zero_len) {
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) {
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;
out->chunk_cnt++;

return 0;
}

int write_sparse_end_chunk(struct output_file* out) {
chunk_header_t chunk_header;
int ret;
Expand Down Expand Up @@ -454,6 +532,7 @@ static struct sparse_file_ops sparse_file_ops = {
.write_fill_chunk = write_sparse_fill_chunk,
.write_skip_chunk = write_sparse_skip_chunk,
.write_end_chunk = write_sparse_end_chunk,
.write_fd_chunk = write_sparse_fd_chunk,
};

static int write_normal_data_chunk(struct output_file* out, uint64_t len, void* data) {
Expand Down Expand Up @@ -495,6 +574,23 @@ static int write_normal_fill_chunk(struct output_file* out, uint64_t len, uint32
return 0;
}

static int write_normal_fd_chunk(struct output_file* out, uint64_t len, int fd, int64_t offset) {
int ret;
uint64_t rnd_up_len = ALIGN(len, out->block_size);

bool ok = write_fd_chunk_range(fd, offset, len, [&ret, out](char* data, size_t size) -> bool {
ret = out->ops->write(out, data, size);
return ret >= 0;
});
if (!ok) return ret;

if (rnd_up_len > len) {
ret = out->ops->skip(out, rnd_up_len - len);
}

return ret;
}

static int write_normal_skip_chunk(struct output_file* out, uint64_t len) {
return out->ops->skip(out, len);
}
Expand All @@ -508,6 +604,7 @@ static struct sparse_file_ops normal_file_ops = {
.write_fill_chunk = write_normal_fill_chunk,
.write_skip_chunk = write_normal_skip_chunk,
.write_end_chunk = write_normal_end_chunk,
.write_fd_chunk = write_normal_fd_chunk,
};

void output_file_close(struct output_file* out) {
Expand Down Expand Up @@ -670,10 +767,7 @@ int write_fill_chunk(struct output_file* out, uint64_t len, uint32_t fill_val) {
}

int write_fd_chunk(struct output_file* out, uint64_t len, int fd, int64_t offset) {
auto m = android::base::MappedFile::FromFd(fd, offset, len, PROT_READ);
if (!m) return -errno;

return out->sparse_ops->write_data_chunk(out, m->size(), m->data());
return out->sparse_ops->write_fd_chunk(out, len, fd, offset);
}

/* Write a contiguous region of data blocks from a file */
Expand Down

0 comments on commit f6b7d39

Please sign in to comment.