diff options
author | Sami Tolvanen <samitolvanen@google.com> | 2023-04-27 15:17:51 -0700 |
---|---|---|
committer | Sami Tolvanen <samitolvanen@google.com> | 2023-04-28 11:02:53 -0700 |
commit | 2f6bcd0a072a740154ac6c11a9b0242049e2bf63 (patch) | |
tree | be8e59f286aa0b7859597def01cc45e7efc09b75 /libfec | |
parent | fb666d177612325778ba3c76e9bf508ab901cd65 (diff) | |
download | extras-2f6bcd0a072a740154ac6c11a9b0242049e2bf63.tar.gz |
libfec: Fix unaligned multiblock reads
fec_process miscalculates the number of blocks it needs to read when
we perform a small read that spans multiple blocks, which results in
a partial read. The function also returns early when it notices the
partial read without cleaning up the worker threads, which can lead to
the caller releasing the buffer that the threads are still using. Fix
the calculation and add a test case to fec_unittest.
Bug: 279705058
Test: fec_unittest
Change-Id: I0d36950d3324572043b345997a21bb3e985968e5
Diffstat (limited to 'libfec')
-rw-r--r-- | libfec/fec_process.cpp | 23 | ||||
-rw-r--r-- | libfec/test/fec_unittest.cpp | 10 |
2 files changed, 19 insertions, 14 deletions
diff --git a/libfec/fec_process.cpp b/libfec/fec_process.cpp index f11b8b2c..46f48bc7 100644 --- a/libfec/fec_process.cpp +++ b/libfec/fec_process.cpp @@ -60,30 +60,27 @@ ssize_t process(fec_handle *f, uint8_t *buf, size_t count, uint64_t offset, } uint64_t start = (offset / FEC_BLOCKSIZE) * FEC_BLOCKSIZE; - size_t blocks = fec_div_round_up(count, FEC_BLOCKSIZE); + size_t blocks = fec_div_round_up(offset + count - start, FEC_BLOCKSIZE); - size_t count_per_thread = fec_div_round_up(blocks, threads) * FEC_BLOCKSIZE; - size_t max_threads = fec_div_round_up(count, count_per_thread); - - if ((size_t)threads > max_threads) { - threads = (int)max_threads; + /* start at most one thread per block we're accessing */ + if ((size_t)threads > blocks) { + threads = (int)blocks; } + size_t count_per_thread = fec_div_round_up(blocks, threads) * FEC_BLOCKSIZE; size_t left = count; uint64_t pos = offset; uint64_t end = start + count_per_thread; - debug("%d threads, %zu bytes per thread (total %zu)", threads, - count_per_thread, count); + debug("max %d threads, %zu bytes per thread (total %zu spanning %zu blocks)", threads, + count_per_thread, count, blocks); std::vector<pthread_t> handles; process_info info[threads]; ssize_t rc = 0; /* start threads to process queue */ - for (int i = 0; i < threads; ++i) { - check(left > 0); - + for (int i = 0; i < threads && left > 0; ++i) { info[i].id = i; info[i].f = f; info[i].buf = &buf[pos - offset]; @@ -111,8 +108,6 @@ ssize_t process(fec_handle *f, uint8_t *buf, size_t count, uint64_t offset, left -= info[i].count; } - check(left == 0); - ssize_t nread = 0; /* wait for all threads to complete */ @@ -130,7 +125,7 @@ ssize_t process(fec_handle *f, uint8_t *buf, size_t count, uint64_t offset, } } - if (rc == -1) { + if (left > 0 || rc == -1) { errno = EIO; return -1; } diff --git a/libfec/test/fec_unittest.cpp b/libfec/test/fec_unittest.cpp index 421eb501..1ced2d98 100644 --- a/libfec/test/fec_unittest.cpp +++ b/libfec/test/fec_unittest.cpp @@ -215,6 +215,16 @@ TEST_F(FecUnitTest, VerityImage_FecRead) { ASSERT_EQ(1024, fec_pread(handle, read_data.data(), 1024, corrupt_offset)); ASSERT_EQ(std::vector<uint8_t>(1024, 255), read_data); + + // Unaligned read that spans two blocks + ASSERT_EQ(678, fec_pread(handle, read_data.data(), 678, corrupt_offset - 123)); + ASSERT_EQ(std::vector<uint8_t>(123, 254), + std::vector<uint8_t>(read_data.begin(), read_data.begin() + 123)); + ASSERT_EQ(std::vector<uint8_t>(555, 255), + std::vector<uint8_t>(read_data.begin() + 123, read_data.begin() + 678)); + + std::vector<uint8_t> large_data(53388, 0); + ASSERT_EQ(53388, fec_pread(handle, large_data.data(), 53388, 385132)); } TEST_F(FecUnitTest, LoadAvbImage_HashtreeFooter) { |