aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2023-10-06 10:27:47 +0200
committerGitHub <[email protected]>2023-10-06 10:27:47 +0200
commit3a09799e45e3460cdd9a54a73e9932f58eb50e56 (patch)
tree0e3734f14eab988b99f9ed3dcb6861022c442935 /src
parent0.2.26 (diff)
downloadzen-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.h13
-rw-r--r--src/zenserver/cache/cachedisklayer.cpp33
-rw-r--r--src/zenserver/cache/cacheshared.h4
-rw-r--r--src/zenserver/cache/httpstructuredcache.cpp3
-rw-r--r--src/zenserver/cache/structuredcachestore.cpp35
-rw-r--r--src/zenserver/cache/structuredcachestore.h2
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;