Skip to content

Commit 06662cf

Browse files
authored
Merge pull request #20301 from ghalliday/issue34805
HPCC-34805 Fix race condition on first access to a secret Reviewed-By: Jack Del Vecchio <Jack.DelVecchio@lexisnexisrisk.com> Merged-by: Gavin Halliday <gavin.halliday@lexisnexisrisk.com>
2 parents 96965b6 + 43bd800 commit 06662cf

2 files changed

Lines changed: 66 additions & 0 deletions

File tree

system/jlib/jsecrets.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,8 @@ class SecretCacheEntry : public CInterface
465465
// Is it time to check if there is a new value for this secret?
466466
bool needsRefresh(cache_timestamp now) const
467467
{
468+
if (!initialized)
469+
return true;
468470
cache_timestamp elapsed = (now - checkedTimestamp);
469471
return (elapsed > secretTimeoutNs);
470472
}
@@ -479,6 +481,7 @@ class SecretCacheEntry : public CInterface
479481
//Update the checked timestamp - so that we do not continually check for updates to secrets which
480482
//are stale because the vault or other source of values in inaccessible.
481483
//Keep using the last good value
484+
initialized = true;
482485
checkedTimestamp = now;
483486
if (accessed)
484487
accessedTimestamp = now;
@@ -490,6 +493,7 @@ class SecretCacheEntry : public CInterface
490493
private:
491494
void updateContents(IPropertyTree * _contents, cache_timestamp now, bool accessed)
492495
{
496+
initialized = true;
493497
contents.set(_contents);
494498
updateHash();
495499
contentTimestamp = now;
@@ -512,6 +516,7 @@ class SecretCacheEntry : public CInterface
512516
cache_timestamp accessedTimestamp = 0; // When was this secret last accessed?
513517
cache_timestamp checkedTimestamp = 0; // When was this last checked for updates?
514518
unsigned contentHash = 0;
519+
bool initialized = false;
515520
};
516521

517522

testing/unittests/jlibtests.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4519,6 +4519,7 @@ class JLibSecretsTest : public CppUnit::TestFixture
45194519
public:
45204520
CPPUNIT_TEST_SUITE(JLibSecretsTest);
45214521
CPPUNIT_TEST(setup);
4522+
CPPUNIT_TEST(testConcurrentRead);
45224523
CPPUNIT_TEST(testUpdate1);
45234524
CPPUNIT_TEST(testUpdate2);
45244525
CPPUNIT_TEST(testBackgroundUpdate);
@@ -4789,6 +4790,66 @@ class JLibSecretsTest : public CppUnit::TestFixture
47894790
stopSecretUpdateThread();
47904791
}
47914792

4793+
class SecretReader : public Thread
4794+
{
4795+
public:
4796+
SecretReader(Semaphore & _startSem) : startSem(_startSem) {}
4797+
4798+
virtual int run()
4799+
{
4800+
startSem.wait();
4801+
try
4802+
{
4803+
getSecretValue(secret, "testing", "concurrent", "value", true);
4804+
}
4805+
catch (IException * e)
4806+
{
4807+
DBGLOG("Failed to read secret");
4808+
error.setown(e);
4809+
}
4810+
return 0;
4811+
}
4812+
4813+
public:
4814+
Semaphore & startSem;
4815+
StringBuffer secret;
4816+
Owned<IException> error;
4817+
};
4818+
4819+
void testConcurrentRead()
4820+
{
4821+
initPath(); // secretRoot needs to be called for each test
4822+
4823+
writeTestingSecret("concurrent", "value", "hello");
4824+
4825+
unsigned numReaders = 5;
4826+
Semaphore startSem;
4827+
CIArrayOf<SecretReader> readers;
4828+
4829+
for (unsigned i = 0; i < numReaders; i++)
4830+
{
4831+
SecretReader * reader = new SecretReader(startSem);
4832+
readers.append(*reader);
4833+
reader->start(true);
4834+
}
4835+
4836+
startSem.signal(numReaders);
4837+
4838+
for (unsigned i = 0; i < numReaders; i++)
4839+
readers.item(i).join();
4840+
4841+
for (unsigned i = 0; i < numReaders; i++)
4842+
{
4843+
IException * e = readers.item(i).error;
4844+
if (e)
4845+
{
4846+
StringBuffer msg;
4847+
e->errorMessage(msg);
4848+
CPPUNIT_FAIL(msg.str());
4849+
}
4850+
}
4851+
}
4852+
47924853
void testKeyEncoding()
47934854
{
47944855
for (auto category : { "abc", "def" })

0 commit comments

Comments
 (0)