123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "VariableLengthPrefixSet.h"
- #include "nsUrlClassifierPrefixSet.h"
- #include "nsPrintfCString.h"
- #include "nsThreadUtils.h"
- #include "mozilla/EndianUtils.h"
- #include "mozilla/Telemetry.h"
- #include "mozilla/Unused.h"
- #include <algorithm>
- // MOZ_LOG=UrlClassifierPrefixSet:5
- static mozilla::LazyLogModule gUrlClassifierPrefixSetLog("UrlClassifierPrefixSet");
- #define LOG(args) MOZ_LOG(gUrlClassifierPrefixSetLog, mozilla::LogLevel::Debug, args)
- #define LOG_ENABLED() MOZ_LOG_TEST(gUrlClassifierPrefixSetLog, mozilla::LogLevel::Debug)
- namespace mozilla {
- namespace safebrowsing {
- #define PREFIX_SIZE_FIXED 4
- NS_IMPL_ISUPPORTS(VariableLengthPrefixSet, nsIMemoryReporter)
- // Definition required due to std::max<>()
- const uint32_t VariableLengthPrefixSet::MAX_BUFFER_SIZE;
- // This class will process prefix size between 4~32. But for 4 bytes prefixes,
- // they will be passed to nsUrlClassifierPrefixSet because of better optimization.
- VariableLengthPrefixSet::VariableLengthPrefixSet()
- : mLock("VariableLengthPrefixSet.mLock")
- , mMemoryReportPath()
- {
- mFixedPrefixSet = new nsUrlClassifierPrefixSet();
- }
- NS_IMETHODIMP
- VariableLengthPrefixSet::Init(const nsACString& aName)
- {
- mMemoryReportPath =
- nsPrintfCString(
- "explicit/storage/prefix-set/%s",
- (!aName.IsEmpty() ? PromiseFlatCString(aName).get() : "?!")
- );
- RegisterWeakMemoryReporter(this);
- return NS_OK;
- }
- VariableLengthPrefixSet::~VariableLengthPrefixSet()
- {
- UnregisterWeakMemoryReporter(this);
- }
- NS_IMETHODIMP
- VariableLengthPrefixSet::SetPrefixes(const PrefixStringMap& aPrefixMap)
- {
- MutexAutoLock lock(mLock);
- // Prefix size should not less than 4-bytes or greater than 32-bytes
- for (auto iter = aPrefixMap.ConstIter(); !iter.Done(); iter.Next()) {
- if (iter.Key() < PREFIX_SIZE_FIXED ||
- iter.Key() > COMPLETE_SIZE) {
- return NS_ERROR_FAILURE;
- }
- }
- // Clear old prefixSet before setting new one.
- mFixedPrefixSet->SetPrefixes(nullptr, 0);
- mVLPrefixSet.Clear();
- // 4-bytes prefixes are handled by nsUrlClassifierPrefixSet.
- nsCString* prefixes = aPrefixMap.Get(PREFIX_SIZE_FIXED);
- if (prefixes) {
- NS_ENSURE_TRUE(prefixes->Length() % PREFIX_SIZE_FIXED == 0, NS_ERROR_FAILURE);
- uint32_t numPrefixes = prefixes->Length() / PREFIX_SIZE_FIXED;
- #if MOZ_BIG_ENDIAN
- const uint32_t* arrayPtr = reinterpret_cast<const uint32_t*>(prefixes->BeginReading());
- #else
- FallibleTArray<uint32_t> array;
- // Prefixes are lexicographically-sorted, so the interger array
- // passed to nsUrlClassifierPrefixSet should also follow the same order.
- // To make sure of that, we convert char array to integer with Big-Endian
- // instead of casting to integer directly.
- if (!array.SetCapacity(numPrefixes, fallible)) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
- const char* begin = prefixes->BeginReading();
- const char* end = prefixes->EndReading();
- while (begin != end) {
- array.AppendElement(BigEndian::readUint32(begin), fallible);
- begin += sizeof(uint32_t);
- }
- const uint32_t* arrayPtr = array.Elements();
- #endif
- nsresult rv = mFixedPrefixSet->SetPrefixes(arrayPtr, numPrefixes);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- // 5~32 bytes prefixes are stored in mVLPrefixSet.
- for (auto iter = aPrefixMap.ConstIter(); !iter.Done(); iter.Next()) {
- // Skip 4bytes prefixes because it is already stored in mFixedPrefixSet.
- if (iter.Key() == PREFIX_SIZE_FIXED) {
- continue;
- }
- mVLPrefixSet.Put(iter.Key(), new nsCString(*iter.Data()));
- }
- return NS_OK;
- }
- nsresult
- VariableLengthPrefixSet::GetPrefixes(PrefixStringMap& aPrefixMap)
- {
- MutexAutoLock lock(mLock);
- // 4-bytes prefixes are handled by nsUrlClassifierPrefixSet.
- FallibleTArray<uint32_t> array;
- nsresult rv = mFixedPrefixSet->GetPrefixesNative(array);
- NS_ENSURE_SUCCESS(rv, rv);
- size_t count = array.Length();
- if (count) {
- nsCString* prefixes = new nsCString();
- prefixes->SetLength(PREFIX_SIZE_FIXED * count);
- // Writing integer array to character array
- uint32_t* begin = reinterpret_cast<uint32_t*>(prefixes->BeginWriting());
- for (uint32_t i = 0; i < count; i++) {
- begin[i] = NativeEndian::swapToBigEndian(array[i]);
- }
- aPrefixMap.Put(PREFIX_SIZE_FIXED, prefixes);
- }
- // Copy variable-length prefix set
- for (auto iter = mVLPrefixSet.ConstIter(); !iter.Done(); iter.Next()) {
- aPrefixMap.Put(iter.Key(), new nsCString(*iter.Data()));
- }
- return NS_OK;
- }
- // It should never be the case that more than one hash prefixes match a given
- // full hash. However, if that happens, this method returns any one of them.
- // It does not guarantee which one of those will be returned.
- NS_IMETHODIMP
- VariableLengthPrefixSet::Matches(const nsACString& aFullHash, uint32_t* aLength)
- {
- MutexAutoLock lock(mLock);
- // Only allow full-length hash to check if match any of the prefix
- MOZ_ASSERT(aFullHash.Length() == COMPLETE_SIZE);
- NS_ENSURE_ARG_POINTER(aLength);
- *aLength = 0;
- // Check if it matches 4-bytes prefixSet first
- const uint32_t* hash = reinterpret_cast<const uint32_t*>(aFullHash.BeginReading());
- uint32_t value = BigEndian::readUint32(hash);
- bool found = false;
- nsresult rv = mFixedPrefixSet->Contains(value, &found);
- NS_ENSURE_SUCCESS(rv, rv);
- if (found) {
- *aLength = PREFIX_SIZE_FIXED;
- return NS_OK;
- }
- for (auto iter = mVLPrefixSet.ConstIter(); !iter.Done(); iter.Next()) {
- if (BinarySearch(aFullHash, *iter.Data(), iter.Key())) {
- *aLength = iter.Key();
- return NS_OK;
- }
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- VariableLengthPrefixSet::IsEmpty(bool* aEmpty)
- {
- MutexAutoLock lock(mLock);
- NS_ENSURE_ARG_POINTER(aEmpty);
- mFixedPrefixSet->IsEmpty(aEmpty);
- *aEmpty = *aEmpty && mVLPrefixSet.IsEmpty();
- return NS_OK;
- }
- NS_IMETHODIMP
- VariableLengthPrefixSet::LoadFromFile(nsIFile* aFile)
- {
- MutexAutoLock lock(mLock);
- NS_ENSURE_ARG_POINTER(aFile);
- nsCOMPtr<nsIInputStream> localInFile;
- nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(localInFile), aFile,
- PR_RDONLY | nsIFile::OS_READAHEAD);
- NS_ENSURE_SUCCESS(rv, rv);
- // Calculate how big the file is, make sure our read buffer isn't bigger
- // than the file itself which is just wasting memory.
- int64_t fileSize;
- rv = aFile->GetFileSize(&fileSize);
- NS_ENSURE_SUCCESS(rv, rv);
- if (fileSize < 0 || fileSize > UINT32_MAX) {
- return NS_ERROR_FAILURE;
- }
- uint32_t bufferSize = std::min<uint32_t>(static_cast<uint32_t>(fileSize),
- MAX_BUFFER_SIZE);
- // Convert to buffered stream
- nsCOMPtr<nsIInputStream> in = NS_BufferInputStream(localInFile, bufferSize);
- rv = mFixedPrefixSet->LoadPrefixes(in);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = LoadPrefixes(in);
- NS_ENSURE_SUCCESS(rv, rv);
- return NS_OK;;
- }
- NS_IMETHODIMP
- VariableLengthPrefixSet::StoreToFile(nsIFile* aFile)
- {
- NS_ENSURE_ARG_POINTER(aFile);
- MutexAutoLock lock(mLock);
- nsCOMPtr<nsIOutputStream> localOutFile;
- nsresult rv = NS_NewLocalFileOutputStream(getter_AddRefs(localOutFile), aFile,
- PR_WRONLY | PR_TRUNCATE | PR_CREATE_FILE);
- NS_ENSURE_SUCCESS(rv, rv);
- uint32_t fileSize = 0;
- // Preallocate the file storage
- nsCOMPtr<nsIFileOutputStream> fos(do_QueryInterface(localOutFile));
- fileSize += mFixedPrefixSet->CalculatePreallocateSize();
- fileSize += CalculatePreallocateSize();
- Unused << fos->Preallocate(fileSize);
- // Convert to buffered stream
- nsCOMPtr<nsIOutputStream> out =
- NS_BufferOutputStream(localOutFile, std::min(fileSize, MAX_BUFFER_SIZE));
- rv = mFixedPrefixSet->WritePrefixes(out);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = WritePrefixes(out);
- NS_ENSURE_SUCCESS(rv, rv);
- return NS_OK;
- }
- nsresult
- VariableLengthPrefixSet::LoadPrefixes(nsIInputStream* in)
- {
- uint32_t magic;
- uint32_t read;
- nsresult rv = in->Read(reinterpret_cast<char*>(&magic), sizeof(uint32_t), &read);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(read == sizeof(uint32_t), NS_ERROR_FAILURE);
- if (magic != PREFIXSET_VERSION_MAGIC) {
- LOG(("Version magic mismatch, not loading"));
- return NS_ERROR_FILE_CORRUPTED;
- }
- mVLPrefixSet.Clear();
- uint32_t count;
- rv = in->Read(reinterpret_cast<char*>(&count), sizeof(uint32_t), &read);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(read == sizeof(uint32_t), NS_ERROR_FAILURE);
- for(;count > 0; count--) {
- uint8_t prefixSize;
- rv = in->Read(reinterpret_cast<char*>(&prefixSize), sizeof(uint8_t), &read);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(read == sizeof(uint8_t), NS_ERROR_FAILURE);
- uint32_t stringLength;
- rv = in->Read(reinterpret_cast<char*>(&stringLength), sizeof(uint32_t), &read);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(read == sizeof(uint32_t), NS_ERROR_FAILURE);
- nsCString* vlPrefixes = new nsCString();
- if (!vlPrefixes->SetLength(stringLength, fallible)) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
- rv = in->Read(reinterpret_cast<char*>(vlPrefixes->BeginWriting()), stringLength, &read);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(read == stringLength, NS_ERROR_FAILURE);
- mVLPrefixSet.Put(prefixSize, vlPrefixes);
- }
- return NS_OK;
- }
- uint32_t
- VariableLengthPrefixSet::CalculatePreallocateSize()
- {
- uint32_t fileSize = 0;
- // Store how many prefix string.
- fileSize += sizeof(uint32_t);
- for (auto iter = mVLPrefixSet.ConstIter(); !iter.Done(); iter.Next()) {
- // Store prefix size, prefix string length, and prefix string.
- fileSize += sizeof(uint8_t);
- fileSize += sizeof(uint32_t);
- fileSize += iter.Data()->Length();
- }
- return fileSize;
- }
- nsresult
- VariableLengthPrefixSet::WritePrefixes(nsIOutputStream* out)
- {
- uint32_t written;
- uint32_t writelen = sizeof(uint32_t);
- uint32_t magic = PREFIXSET_VERSION_MAGIC;
- nsresult rv = out->Write(reinterpret_cast<char*>(&magic), writelen, &written);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE);
- uint32_t count = mVLPrefixSet.Count();
- rv = out->Write(reinterpret_cast<char*>(&count), writelen, &written);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE);
- // Store PrefixSize, Length of Prefix String and then Prefix String
- for (auto iter = mVLPrefixSet.ConstIter(); !iter.Done(); iter.Next()) {
- const nsCString& vlPrefixes = *iter.Data();
- uint8_t prefixSize = iter.Key();
- writelen = sizeof(uint8_t);
- rv = out->Write(reinterpret_cast<char*>(&prefixSize), writelen, &written);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE);
- uint32_t stringLength = vlPrefixes.Length();
- writelen = sizeof(uint32_t);
- rv = out->Write(reinterpret_cast<char*>(&stringLength), writelen, &written);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE);
- rv = out->Write(const_cast<char*>(vlPrefixes.BeginReading()),
- stringLength, &written);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(stringLength == written, NS_ERROR_FAILURE);
- }
- return NS_OK;
- }
- bool
- VariableLengthPrefixSet::BinarySearch(const nsACString& aFullHash,
- const nsACString& aPrefixes,
- uint32_t aPrefixSize)
- {
- const char* fullhash = aFullHash.BeginReading();
- const char* prefixes = aPrefixes.BeginReading();
- int32_t begin = 0, end = aPrefixes.Length() / aPrefixSize;
- while (end > begin) {
- int32_t mid = (begin + end) >> 1;
- int cmp = memcmp(fullhash, prefixes + mid*aPrefixSize, aPrefixSize);
- if (cmp < 0) {
- end = mid;
- } else if (cmp > 0) {
- begin = mid + 1;
- } else {
- return true;
- }
- }
- return false;
- }
- MOZ_DEFINE_MALLOC_SIZE_OF(UrlClassifierMallocSizeOf)
- NS_IMETHODIMP
- VariableLengthPrefixSet::CollectReports(nsIHandleReportCallback* aHandleReport,
- nsISupports* aData, bool aAnonymize)
- {
- MOZ_ASSERT(NS_IsMainThread());
- size_t amount = SizeOfIncludingThis(UrlClassifierMallocSizeOf);
- return aHandleReport->Callback(
- EmptyCString(), mMemoryReportPath, KIND_HEAP, UNITS_BYTES, amount,
- NS_LITERAL_CSTRING("Memory used by the variable-length prefix set for a URL classifier."),
- aData);
- }
- size_t
- VariableLengthPrefixSet::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
- {
- MutexAutoLock lock(mLock);
- size_t n = 0;
- n += aMallocSizeOf(this);
- n += mFixedPrefixSet->SizeOfIncludingThis(moz_malloc_size_of) - aMallocSizeOf(mFixedPrefixSet);
- n += mVLPrefixSet.ShallowSizeOfExcludingThis(aMallocSizeOf);
- for (auto iter = mVLPrefixSet.ConstIter(); !iter.Done(); iter.Next()) {
- n += iter.Data()->SizeOfExcludingThisIfUnshared(aMallocSizeOf);
- }
- return n;
- }
- } // namespace safebrowsing
- } // namespace mozilla
|