mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 12:17:44 +00:00
AK: Fix OOB access in DuplexMemoryStream::offset_of()
This fixes an OOB access when the last read/written chunk is empty (as we _just_ started on a new chunk). Also adds a test case to TestMemoryStream. Found via human fuzzing in the shell: ```sh for $(cat /dev/urandom) { clear match $it { ?* as (x) { echo $x sleep 1 } } } ``` would assert at some point.
This commit is contained in:
parent
b1fb8e3741
commit
4c343c5f26
2 changed files with 21 additions and 3 deletions
|
@ -221,22 +221,26 @@ public:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: Does not read across chunk boundaries
|
||||||
|
// Perhaps implement AK::memmem() for iterators?
|
||||||
Optional<size_t> offset_of(ReadonlyBytes value) const
|
Optional<size_t> offset_of(ReadonlyBytes value) const
|
||||||
{
|
{
|
||||||
if (value.size() > size())
|
if (value.size() > size())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
// First, find which chunk we're in.
|
// First, find which chunk we're in.
|
||||||
auto chunk_index = (m_read_offset - m_base_offset) / chunk_size;
|
auto chunk_index = min((m_read_offset - m_base_offset) / chunk_size, m_chunks.size() - 1);
|
||||||
auto last_written_chunk_index = (m_write_offset - m_base_offset) / chunk_size;
|
auto last_written_chunk_index = (m_write_offset - m_base_offset) / chunk_size;
|
||||||
auto first_chunk_index = chunk_index;
|
auto first_chunk_index = chunk_index;
|
||||||
auto last_written_chunk_offset = m_write_offset % chunk_size;
|
auto last_written_chunk_offset = m_write_offset % chunk_size;
|
||||||
auto first_chunk_offset = m_read_offset % chunk_size;
|
auto first_chunk_offset = m_read_offset % chunk_size;
|
||||||
size_t last_chunk_offset = 0;
|
size_t last_chunk_offset = 0;
|
||||||
auto found_value = false;
|
auto found_value = false;
|
||||||
|
auto chunk_index_max_bound = last_written_chunk_offset > 0 ? last_written_chunk_index + 1 : last_written_chunk_index;
|
||||||
|
|
||||||
for (; chunk_index <= last_written_chunk_index; ++chunk_index) {
|
for (; chunk_index < chunk_index_max_bound; ++chunk_index) {
|
||||||
auto chunk_bytes = m_chunks[chunk_index].bytes();
|
auto& chunk = m_chunks[chunk_index];
|
||||||
|
auto chunk_bytes = chunk.bytes();
|
||||||
size_t chunk_offset = 0;
|
size_t chunk_offset = 0;
|
||||||
if (chunk_index == last_written_chunk_index) {
|
if (chunk_index == last_written_chunk_index) {
|
||||||
chunk_bytes = chunk_bytes.slice(0, last_written_chunk_offset);
|
chunk_bytes = chunk_bytes.slice(0, last_written_chunk_offset);
|
||||||
|
|
|
@ -194,4 +194,18 @@ TEST_CASE(new_output_memory_stream)
|
||||||
EXPECT_EQ(stream.bytes().size(), 2u);
|
EXPECT_EQ(stream.bytes().size(), 2u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE(offset_of_out_of_bounds)
|
||||||
|
{
|
||||||
|
Array<u8, 4> target { 0xff, 0xff, 0xff, 0xff };
|
||||||
|
|
||||||
|
Array<u8, DuplexMemoryStream::chunk_size> whole_chunk;
|
||||||
|
whole_chunk.span().fill(0);
|
||||||
|
|
||||||
|
DuplexMemoryStream stream;
|
||||||
|
|
||||||
|
stream << whole_chunk;
|
||||||
|
|
||||||
|
EXPECT(!stream.offset_of(target).has_value());
|
||||||
|
}
|
||||||
|
|
||||||
TEST_MAIN(MemoryStream)
|
TEST_MAIN(MemoryStream)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue