diff options
| author | Stefan Boberg <[email protected]> | 2023-10-06 10:27:47 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-10-06 10:27:47 +0200 |
| commit | 3a09799e45e3460cdd9a54a73e9932f58eb50e56 (patch) | |
| tree | 0e3734f14eab988b99f9ed3dcb6861022c442935 /src | |
| parent | 0.2.26 (diff) | |
| download | zen-3a09799e45e3460cdd9a54a73e9932f58eb50e56.tar.xz zen-3a09799e45e3460cdd9a54a73e9932f58eb50e56.zip | |
reject known bad bucket names in structured cache (#452)v0.2.27-pre0
* added string_view helpers for ParseHexBytes/ParseHexNumber
* reject known bad buckets in structured cache put handler (32-character hex bucket names are rejected)
* also added bucket rejection logic to bucket discovery
* added rejected_writes stat to HttpStructuredCache
Diffstat (limited to 'src')
| -rw-r--r-- | src/zencore/include/zencore/string.h | 13 | ||||
| -rw-r--r-- | src/zenserver/cache/cachedisklayer.cpp | 33 | ||||
| -rw-r--r-- | src/zenserver/cache/cacheshared.h | 4 | ||||
| -rw-r--r-- | src/zenserver/cache/httpstructuredcache.cpp | 3 | ||||
| -rw-r--r-- | src/zenserver/cache/structuredcachestore.cpp | 35 | ||||
| -rw-r--r-- | src/zenserver/cache/structuredcachestore.h | 2 |
6 files changed, 82 insertions, 8 deletions
diff --git a/src/zencore/include/zencore/string.h b/src/zencore/include/zencore/string.h index b26407005..2319d7ade 100644 --- a/src/zencore/include/zencore/string.h +++ b/src/zencore/include/zencore/string.h @@ -556,6 +556,12 @@ ParseHexBytes(const char* InputString, size_t CharacterCount, uint8_t* OutPtr) return (allBits & 0x80) == 0; } +inline bool +ParseHexBytes(std::string_view InputString, uint8_t* OutPtr) +{ + return ParseHexBytes(InputString.data(), InputString.size(), OutPtr); +} + inline void ToHexBytes(const uint8_t* InputData, size_t ByteCount, char* OutString) { @@ -575,6 +581,7 @@ ParseHexNumber(const char* InputString, size_t CharacterCount, uint8_t* OutPtr) uint8_t allBits = 0; + // This assumes little-endian InputString += CharacterCount; while (CharacterCount) { @@ -593,6 +600,12 @@ ParseHexNumber(const char* InputString, size_t CharacterCount, uint8_t* OutPtr) return (allBits & 0x80) == 0; } +inline bool +ParseHexNumber(std::string_view InputString, uint8_t* OutPtr) +{ + return ParseHexNumber(InputString.data(), InputString.size(), OutPtr); +} + inline void ToHexNumber(const uint8_t* InputData, size_t ByteCount, char* OutString) { diff --git a/src/zenserver/cache/cachedisklayer.cpp b/src/zenserver/cache/cachedisklayer.cpp index 894676d6a..177d37aa9 100644 --- a/src/zenserver/cache/cachedisklayer.cpp +++ b/src/zenserver/cache/cachedisklayer.cpp @@ -2145,17 +2145,26 @@ ZenCacheDiskLayer::DiscoverBuckets() // Initialize buckets + std::vector<std::filesystem::path> BadBucketDirectories; + RwLock::ExclusiveLockScope _(m_Lock); for (const std::filesystem::path& BucketPath : DirContent.Directories) { const std::string BucketName = PathToUtf8(BucketPath.stem()); - // New bucket needs to be created + if (auto It = m_Buckets.find(BucketName); It != m_Buckets.end()) { continue; } + if (IsKnownBadBucketName(BucketName)) + { + BadBucketDirectories.push_back(BucketPath); + + continue; + } + auto InsertResult = m_Buckets.emplace(BucketName, std::make_unique<CacheBucket>(BucketName)); CacheBucket& Bucket = *InsertResult.first->second; @@ -2176,6 +2185,28 @@ ZenCacheDiskLayer::DiscoverBuckets() } ZEN_INFO("Discovered bucket '{}'", BucketName); } + + for (const std::filesystem::path& BadBucketPath : BadBucketDirectories) + { + bool IsOk = false; + + try + { + IsOk = DeleteDirectories(BadBucketPath); + } + catch (std::exception&) + { + } + + if (IsOk) + { + ZEN_INFO("found bad bucket at '{}', deleted contents", BadBucketPath); + } + else + { + ZEN_WARN("bad bucket delete failed for '{}'", BadBucketPath); + } + } } bool diff --git a/src/zenserver/cache/cacheshared.h b/src/zenserver/cache/cacheshared.h index 84330be59..e3e8a2f84 100644 --- a/src/zenserver/cache/cacheshared.h +++ b/src/zenserver/cache/cacheshared.h @@ -57,6 +57,8 @@ struct CacheValueDetails std::unordered_map<std::string, NamespaceDetails> Namespaces; }; +bool IsKnownBadBucketName(std::string_view BucketName); + ////////////////////////////////////////////////////////////////////////// // This store the access time as seconds since epoch internally in a 32-bit value giving is a range of 136 years since epoch @@ -95,4 +97,4 @@ private: std::atomic_uint32_t SecondsSinceEpoch; }; -} // namespace zen
\ No newline at end of file +} // namespace zen diff --git a/src/zenserver/cache/httpstructuredcache.cpp b/src/zenserver/cache/httpstructuredcache.cpp index 4a3f5f269..d499ccd9f 100644 --- a/src/zenserver/cache/httpstructuredcache.cpp +++ b/src/zenserver/cache/httpstructuredcache.cpp @@ -3299,7 +3299,8 @@ HttpStructuredCacheService::HandleStatsRequest(HttpServerRequest& Request) if (ShowCacheStoreStats) { Cbo.BeginObject("store"); - Cbo << "hits" << CacheStoreStats.HitCount << "misses" << CacheStoreStats.MissCount << "writes" << CacheStoreStats.WriteCount; + Cbo << "hits" << CacheStoreStats.HitCount << "misses" << CacheStoreStats.MissCount << "writes" << CacheStoreStats.WriteCount + << "rejected_writes" << CacheStoreStats.RejectedWriteCount; const uint64_t StoreTotal = CacheStoreStats.HitCount + CacheStoreStats.MissCount; Cbo << "hit_ratio" << (StoreTotal > 0 ? (double(CacheStoreStats.HitCount) / double(StoreTotal)) : 0.0); EmitSnapshot("read", CacheStoreStats.GetOps, Cbo); diff --git a/src/zenserver/cache/structuredcachestore.cpp b/src/zenserver/cache/structuredcachestore.cpp index 1b6eeca3a..f7960f498 100644 --- a/src/zenserver/cache/structuredcachestore.cpp +++ b/src/zenserver/cache/structuredcachestore.cpp @@ -43,6 +43,21 @@ ZEN_THIRD_PARTY_INCLUDES_END namespace zen { +bool +IsKnownBadBucketName(std::string_view Bucket) +{ + if (Bucket.size() == 32) + { + uint8_t BucketHex[16]; + if (ParseHexBytes(Bucket, BucketHex)) + { + return true; + } + } + + return false; +} + ZenCacheNamespace::ZenCacheNamespace(GcManager& Gc, JobQueue& JobQueue, const std::filesystem::path& RootDir, @@ -459,6 +474,14 @@ ZenCacheStore::Put(const CacheRequestContext& Context, metrics::RequestStats::Scope $(m_PutOps, Value.Value.GetSize()); + // Ad hoc rejection of known bad usage patterns for DDC bucket names + + if (IsKnownBadBucketName(Bucket)) + { + m_RejectedWriteCount++; + return; + } + if (m_WriteLogEnabled) { ZEN_TRACE_CPU("Z$::Get::WriteLog"); @@ -485,6 +508,7 @@ ZenCacheStore::Put(const CacheRequestContext& Context, m_WriteCount++; return; } + ZEN_WARN("request for unknown namespace '{}' in ZenCacheStore::Put [{}] bucket '{}', key '{}'", Context, Namespace, @@ -662,11 +686,12 @@ ZenCacheStore::StorageSize() const ZenCacheStore::CacheStoreStats ZenCacheStore::Stats() { - ZenCacheStore::CacheStoreStats Result{.HitCount = m_HitCount, - .MissCount = m_MissCount, - .WriteCount = m_WriteCount, - .PutOps = m_PutOps.Snapshot(), - .GetOps = m_GetOps.Snapshot()}; + ZenCacheStore::CacheStoreStats Result{.HitCount = m_HitCount, + .MissCount = m_MissCount, + .WriteCount = m_WriteCount, + .RejectedWriteCount = m_RejectedWriteCount, + .PutOps = m_PutOps.Snapshot(), + .GetOps = m_GetOps.Snapshot()}; IterateNamespaces([&](std::string_view NamespaceName, ZenCacheNamespace& Store) { Result.NamespaceStats.emplace_back(NamedNamespaceStats{.NamespaceName = std::string(NamespaceName), .Stats = Store.Stats()}); }); diff --git a/src/zenserver/cache/structuredcachestore.h b/src/zenserver/cache/structuredcachestore.h index 0c87ecc22..1602b43ad 100644 --- a/src/zenserver/cache/structuredcachestore.h +++ b/src/zenserver/cache/structuredcachestore.h @@ -172,6 +172,7 @@ public: uint64_t HitCount; uint64_t MissCount; uint64_t WriteCount; + uint64_t RejectedWriteCount; metrics::RequestStatsSnapshot PutOps; metrics::RequestStatsSnapshot GetOps; std::vector<NamedNamespaceStats> NamespaceStats; @@ -233,6 +234,7 @@ private: std::atomic<uint64_t> m_HitCount{}; std::atomic<uint64_t> m_MissCount{}; std::atomic<uint64_t> m_WriteCount{}; + std::atomic<uint64_t> m_RejectedWriteCount{}; metrics::RequestStats m_PutOps; metrics::RequestStats m_GetOps; |