fune/netwerk/base/SSLTokensCache.cpp
Valentin Gosu 1a1f42da37 Bug 1714307 - Run modernize-use-default-member-init --fix check on netwerk r=necko-reviewers,kershaw
This changeset is the result of adding modernize-use-default-member-init to
tools/clang-tidy/config.yaml then proceeding to run
`./mach static-analysis check netwerk/ --fix`
I then went through the resulting fix and manually updated all of the member
variables which were missed due to them having a non-trivial constructor.

Note that the tool was only run on Linux, so code that only runs on some
platforms may have been missed.

The member variables that are still initialized in the contructor definition
are:
  - bitfields (not all currently supported compilers allow default-member-init
  - variables that are initialized via a parameter
  - variables that use code not visible in the header file

There are a few advantages to landing this change:
- fewer lines of code - now declaration is in the same place as initialization
  this also makes it easier to see when looking at the header.
- it makes it harder to miss initializing a member when adding a new contructor
- variables that depend on an include guard look much nicer now

Additionally I removed some unnecessary reinitialization of NetAddr members
(it has a constructor that does that now), and changed nsWifiScannerDBus to
use the thread-safe strtok_r instead of strtok.

Differential Revision: https://phabricator.services.mozilla.com/D116980
2021-06-11 07:10:41 +00:00

417 lines
12 KiB
C++

/* 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 "SSLTokensCache.h"
#include "mozilla/ArrayAlgorithm.h"
#include "mozilla/Preferences.h"
#include "mozilla/Logging.h"
#include "nsIOService.h"
#include "nsNSSIOLayer.h"
#include "TransportSecurityInfo.h"
#include "CertVerifier.h"
#include "ssl.h"
#include "sslexp.h"
namespace mozilla {
namespace net {
static LazyLogModule gSSLTokensCacheLog("SSLTokensCache");
#undef LOG
#define LOG(args) MOZ_LOG(gSSLTokensCacheLog, mozilla::LogLevel::Debug, args)
class ExpirationComparator {
public:
bool Equals(SSLTokensCache::TokenCacheRecord* a,
SSLTokensCache::TokenCacheRecord* b) const {
return a->mExpirationTime == b->mExpirationTime;
}
bool LessThan(SSLTokensCache::TokenCacheRecord* a,
SSLTokensCache::TokenCacheRecord* b) const {
return a->mExpirationTime < b->mExpirationTime;
}
};
SessionCacheInfo SessionCacheInfo::Clone() const {
SessionCacheInfo result;
result.mEVStatus = mEVStatus;
result.mCertificateTransparencyStatus = mCertificateTransparencyStatus;
result.mServerCertBytes = mServerCertBytes.Clone();
result.mSucceededCertChainBytes =
mSucceededCertChainBytes
? Some(TransformIntoNewArray(
*mSucceededCertChainBytes,
[](const auto& element) { return element.Clone(); }))
: Nothing();
return result;
}
StaticRefPtr<SSLTokensCache> SSLTokensCache::gInstance;
StaticMutex SSLTokensCache::sLock;
uint32_t SSLTokensCache::TokenCacheRecord::Size() const {
uint32_t size = mToken.Length() + sizeof(mSessionCacheInfo.mEVStatus) +
sizeof(mSessionCacheInfo.mCertificateTransparencyStatus) +
mSessionCacheInfo.mServerCertBytes.Length();
if (mSessionCacheInfo.mSucceededCertChainBytes) {
for (const auto& cert : mSessionCacheInfo.mSucceededCertChainBytes.ref()) {
size += cert.Length();
}
}
return size;
}
void SSLTokensCache::TokenCacheRecord::Reset() {
mToken.Clear();
mExpirationTime = 0;
mSessionCacheInfo.mEVStatus = psm::EVStatus::NotEV;
mSessionCacheInfo.mCertificateTransparencyStatus =
nsITransportSecurityInfo::CERTIFICATE_TRANSPARENCY_NOT_APPLICABLE;
mSessionCacheInfo.mServerCertBytes.Clear();
mSessionCacheInfo.mSucceededCertChainBytes.reset();
}
NS_IMPL_ISUPPORTS(SSLTokensCache, nsIMemoryReporter)
// static
nsresult SSLTokensCache::Init() {
StaticMutexAutoLock lock(sLock);
// SSLTokensCache should be only used in parent process and socket process.
// Ideally, parent process should not use this when socket process is enabled.
// However, some xpcsehll tests may need to create and use sockets directly,
// so we still allow to use this in parent process no matter socket process is
// enabled or not.
if (!(XRE_IsSocketProcess() || XRE_IsParentProcess())) {
return NS_OK;
}
MOZ_ASSERT(!gInstance);
gInstance = new SSLTokensCache();
RegisterWeakMemoryReporter(gInstance);
return NS_OK;
}
// static
nsresult SSLTokensCache::Shutdown() {
StaticMutexAutoLock lock(sLock);
if (!gInstance) {
return NS_ERROR_UNEXPECTED;
}
UnregisterWeakMemoryReporter(gInstance);
gInstance = nullptr;
return NS_OK;
}
SSLTokensCache::SSLTokensCache() { LOG(("SSLTokensCache::SSLTokensCache")); }
SSLTokensCache::~SSLTokensCache() { LOG(("SSLTokensCache::~SSLTokensCache")); }
// static
nsresult SSLTokensCache::Put(const nsACString& aKey, const uint8_t* aToken,
uint32_t aTokenLen,
nsITransportSecurityInfo* aSecInfo) {
PRUint32 expirationTime;
SSLResumptionTokenInfo tokenInfo;
if (SSL_GetResumptionTokenInfo(aToken, aTokenLen, &tokenInfo,
sizeof(tokenInfo)) != SECSuccess) {
LOG((" cannot get expiration time from the token, NSS error %d",
PORT_GetError()));
return NS_ERROR_FAILURE;
}
expirationTime = tokenInfo.expirationTime;
SSL_DestroyResumptionTokenInfo(&tokenInfo);
return Put(aKey, aToken, aTokenLen, aSecInfo, expirationTime);
}
// static
nsresult SSLTokensCache::Put(const nsACString& aKey, const uint8_t* aToken,
uint32_t aTokenLen,
nsITransportSecurityInfo* aSecInfo,
PRUint32 aExpirationTime) {
StaticMutexAutoLock lock(sLock);
LOG(("SSLTokensCache::Put [key=%s, tokenLen=%u]",
PromiseFlatCString(aKey).get(), aTokenLen));
if (!gInstance) {
LOG((" service not initialized"));
return NS_ERROR_NOT_INITIALIZED;
}
if (!aSecInfo) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIX509Cert> cert;
aSecInfo->GetServerCert(getter_AddRefs(cert));
if (!cert) {
return NS_ERROR_FAILURE;
}
nsTArray<uint8_t> certBytes;
nsresult rv = cert->GetRawDER(certBytes);
if (NS_FAILED(rv)) {
return rv;
}
Maybe<nsTArray<nsTArray<uint8_t>>> succeededCertChainBytes;
nsTArray<RefPtr<nsIX509Cert>> succeededCertArray;
rv = aSecInfo->GetSucceededCertChain(succeededCertArray);
if (NS_FAILED(rv)) {
return rv;
}
Maybe<bool> isBuiltCertChainRootBuiltInRoot;
if (!succeededCertArray.IsEmpty()) {
succeededCertChainBytes.emplace();
for (const auto& cert : succeededCertArray) {
nsTArray<uint8_t> rawCert;
nsresult rv = cert->GetRawDER(rawCert);
if (NS_FAILED(rv)) {
return rv;
}
succeededCertChainBytes->AppendElement(std::move(rawCert));
}
bool builtInRoot = false;
rv = aSecInfo->GetIsBuiltCertChainRootBuiltInRoot(&builtInRoot);
if (NS_FAILED(rv)) {
return rv;
}
isBuiltCertChainRootBuiltInRoot.emplace(builtInRoot);
}
bool isEV;
rv = aSecInfo->GetIsExtendedValidation(&isEV);
if (NS_FAILED(rv)) {
return rv;
}
uint16_t certificateTransparencyStatus;
rv = aSecInfo->GetCertificateTransparencyStatus(
&certificateTransparencyStatus);
if (NS_FAILED(rv)) {
return rv;
}
TokenCacheRecord* const rec =
gInstance->mTokenCacheRecords.WithEntryHandle(aKey, [&](auto&& entry) {
if (!entry) {
auto rec = MakeUnique<TokenCacheRecord>();
rec->mKey = aKey;
gInstance->mExpirationArray.AppendElement(rec.get());
entry.Insert(std::move(rec));
} else {
gInstance->mCacheSize -= entry.Data()->Size();
entry.Data()->Reset();
}
return entry->get();
});
rec->mExpirationTime = aExpirationTime;
MOZ_ASSERT(rec->mToken.IsEmpty());
rec->mToken.AppendElements(aToken, aTokenLen);
rec->mSessionCacheInfo.mServerCertBytes = std::move(certBytes);
rec->mSessionCacheInfo.mSucceededCertChainBytes =
succeededCertChainBytes
? Some(TransformIntoNewArray(
*succeededCertChainBytes,
[](auto& element) { return nsTArray(std::move(element)); }))
: Nothing();
if (isEV) {
rec->mSessionCacheInfo.mEVStatus = psm::EVStatus::EV;
}
rec->mSessionCacheInfo.mCertificateTransparencyStatus =
certificateTransparencyStatus;
rec->mSessionCacheInfo.mIsBuiltCertChainRootBuiltInRoot =
std::move(isBuiltCertChainRootBuiltInRoot);
gInstance->mCacheSize += rec->Size();
gInstance->LogStats();
gInstance->EvictIfNecessary();
return NS_OK;
}
// static
nsresult SSLTokensCache::Get(const nsACString& aKey,
nsTArray<uint8_t>& aToken) {
StaticMutexAutoLock lock(sLock);
LOG(("SSLTokensCache::Get [key=%s]", PromiseFlatCString(aKey).get()));
if (!gInstance) {
LOG((" service not initialized"));
return NS_ERROR_NOT_INITIALIZED;
}
TokenCacheRecord* rec = nullptr;
if (gInstance->mTokenCacheRecords.Get(aKey, &rec)) {
if (rec->mToken.Length()) {
aToken = rec->mToken.Clone();
return NS_OK;
}
}
LOG((" token not found"));
return NS_ERROR_NOT_AVAILABLE;
}
// static
bool SSLTokensCache::GetSessionCacheInfo(const nsACString& aKey,
SessionCacheInfo& aResult) {
StaticMutexAutoLock lock(sLock);
LOG(("SSLTokensCache::GetSessionCacheInfo [key=%s]",
PromiseFlatCString(aKey).get()));
if (!gInstance) {
LOG((" service not initialized"));
return false;
}
TokenCacheRecord* rec = nullptr;
if (gInstance->mTokenCacheRecords.Get(aKey, &rec)) {
aResult = rec->mSessionCacheInfo.Clone();
return true;
}
LOG((" token not found"));
return false;
}
// static
nsresult SSLTokensCache::Remove(const nsACString& aKey) {
StaticMutexAutoLock lock(sLock);
LOG(("SSLTokensCache::Remove [key=%s]", PromiseFlatCString(aKey).get()));
if (!gInstance) {
LOG((" service not initialized"));
return NS_ERROR_NOT_INITIALIZED;
}
return gInstance->RemoveLocked(aKey);
}
nsresult SSLTokensCache::RemoveLocked(const nsACString& aKey) {
sLock.AssertCurrentThreadOwns();
LOG(("SSLTokensCache::RemoveLocked [key=%s]",
PromiseFlatCString(aKey).get()));
UniquePtr<TokenCacheRecord> rec;
if (!mTokenCacheRecords.Remove(aKey, &rec)) {
LOG((" token not found"));
return NS_ERROR_NOT_AVAILABLE;
}
mCacheSize -= rec->Size();
if (!mExpirationArray.RemoveElement(rec.get())) {
MOZ_ASSERT(false, "token not found in mExpirationArray");
}
LogStats();
return NS_OK;
}
void SSLTokensCache::EvictIfNecessary() {
// kilobytes to bytes
uint32_t capacity = StaticPrefs::network_ssl_tokens_cache_capacity() << 10;
if (mCacheSize <= capacity) {
return;
}
LOG(("SSLTokensCache::EvictIfNecessary - evicting"));
mExpirationArray.Sort(ExpirationComparator());
while (mCacheSize > capacity && mExpirationArray.Length() > 0) {
if (NS_FAILED(RemoveLocked(mExpirationArray[0]->mKey))) {
MOZ_ASSERT(false,
"mExpirationArray and mTokenCacheRecords are out of sync!");
mExpirationArray.RemoveElementAt(0);
}
}
}
void SSLTokensCache::LogStats() {
LOG(("SSLTokensCache::LogStats [count=%zu, cacheSize=%u]",
mExpirationArray.Length(), mCacheSize));
}
size_t SSLTokensCache::SizeOfIncludingThis(
mozilla::MallocSizeOf mallocSizeOf) const {
size_t n = mallocSizeOf(this);
n += mTokenCacheRecords.ShallowSizeOfExcludingThis(mallocSizeOf);
n += mExpirationArray.ShallowSizeOfExcludingThis(mallocSizeOf);
for (uint32_t i = 0; i < mExpirationArray.Length(); ++i) {
n += mallocSizeOf(mExpirationArray[i]);
n += mExpirationArray[i]->mKey.SizeOfExcludingThisIfUnshared(mallocSizeOf);
n += mExpirationArray[i]->mToken.ShallowSizeOfExcludingThis(mallocSizeOf);
}
return n;
}
MOZ_DEFINE_MALLOC_SIZE_OF(SSLTokensCacheMallocSizeOf)
NS_IMETHODIMP
SSLTokensCache::CollectReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData, bool aAnonymize) {
StaticMutexAutoLock lock(sLock);
MOZ_COLLECT_REPORT("explicit/network/ssl-tokens-cache", KIND_HEAP,
UNITS_BYTES,
SizeOfIncludingThis(SSLTokensCacheMallocSizeOf),
"Memory used for the SSL tokens cache.");
return NS_OK;
}
// static
void SSLTokensCache::Clear() {
LOG(("SSLTokensCache::Clear"));
if (!StaticPrefs::network_ssl_tokens_cache_enabled() &&
!StaticPrefs::network_http_http3_enable_0rtt()) {
return;
}
StaticMutexAutoLock lock(sLock);
if (!gInstance) {
LOG((" service not initialized"));
return;
}
gInstance->mExpirationArray.Clear();
gInstance->mTokenCacheRecords.Clear();
gInstance->mCacheSize = 0;
}
} // namespace net
} // namespace mozilla