mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-12-07 21:59:54 +00:00
RequestServer: Store a couple of data hashes in disk cache entries
We currently have a FIXME to validate cached data with a crc32. But this is sort of a non-starter, because we never actually have the cached data in-memory - we transfer it to the WebContent process via system calls, and it never reaches userspace in RequestServer. Chrome makes a bit of an educated gamble here. They assume cosmic bit blips are extremely unlikely, thus the cached data does not get verified with a hash. Instead, they store non-cryptographic hashes of some select fields, and they validate just those hashes. Here, we store a hash of the cache key in the cache header, and a hash of the cache header in the cache footer. With these validations, along with other validations already in-place, we can be reasonably sure we are not sending corrupt data to the WebContent process.
This commit is contained in:
parent
3663e12585
commit
4470f94129
Notes:
github-actions[bot]
2025-11-21 07:49:53 +00:00
Author: https://github.com/trflynn89
Commit: 4470f94129
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/6886
Reviewed-by: https://github.com/gmta ✅
3 changed files with 33 additions and 8 deletions
|
|
@ -4,6 +4,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/HashFunctions.h>
|
||||
#include <AK/ScopeGuard.h>
|
||||
#include <LibCore/Notifier.h>
|
||||
#include <LibCore/System.h>
|
||||
|
|
@ -20,6 +21,7 @@ ErrorOr<CacheHeader> CacheHeader::read_from_stream(Stream& stream)
|
|||
CacheHeader header;
|
||||
header.magic = TRY(stream.read_value<u32>());
|
||||
header.version = TRY(stream.read_value<u32>());
|
||||
header.key_hash = TRY(stream.read_value<u32>());
|
||||
header.url_size = TRY(stream.read_value<u32>());
|
||||
header.url_hash = TRY(stream.read_value<u32>());
|
||||
header.status_code = TRY(stream.read_value<u32>());
|
||||
|
|
@ -32,6 +34,7 @@ ErrorOr<void> CacheHeader::write_to_stream(Stream& stream) const
|
|||
{
|
||||
TRY(stream.write_value(magic));
|
||||
TRY(stream.write_value(version));
|
||||
TRY(stream.write_value(key_hash));
|
||||
TRY(stream.write_value(url_size));
|
||||
TRY(stream.write_value(url_hash));
|
||||
TRY(stream.write_value(status_code));
|
||||
|
|
@ -40,10 +43,24 @@ ErrorOr<void> CacheHeader::write_to_stream(Stream& stream) const
|
|||
return {};
|
||||
}
|
||||
|
||||
u32 CacheHeader::hash() const
|
||||
{
|
||||
u32 hash = 0;
|
||||
hash = pair_int_hash(hash, magic);
|
||||
hash = pair_int_hash(hash, version);
|
||||
hash = pair_int_hash(hash, key_hash);
|
||||
hash = pair_int_hash(hash, url_size);
|
||||
hash = pair_int_hash(hash, url_hash);
|
||||
hash = pair_int_hash(hash, status_code);
|
||||
hash = pair_int_hash(hash, reason_phrase_size);
|
||||
hash = pair_int_hash(hash, reason_phrase_hash);
|
||||
return hash;
|
||||
}
|
||||
|
||||
ErrorOr<void> CacheFooter::write_to_stream(Stream& stream) const
|
||||
{
|
||||
TRY(stream.write_value(data_size));
|
||||
TRY(stream.write_value(crc32));
|
||||
TRY(stream.write_value(header_hash));
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
@ -51,7 +68,7 @@ ErrorOr<CacheFooter> CacheFooter::read_from_stream(Stream& stream)
|
|||
{
|
||||
CacheFooter footer;
|
||||
footer.data_size = TRY(stream.read_value<u64>());
|
||||
footer.crc32 = TRY(stream.read_value<u32>());
|
||||
footer.header_hash = TRY(stream.read_value<u32>());
|
||||
return footer;
|
||||
}
|
||||
|
||||
|
|
@ -84,6 +101,7 @@ ErrorOr<NonnullOwnPtr<CacheEntryWriter>> CacheEntryWriter::create(DiskCache& dis
|
|||
auto file = TRY(Core::OutputBufferedFile::create(move(unbuffered_file)));
|
||||
|
||||
CacheHeader cache_header;
|
||||
cache_header.key_hash = u64_hash(cache_key);
|
||||
cache_header.url_size = url.byte_count();
|
||||
cache_header.url_hash = url.hash();
|
||||
|
||||
|
|
@ -162,8 +180,6 @@ ErrorOr<void> CacheEntryWriter::write_data(ReadonlyBytes data)
|
|||
}
|
||||
|
||||
m_cache_footer.data_size += data.size();
|
||||
|
||||
// FIXME: Update the crc.
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
@ -174,6 +190,8 @@ ErrorOr<void> CacheEntryWriter::flush(HTTP::HeaderMap response_headers)
|
|||
if (m_marked_for_deletion)
|
||||
return Error::from_string_literal("Cache entry has been deleted");
|
||||
|
||||
m_cache_footer.header_hash = m_cache_header.hash();
|
||||
|
||||
if (auto result = m_file->write_value(m_cache_footer); result.is_error()) {
|
||||
dbgln("\033[31;1mUnable to flush cache entry for\033[0m {}: {}", m_url, result.error());
|
||||
remove();
|
||||
|
|
@ -209,6 +227,9 @@ ErrorOr<NonnullOwnPtr<CacheEntryReader>> CacheEntryReader::create(DiskCache& dis
|
|||
if (cache_header.version != CACHE_VERSION)
|
||||
return Error::from_string_literal("Version mismatch");
|
||||
|
||||
if (cache_header.key_hash != u64_hash(cache_key))
|
||||
return Error::from_string_literal("Key hash mismatch");
|
||||
|
||||
url = TRY(String::from_stream(*file, cache_header.url_size));
|
||||
if (url.hash() != cache_header.url_hash)
|
||||
return Error::from_string_literal("URL hash mismatch");
|
||||
|
|
@ -350,8 +371,8 @@ ErrorOr<void> CacheEntryReader::read_and_validate_footer()
|
|||
|
||||
if (m_cache_footer.data_size != m_data_size)
|
||||
return Error::from_string_literal("Invalid data size in footer");
|
||||
|
||||
// FIXME: Validate the crc.
|
||||
if (m_cache_footer.header_hash != m_cache_header.hash())
|
||||
return Error::from_string_literal("Invalid header hash in footer");
|
||||
|
||||
return {};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,11 +22,15 @@ struct CacheHeader {
|
|||
static ErrorOr<CacheHeader> read_from_stream(Stream&);
|
||||
ErrorOr<void> write_to_stream(Stream&) const;
|
||||
|
||||
u32 hash() const;
|
||||
|
||||
static constexpr auto CACHE_MAGIC = 0xcafef00du;
|
||||
|
||||
u32 magic { CACHE_MAGIC };
|
||||
u32 version { CACHE_VERSION };
|
||||
|
||||
u32 key_hash { 0 };
|
||||
|
||||
u32 url_size { 0 };
|
||||
u32 url_hash { 0 };
|
||||
|
||||
|
|
@ -40,7 +44,7 @@ struct CacheFooter {
|
|||
ErrorOr<void> write_to_stream(Stream&) const;
|
||||
|
||||
u64 data_size { 0 };
|
||||
u32 crc32 { 0 };
|
||||
u32 header_hash { 0 };
|
||||
};
|
||||
|
||||
// A cache entry is an amalgamation of all information needed to reconstruct HTTP responses. It is created once we have
|
||||
|
|
|
|||
|
|
@ -11,6 +11,6 @@
|
|||
namespace RequestServer {
|
||||
|
||||
// Increment this version when a breaking change is made to the cache index or cache entry formats.
|
||||
static constexpr inline u32 CACHE_VERSION = 3u;
|
||||
static constexpr inline u32 CACHE_VERSION = 4u;
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue