Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/toolkit/components/url-classifier/VariableLengthPrefixSet.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "VariableLengthPrefixSet.h"
8
#include "nsUrlClassifierPrefixSet.h"
9
#include "nsPrintfCString.h"
10
#include "nsThreadUtils.h"
11
#include "mozilla/EndianUtils.h"
12
#include "mozilla/Telemetry.h"
13
#include "mozilla/Unused.h"
14
#include <algorithm>
15
16
// MOZ_LOG=UrlClassifierPrefixSet:5
17
static mozilla::LazyLogModule gUrlClassifierPrefixSetLog("UrlClassifierPrefixSet");
18
0
#define LOG(args) MOZ_LOG(gUrlClassifierPrefixSetLog, mozilla::LogLevel::Debug, args)
19
#define LOG_ENABLED() MOZ_LOG_TEST(gUrlClassifierPrefixSetLog, mozilla::LogLevel::Debug)
20
21
namespace mozilla {
22
namespace safebrowsing {
23
24
0
#define PREFIX_SIZE_FIXED 4
25
26
NS_IMPL_ISUPPORTS(VariableLengthPrefixSet, nsIMemoryReporter)
27
28
// Definition required due to std::max<>()
29
const uint32_t VariableLengthPrefixSet::MAX_BUFFER_SIZE;
30
31
// This class will process prefix size between 4~32. But for 4 bytes prefixes,
32
// they will be passed to nsUrlClassifierPrefixSet because of better optimization.
33
VariableLengthPrefixSet::VariableLengthPrefixSet()
34
  : mLock("VariableLengthPrefixSet.mLock")
35
  , mFixedPrefixSet(new nsUrlClassifierPrefixSet)
36
0
{
37
0
}
38
39
nsresult
40
VariableLengthPrefixSet::Init(const nsACString& aName)
41
0
{
42
0
  mMemoryReportPath =
43
0
    nsPrintfCString(
44
0
      "explicit/storage/prefix-set/%s",
45
0
      (!aName.IsEmpty() ? PromiseFlatCString(aName).get() : "?!")
46
0
    );
47
0
48
0
  RegisterWeakMemoryReporter(this);
49
0
50
0
  return mFixedPrefixSet->Init(aName);
51
0
}
52
53
VariableLengthPrefixSet::~VariableLengthPrefixSet()
54
0
{
55
0
  UnregisterWeakMemoryReporter(this);
56
0
}
57
58
nsresult
59
VariableLengthPrefixSet::SetPrefixes(const PrefixStringMap& aPrefixMap)
60
0
{
61
0
  MutexAutoLock lock(mLock);
62
0
63
0
  // Prefix size should not less than 4-bytes or greater than 32-bytes
64
0
  for (auto iter = aPrefixMap.ConstIter(); !iter.Done(); iter.Next()) {
65
0
    if (iter.Key() < PREFIX_SIZE_FIXED ||
66
0
        iter.Key() > COMPLETE_SIZE) {
67
0
      return NS_ERROR_FAILURE;
68
0
    }
69
0
  }
70
0
71
0
  // Clear old prefixSet before setting new one.
72
0
  mFixedPrefixSet->SetPrefixes(nullptr, 0);
73
0
  mVLPrefixSet.Clear();
74
0
75
0
  // 4-bytes prefixes are handled by nsUrlClassifierPrefixSet.
76
0
  nsCString* prefixes = aPrefixMap.Get(PREFIX_SIZE_FIXED);
77
0
  if (prefixes) {
78
0
    NS_ENSURE_TRUE(prefixes->Length() % PREFIX_SIZE_FIXED == 0, NS_ERROR_FAILURE);
79
0
80
0
    uint32_t numPrefixes = prefixes->Length() / PREFIX_SIZE_FIXED;
81
0
82
#if MOZ_BIG_ENDIAN
83
    const uint32_t* arrayPtr = reinterpret_cast<const uint32_t*>(prefixes->BeginReading());
84
#else
85
    FallibleTArray<uint32_t> array;
86
0
    // Prefixes are lexicographically-sorted, so the interger array
87
0
    // passed to nsUrlClassifierPrefixSet should also follow the same order.
88
0
    // To make sure of that, we convert char array to integer with Big-Endian
89
0
    // instead of casting to integer directly.
90
0
    if (!array.SetCapacity(numPrefixes, fallible)) {
91
0
      return NS_ERROR_OUT_OF_MEMORY;
92
0
    }
93
0
94
0
    const char* begin = prefixes->BeginReading();
95
0
    const char* end = prefixes->EndReading();
96
0
97
0
    while (begin != end) {
98
0
      array.AppendElement(BigEndian::readUint32(begin), fallible);
99
0
      begin += sizeof(uint32_t);
100
0
    }
101
0
    MOZ_ASSERT(array.Length() == numPrefixes);
102
0
103
0
    const uint32_t* arrayPtr = array.Elements();
104
0
#endif
105
0
106
0
    nsresult rv = mFixedPrefixSet->SetPrefixes(arrayPtr, numPrefixes);
107
0
    NS_ENSURE_SUCCESS(rv, rv);
108
0
  }
109
0
110
0
  // 5~32 bytes prefixes are stored in mVLPrefixSet.
111
0
  for (auto iter = aPrefixMap.ConstIter(); !iter.Done(); iter.Next()) {
112
0
    // Skip 4bytes prefixes because it is already stored in mFixedPrefixSet.
113
0
    if (iter.Key() == PREFIX_SIZE_FIXED) {
114
0
      continue;
115
0
    }
116
0
117
0
    mVLPrefixSet.Put(iter.Key(), new nsCString(*iter.Data()));
118
0
  }
119
0
120
0
  return NS_OK;
121
0
}
122
123
nsresult
124
VariableLengthPrefixSet::GetPrefixes(PrefixStringMap& aPrefixMap)
125
0
{
126
0
  MutexAutoLock lock(mLock);
127
0
128
0
  // 4-bytes prefixes are handled by nsUrlClassifierPrefixSet.
129
0
  FallibleTArray<uint32_t> array;
130
0
  nsresult rv = mFixedPrefixSet->GetPrefixesNative(array);
131
0
  NS_ENSURE_SUCCESS(rv, rv);
132
0
133
0
  size_t count = array.Length();
134
0
  if (count) {
135
0
    nsCString* prefixes = new nsCString();
136
0
    if (!prefixes->SetLength(PREFIX_SIZE_FIXED * count, fallible)) {
137
0
      return NS_ERROR_OUT_OF_MEMORY;
138
0
    }
139
0
140
0
    // Writing integer array to character array
141
0
    uint32_t* begin = reinterpret_cast<uint32_t*>(prefixes->BeginWriting());
142
0
    for (uint32_t i = 0; i < count; i++) {
143
0
      begin[i] = NativeEndian::swapToBigEndian(array[i]);
144
0
    }
145
0
146
0
    aPrefixMap.Put(PREFIX_SIZE_FIXED, prefixes);
147
0
  }
148
0
149
0
  // Copy variable-length prefix set
150
0
  for (auto iter = mVLPrefixSet.ConstIter(); !iter.Done(); iter.Next()) {
151
0
    aPrefixMap.Put(iter.Key(), new nsCString(*iter.Data()));
152
0
  }
153
0
154
0
  return NS_OK;
155
0
}
156
157
nsresult
158
VariableLengthPrefixSet::GetFixedLengthPrefixes(FallibleTArray<uint32_t>& aPrefixes)
159
0
{
160
0
  return mFixedPrefixSet->GetPrefixesNative(aPrefixes);
161
0
}
162
163
// It should never be the case that more than one hash prefixes match a given
164
// full hash. However, if that happens, this method returns any one of them.
165
// It does not guarantee which one of those will be returned.
166
nsresult
167
VariableLengthPrefixSet::Matches(const nsACString& aFullHash,
168
                                 uint32_t* aLength) const
169
0
{
170
0
  MutexAutoLock lock(mLock);
171
0
172
0
  // Only allow full-length hash to check if match any of the prefix
173
0
  MOZ_ASSERT(aFullHash.Length() == COMPLETE_SIZE);
174
0
  NS_ENSURE_ARG_POINTER(aLength);
175
0
176
0
  *aLength = 0;
177
0
178
0
  // Check if it matches 4-bytes prefixSet first
179
0
  const uint32_t* hash = reinterpret_cast<const uint32_t*>(aFullHash.BeginReading());
180
0
  uint32_t value = BigEndian::readUint32(hash);
181
0
182
0
  bool found = false;
183
0
  nsresult rv = mFixedPrefixSet->Contains(value, &found);
184
0
  NS_ENSURE_SUCCESS(rv, rv);
185
0
186
0
  if (found) {
187
0
    *aLength = PREFIX_SIZE_FIXED;
188
0
    return NS_OK;
189
0
  }
190
0
191
0
  for (auto iter = mVLPrefixSet.ConstIter(); !iter.Done(); iter.Next()) {
192
0
    if (BinarySearch(aFullHash, *iter.Data(), iter.Key())) {
193
0
      *aLength = iter.Key();
194
0
      MOZ_ASSERT(*aLength > 4);
195
0
      return NS_OK;
196
0
    }
197
0
  }
198
0
199
0
  return NS_OK;
200
0
}
201
202
nsresult
203
VariableLengthPrefixSet::IsEmpty(bool* aEmpty) const
204
0
{
205
0
  MutexAutoLock lock(mLock);
206
0
207
0
  NS_ENSURE_ARG_POINTER(aEmpty);
208
0
209
0
  mFixedPrefixSet->IsEmpty(aEmpty);
210
0
  *aEmpty = *aEmpty && mVLPrefixSet.IsEmpty();
211
0
212
0
  return NS_OK;
213
0
}
214
215
nsresult
216
VariableLengthPrefixSet::LoadFromFile(nsCOMPtr<nsIFile>& aFile)
217
0
{
218
0
  MutexAutoLock lock(mLock);
219
0
220
0
  NS_ENSURE_ARG_POINTER(aFile);
221
0
222
0
  Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_VLPS_FILELOAD_TIME> timer;
223
0
224
0
  nsCOMPtr<nsIInputStream> localInFile;
225
0
  nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(localInFile), aFile,
226
0
                                           PR_RDONLY | nsIFile::OS_READAHEAD);
227
0
  NS_ENSURE_SUCCESS(rv, rv);
228
0
229
0
  // Calculate how big the file is, make sure our read buffer isn't bigger
230
0
  // than the file itself which is just wasting memory.
231
0
  int64_t fileSize;
232
0
  rv = aFile->GetFileSize(&fileSize);
233
0
  NS_ENSURE_SUCCESS(rv, rv);
234
0
235
0
  if (fileSize < 0 || fileSize > UINT32_MAX) {
236
0
    return NS_ERROR_FAILURE;
237
0
  }
238
0
239
0
  uint32_t bufferSize = std::min<uint32_t>(static_cast<uint32_t>(fileSize),
240
0
                                           MAX_BUFFER_SIZE);
241
0
242
0
  // Convert to buffered stream
243
0
  nsCOMPtr<nsIInputStream> in;
244
0
  rv = NS_NewBufferedInputStream(getter_AddRefs(in), localInFile.forget(),
245
0
                                 bufferSize);
246
0
  NS_ENSURE_SUCCESS(rv, rv);
247
0
248
0
  rv = mFixedPrefixSet->LoadPrefixes(in);
249
0
  NS_ENSURE_SUCCESS(rv, rv);
250
0
251
0
  rv = LoadPrefixes(in);
252
0
  NS_ENSURE_SUCCESS(rv, rv);
253
0
254
0
  return NS_OK;;
255
0
}
256
257
nsresult
258
VariableLengthPrefixSet::StoreToFile(nsCOMPtr<nsIFile>& aFile) const
259
0
{
260
0
  NS_ENSURE_ARG_POINTER(aFile);
261
0
262
0
  MutexAutoLock lock(mLock);
263
0
264
0
  nsCOMPtr<nsIOutputStream> localOutFile;
265
0
  nsresult rv = NS_NewLocalFileOutputStream(getter_AddRefs(localOutFile), aFile,
266
0
                                            PR_WRONLY | PR_TRUNCATE | PR_CREATE_FILE);
267
0
  NS_ENSURE_SUCCESS(rv, rv);
268
0
269
0
  uint32_t fileSize = 0;
270
0
  // Preallocate the file storage
271
0
  {
272
0
    nsCOMPtr<nsIFileOutputStream> fos(do_QueryInterface(localOutFile));
273
0
    Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_VLPS_FALLOCATE_TIME> timer;
274
0
275
0
    fileSize += mFixedPrefixSet->CalculatePreallocateSize();
276
0
    fileSize += CalculatePreallocateSize();
277
0
278
0
    Unused << fos->Preallocate(fileSize);
279
0
  }
280
0
281
0
  // Convert to buffered stream
282
0
  nsCOMPtr<nsIOutputStream> out;
283
0
  rv = NS_NewBufferedOutputStream(getter_AddRefs(out), localOutFile.forget(),
284
0
                                  std::min(fileSize, MAX_BUFFER_SIZE));
285
0
  NS_ENSURE_SUCCESS(rv, rv);
286
0
287
0
  rv = mFixedPrefixSet->WritePrefixes(out);
288
0
  NS_ENSURE_SUCCESS(rv, rv);
289
0
290
0
  rv = WritePrefixes(out);
291
0
  NS_ENSURE_SUCCESS(rv, rv);
292
0
293
0
  return NS_OK;
294
0
}
295
296
nsresult
297
VariableLengthPrefixSet::LoadPrefixes(nsCOMPtr<nsIInputStream>& in)
298
0
{
299
0
  uint32_t magic;
300
0
  uint32_t read;
301
0
302
0
  nsresult rv = in->Read(reinterpret_cast<char*>(&magic), sizeof(uint32_t), &read);
303
0
  NS_ENSURE_SUCCESS(rv, rv);
304
0
  NS_ENSURE_TRUE(read == sizeof(uint32_t), NS_ERROR_FAILURE);
305
0
306
0
  if (magic != PREFIXSET_VERSION_MAGIC) {
307
0
    LOG(("Version magic mismatch, not loading"));
308
0
    return NS_ERROR_FILE_CORRUPTED;
309
0
  }
310
0
311
0
  mVLPrefixSet.Clear();
312
0
313
0
  uint32_t count;
314
0
  rv = in->Read(reinterpret_cast<char*>(&count), sizeof(uint32_t), &read);
315
0
  NS_ENSURE_SUCCESS(rv, rv);
316
0
  NS_ENSURE_TRUE(read == sizeof(uint32_t), NS_ERROR_FAILURE);
317
0
318
0
  for(;count > 0; count--) {
319
0
    uint8_t prefixSize;
320
0
    rv = in->Read(reinterpret_cast<char*>(&prefixSize), sizeof(uint8_t), &read);
321
0
    NS_ENSURE_SUCCESS(rv, rv);
322
0
    NS_ENSURE_TRUE(read == sizeof(uint8_t), NS_ERROR_FAILURE);
323
0
324
0
    if (prefixSize < PREFIX_SIZE || prefixSize > COMPLETE_SIZE) {
325
0
      return NS_ERROR_FILE_CORRUPTED;
326
0
    }
327
0
328
0
    uint32_t stringLength;
329
0
    rv = in->Read(reinterpret_cast<char*>(&stringLength), sizeof(uint32_t), &read);
330
0
    NS_ENSURE_SUCCESS(rv, rv);
331
0
    NS_ENSURE_TRUE(read == sizeof(uint32_t), NS_ERROR_FAILURE);
332
0
333
0
    nsCString* vlPrefixes = new nsCString();
334
0
    if (!vlPrefixes->SetLength(stringLength, fallible)) {
335
0
      return NS_ERROR_OUT_OF_MEMORY;
336
0
    }
337
0
338
0
    rv = in->Read(reinterpret_cast<char*>(vlPrefixes->BeginWriting()), stringLength, &read);
339
0
    NS_ENSURE_SUCCESS(rv, rv);
340
0
    NS_ENSURE_TRUE(read == stringLength, NS_ERROR_FAILURE);
341
0
342
0
    mVLPrefixSet.Put(prefixSize, vlPrefixes);
343
0
  }
344
0
345
0
  return NS_OK;
346
0
}
347
348
uint32_t
349
VariableLengthPrefixSet::CalculatePreallocateSize() const
350
0
{
351
0
  uint32_t fileSize = 0;
352
0
353
0
  // Store how many prefix string.
354
0
  fileSize += sizeof(uint32_t);
355
0
356
0
  for (auto iter = mVLPrefixSet.ConstIter(); !iter.Done(); iter.Next()) {
357
0
    // Store prefix size, prefix string length, and prefix string.
358
0
    fileSize += sizeof(uint8_t);
359
0
    fileSize += sizeof(uint32_t);
360
0
    fileSize += iter.Data()->Length();
361
0
  }
362
0
  return fileSize;
363
0
}
364
365
nsresult
366
VariableLengthPrefixSet::WritePrefixes(nsCOMPtr<nsIOutputStream>& out) const
367
0
{
368
0
  uint32_t written;
369
0
  uint32_t writelen = sizeof(uint32_t);
370
0
  uint32_t magic = PREFIXSET_VERSION_MAGIC;
371
0
  nsresult rv = out->Write(reinterpret_cast<char*>(&magic), writelen, &written);
372
0
  NS_ENSURE_SUCCESS(rv, rv);
373
0
  NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE);
374
0
375
0
  uint32_t count = mVLPrefixSet.Count();
376
0
  rv = out->Write(reinterpret_cast<char*>(&count), writelen, &written);
377
0
  NS_ENSURE_SUCCESS(rv, rv);
378
0
  NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE);
379
0
380
0
  // Store PrefixSize, Length of Prefix String and then Prefix String
381
0
  for (auto iter = mVLPrefixSet.ConstIter(); !iter.Done(); iter.Next()) {
382
0
    const nsCString& vlPrefixes = *iter.Data();
383
0
384
0
    uint8_t prefixSize = iter.Key();
385
0
    writelen = sizeof(uint8_t);
386
0
    rv = out->Write(reinterpret_cast<char*>(&prefixSize), writelen, &written);
387
0
    NS_ENSURE_SUCCESS(rv, rv);
388
0
    NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE);
389
0
390
0
    uint32_t stringLength = vlPrefixes.Length();
391
0
    writelen = sizeof(uint32_t);
392
0
    rv = out->Write(reinterpret_cast<char*>(&stringLength), writelen, &written);
393
0
    NS_ENSURE_SUCCESS(rv, rv);
394
0
    NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE);
395
0
396
0
    rv = out->Write(const_cast<char*>(vlPrefixes.BeginReading()),
397
0
                    stringLength, &written);
398
0
    NS_ENSURE_SUCCESS(rv, rv);
399
0
    NS_ENSURE_TRUE(stringLength == written, NS_ERROR_FAILURE);
400
0
  }
401
0
402
0
  return NS_OK;
403
0
}
404
405
bool
406
VariableLengthPrefixSet::BinarySearch(const nsACString& aFullHash,
407
                                      const nsACString& aPrefixes,
408
                                      uint32_t aPrefixSize) const
409
0
{
410
0
  const char* fullhash = aFullHash.BeginReading();
411
0
  const char* prefixes = aPrefixes.BeginReading();
412
0
  int32_t begin = 0, end = aPrefixes.Length() / aPrefixSize;
413
0
414
0
  while (end > begin) {
415
0
    int32_t mid = (begin + end) >> 1;
416
0
    int cmp = memcmp(fullhash, prefixes + mid*aPrefixSize, aPrefixSize);
417
0
    if (cmp < 0) {
418
0
      end = mid;
419
0
    } else if (cmp > 0) {
420
0
      begin = mid + 1;
421
0
    } else {
422
0
      return true;
423
0
    }
424
0
  }
425
0
  return false;
426
0
}
427
428
MOZ_DEFINE_MALLOC_SIZE_OF(UrlClassifierMallocSizeOf)
429
430
NS_IMETHODIMP
431
VariableLengthPrefixSet::CollectReports(nsIHandleReportCallback* aHandleReport,
432
                                        nsISupports* aData, bool aAnonymize)
433
0
{
434
0
  MOZ_ASSERT(NS_IsMainThread());
435
0
436
0
  size_t amount = SizeOfIncludingThis(UrlClassifierMallocSizeOf);
437
0
438
0
  return aHandleReport->Callback(
439
0
    EmptyCString(), mMemoryReportPath, KIND_HEAP, UNITS_BYTES, amount,
440
0
    NS_LITERAL_CSTRING("Memory used by the variable-length prefix set for a URL classifier."),
441
0
    aData);
442
0
}
443
444
size_t
445
VariableLengthPrefixSet::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
446
0
{
447
0
  MutexAutoLock lock(mLock);
448
0
449
0
  size_t n = 0;
450
0
  n += aMallocSizeOf(this);
451
0
  n += mFixedPrefixSet->SizeOfIncludingThis(moz_malloc_size_of) - aMallocSizeOf(mFixedPrefixSet);
452
0
453
0
  n += mVLPrefixSet.ShallowSizeOfExcludingThis(aMallocSizeOf);
454
0
  for (auto iter = mVLPrefixSet.ConstIter(); !iter.Done(); iter.Next()) {
455
0
    n += iter.Data()->SizeOfExcludingThisIfUnshared(aMallocSizeOf);
456
0
  }
457
0
458
0
  return n;
459
0
}
460
461
} // namespace safebrowsing
462
} // namespace mozilla