1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
|
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_BROWSER_INTEREST_GROUP_INTEREST_GROUP_PERMISSIONS_CACHE_H_
#define CONTENT_BROWSER_INTEREST_GROUP_INTEREST_GROUP_PERMISSIONS_CACHE_H_
#include "content/common/content_export.h"
#include <map>
#include <memory>
#include <tuple>
#include "base/containers/lru_cache.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "net/base/network_isolation_key.h"
#include "url/origin.h"
namespace content {
// Cache of per-origin join/leave interest group permissions, partitioned by
// NetworkIsolationKey and initiating frame origin. To minimize leaks across
// first party contexts, uses a separate LRU cache "shard" for each {frame
// origin, NetworkIsolationKey} pair. Each LRU cache has a maximum number of
// entries, but there's no global limit, to prevent leaks. The short entry
// lifetime and delay in creating entries, along with the LRU size cap, should
// keep memory usage low.
//
// In addition to the LRU-ness of the cache, individual entries are passively
// expired (i.e., no longer returned, but not proactively deleted) after
// `kCacheDuration` has passed since they were added to the cache. Cache shards
// are garbage collected on a timer, at some point after all their entries have
// expired. Expired entries and shards are also deleted on access, which isn't
// strictly necessary.
//
// Permissions are typically learned from .well-known fetches, and should be
// cached by the network process, depending on HTTP response headers, but
// caching it in the browser process results in much better performance for
// sites that want to join (or, more likely, leave) large numbers of
// cross-origin interest groups, due to the limit of outstanding joins/leaves in
// the renderer process, and overhead on accessing the network stack's HTTP
// cache.
class CONTENT_EXPORT InterestGroupPermissionsCache {
public:
// Cache duration for cache entries. Cache shards expire when all their
// individual entries expire, so this is also the expiration duration of
// cache shards, relative to last added entry.
static constexpr base::TimeDelta kCacheDuration = base::Minutes(1);
// How often expired cache shards are evicted. Expired but not-yet-evicted
// entries are not returned.
static constexpr base::TimeDelta kDeleteExpiredTimerDuration =
2 * kCacheDuration;
// The maximum number of cache entries for each cache shard. There is no
// global maximum, to prevent that from becoming a sidechannel.
static constexpr int kMaxCacheEntriesPerShard = 50;
// Permissions associated with an interest group origin.
struct Permissions {
bool can_join = false;
bool can_leave = false;
// Comparison operators are only useful for testing.
friend bool operator==(const Permissions&, const Permissions&) = default;
};
InterestGroupPermissionsCache();
InterestGroupPermissionsCache(InterestGroupPermissionsCache&) = delete;
~InterestGroupPermissionsCache();
InterestGroupPermissionsCache& operator=(
const InterestGroupPermissionsCache&) = delete;
// Retrieves unexpired cached Permissions that `interest_group_owner` has
// granted to `frame_origin`, learned in the context of
// `network_isolation_key`.
Permissions* GetPermissions(
const url::Origin& frame_origin,
const url::Origin& interest_group_owner,
const net::NetworkIsolationKey& network_isolation_key);
// Adds `permissions` to the cache as the set of permissions
// `interest_group_owner` has granted to `frame_origin`, learned in the
// context of `network_isolation_key`.
void CachePermissions(Permissions permissions,
const url::Origin& frame_origin,
const url::Origin& interest_group_owner,
const net::NetworkIsolationKey& network_isolation_key);
// Deletes all cache entries.
void Clear();
// Returns the number of non-deleted cache shards, to test cleanup logic. All
// values in a cache shard may be expired, since expired cache shards are only
// deleted on access and on a timer.
size_t cache_shards_for_testing() const;
private:
// An entry in the cache.
struct CacheEntry {
base::TimeTicks expiry;
Permissions permissions;
};
// An independent shard of the cache. Each shard must have no impact on the
// others, including when their entries are observably evicted.
struct CacheShard {
CacheShard();
CacheShard(CacheShard&&);
~CacheShard();
// Last expiration time of any of the shard's CacheEntry.
base::TimeTicks expiry;
// Map of interest group owner origins to CacheEntries. A std::unique_ptr
// wrapper is needed to make these movable, so they can be put in a
// std::map.
std::unique_ptr<base::LRUCache<url::Origin, CacheEntry>> cache;
};
// Key indicating which cache shard to use.
struct CacheShardKey {
bool operator<(const CacheShardKey& other) const {
return std::tie(frame_origin, network_isolation_key) <
std::tie(other.frame_origin, other.network_isolation_key);
}
url::Origin frame_origin;
net::NetworkIsolationKey network_isolation_key;
};
// Returns the specified cache shard, if it exists and is not expired. Deletes
// the cache shard if it exists but is expired.
CacheShard* FindShard(const url::Origin& frame_origin,
const net::NetworkIsolationKey& network_isolation_key,
base::TimeTicks now);
// Starts a timer to invoke DeleteExpired(), if there are any cache shards and
// the timer hasn't already been started.
void MaybeStartDeleteExpiredTimer();
// Walks through the cache shards, deleting any that have expired, and
// restarts timer if there are any shards left. Expired LRU entries are not
// deleted on an individual basis, except when accessed directly.
void DeleteExpired();
base::OneShotTimer delete_expired_timer_;
std::map<CacheShardKey, CacheShard> cache_shards_;
};
} // namespace content
#endif // CONTENT_BROWSER_INTEREST_GROUP_INTEREST_GROUP_PERMISSIONS_CACHE_H_
|