Skip to content

Commit 4ed598b

Browse files
bitWarriorbitWarriorthomas-bc
authored
PR 5040 - Fixed Potential DB file corruption on longer then shorter re-save DB file (#5080)
* Fixed Potential DB file corruption on longer then shorter re-save DB file * Add note for AI-generated test in PrmDbTester Added a note indicating that the test was generated by AI. --------- Co-authored-by: bitWarrior <contact.bitWarrior@proton.me> Co-authored-by: Thomas Boyer-Chammard <49786685+thomas-bc@users.noreply.github.com>
1 parent 8d2d73a commit 4ed598b

4 files changed

Lines changed: 140 additions & 1 deletion

File tree

Svc/PrmDb/PrmDbImpl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ void PrmDbImpl::PRM_SAVE_FILE_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
153153
Os::File paramFile;
154154
WorkingBuffer buff;
155155

156-
Os::File::Status stat = paramFile.open(this->m_fileName.toChar(), Os::File::OPEN_WRITE);
156+
Os::File::Status stat = paramFile.open(this->m_fileName.toChar(), Os::File::OPEN_WRITE, Os::File::OVERWRITE);
157157
if (stat != Os::File::OP_OK) {
158158
this->log_WARNING_HI_PrmFileWriteError(PrmWriteError::OPEN, 0, stat);
159159
this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);

Svc/PrmDb/test/ut/PrmDbTestMain.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,22 @@ TEST(ParameterDbTest, PrmFileLoadIllegalActions) {
271271
tester.runPrmFileLoadIllegal();
272272
}
273273

274+
TEST(ParameterDbTest, PrmShorterSaveDoesNotCorrupt) {
275+
Svc::PrmDbImpl impl("PrmDbImpl");
276+
277+
impl.init(10, 0);
278+
impl.configure("TestFile.prm");
279+
280+
Svc::PrmDbTester tester(impl);
281+
282+
tester.init();
283+
284+
// connect ports
285+
connectPorts(impl, tester);
286+
287+
tester.runShorterSaveDoesNotCorrupt();
288+
}
289+
274290
int main(int argc, char** argv) {
275291
::testing::InitGoogleTest(&argc, argv);
276292
return RUN_ALL_TESTS();

Svc/PrmDb/test/ut/PrmDbTester.cpp

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1317,6 +1317,128 @@ void PrmDbTester::runPrmFileLoadIllegal() {
13171317
ASSERT_EVENTS_PrmIdAdded_SIZE(1);
13181318
}
13191319

1320+
void PrmDbTester::runShorterSaveDoesNotCorrupt() {
1321+
// NOTE: test generated by AI
1322+
const FwPrmIdType ID_A = 0xA0;
1323+
const FwPrmIdType ID_B = 0xA1;
1324+
const FwPrmIdType ID_C = 0xA2;
1325+
const U32 INITIAL_VAL = 0x11223344;
1326+
const U32 UPDATED_VAL = 0x99;
1327+
1328+
Fw::ParamBuffer pBuff;
1329+
Fw::QueuedComponentBase::MsgDispatchStatus dispatchStat;
1330+
1331+
// -------------------------------------------------------------------
1332+
// Phase 1: populate with 3 parameters and save (the "longer" file)
1333+
// -------------------------------------------------------------------
1334+
this->m_impl.clearDb(PrmDbType::DB_ACTIVE);
1335+
this->clearEvents();
1336+
1337+
const FwPrmIdType ids[3] = {ID_A, ID_B, ID_C};
1338+
for (FwSizeType i = 0; i < 3; i++) {
1339+
pBuff.resetSer();
1340+
ASSERT_EQ(Fw::FW_SERIALIZE_OK, pBuff.serializeFrom(INITIAL_VAL));
1341+
this->invoke_to_setPrm(0, ids[i], pBuff);
1342+
dispatchStat = this->m_impl.doDispatch();
1343+
EXPECT_EQ(Fw::QueuedComponentBase::MSG_DISPATCH_OK, dispatchStat);
1344+
}
1345+
ASSERT_EVENTS_PrmIdAdded_SIZE(3);
1346+
1347+
Os::Stub::File::Test::StaticData::setWriteResult(m_io_data, sizeof m_io_data);
1348+
Os::Stub::File::Test::StaticData::setNextStatus(Os::File::OP_OK);
1349+
this->clearEvents();
1350+
this->clearHistory();
1351+
this->sendCmd_PRM_SAVE_FILE(0, 12);
1352+
dispatchStat = this->m_impl.doDispatch();
1353+
EXPECT_EQ(Fw::QueuedComponentBase::MSG_DISPATCH_OK, dispatchStat);
1354+
ASSERT_CMD_RESPONSE_SIZE(1);
1355+
ASSERT_CMD_RESPONSE(0, PrmDbImpl::OPCODE_PRM_SAVE_FILE, 12, Fw::CmdResponse::OK);
1356+
ASSERT_EVENTS_SIZE(1);
1357+
ASSERT_EVENTS_PrmFileSaveComplete_SIZE(1);
1358+
ASSERT_EVENTS_PrmFileSaveComplete(0, 3);
1359+
1360+
// Record how many bytes the 3-parameter save produced.
1361+
const FwSizeType longWriteSize = Os::Stub::File::Test::StaticData::data.pointer;
1362+
ASSERT_GT(longWriteSize, static_cast<FwSizeType>(0));
1363+
1364+
// -------------------------------------------------------------------
1365+
// Phase 2: replace with 1 parameter and save (the "shorter" file)
1366+
//
1367+
// m_io_data is intentionally NOT zeroed between saves so that the bytes
1368+
// at positions [shortWriteSize, longWriteSize) still hold the old
1369+
// content — exactly what a POSIX file looks like without O_TRUNC.
1370+
// -------------------------------------------------------------------
1371+
this->m_impl.clearDb(PrmDbType::DB_ACTIVE);
1372+
this->clearEvents();
1373+
1374+
pBuff.resetSer();
1375+
ASSERT_EQ(Fw::FW_SERIALIZE_OK, pBuff.serializeFrom(UPDATED_VAL));
1376+
this->invoke_to_setPrm(0, ID_A, pBuff);
1377+
dispatchStat = this->m_impl.doDispatch();
1378+
EXPECT_EQ(Fw::QueuedComponentBase::MSG_DISPATCH_OK, dispatchStat);
1379+
ASSERT_EVENTS_PrmIdAdded_SIZE(1);
1380+
1381+
// Reset the write pointer but leave the buffer contents intact.
1382+
Os::Stub::File::Test::StaticData::setWriteResult(m_io_data, sizeof m_io_data);
1383+
Os::Stub::File::Test::StaticData::setNextStatus(Os::File::OP_OK);
1384+
this->clearEvents();
1385+
this->clearHistory();
1386+
this->sendCmd_PRM_SAVE_FILE(0, 13);
1387+
dispatchStat = this->m_impl.doDispatch();
1388+
EXPECT_EQ(Fw::QueuedComponentBase::MSG_DISPATCH_OK, dispatchStat);
1389+
ASSERT_CMD_RESPONSE_SIZE(1);
1390+
ASSERT_CMD_RESPONSE(0, PrmDbImpl::OPCODE_PRM_SAVE_FILE, 13, Fw::CmdResponse::OK);
1391+
ASSERT_EVENTS_SIZE(1);
1392+
ASSERT_EVENTS_PrmFileSaveComplete_SIZE(1);
1393+
ASSERT_EVENTS_PrmFileSaveComplete(0, 1);
1394+
1395+
const FwSizeType shortWriteSize = Os::Stub::File::Test::StaticData::data.pointer;
1396+
1397+
// Confirm the precondition: the second save must be strictly smaller.
1398+
// If this fires, the test is misconfigured — the two saves must differ
1399+
// in total byte count for the truncation scenario to be meaningful.
1400+
ASSERT_LT(shortWriteSize, longWriteSize);
1401+
1402+
// -------------------------------------------------------------------
1403+
// Phase 3: reload using only the shorter image — must not emit
1404+
// PrmFileBadCrc and must return the updated value.
1405+
//
1406+
// We read back exactly shortWriteSize bytes (the content produced by
1407+
// the second save). This mirrors what the fixed implementation provides
1408+
// on a real filesystem: only the new, shorter content exists on disk
1409+
// because the file was properly truncated on open.
1410+
//
1411+
// Without the fix the read would span longWriteSize bytes (the full
1412+
// untruncated file), the CRC recomputed over those bytes would not match
1413+
// the header value, and PrmFileBadCrc would fire instead.
1414+
// -------------------------------------------------------------------
1415+
this->m_impl.clearDb(PrmDbType::DB_ACTIVE);
1416+
Os::Stub::File::Test::StaticData::setReadResult(m_io_data, shortWriteSize);
1417+
Os::Stub::File::Test::StaticData::setNextStatus(Os::File::OP_OK);
1418+
this->clearEvents();
1419+
1420+
this->m_impl.readParamFile();
1421+
1422+
// The load must succeed — no CRC mismatch event.
1423+
ASSERT_EVENTS_PrmFileBadCrc_SIZE(0);
1424+
ASSERT_EVENTS_SIZE(1);
1425+
ASSERT_EVENTS_PrmFileLoadComplete_SIZE(1);
1426+
ASSERT_EVENTS_PrmFileLoadComplete(0, "ACTIVE", 1, 1, 0);
1427+
1428+
// The updated (shorter) value must be present.
1429+
pBuff.resetSer();
1430+
EXPECT_EQ(Fw::ParamValid::VALID, this->invoke_to_getPrm(0, ID_A, pBuff).e);
1431+
U32 readVal = 0;
1432+
ASSERT_EQ(Fw::FW_SERIALIZE_OK, pBuff.deserializeTo(readVal));
1433+
EXPECT_EQ(UPDATED_VAL, readVal);
1434+
1435+
// ID_B and ID_C were not in the second save and must not be present.
1436+
pBuff.resetSer();
1437+
EXPECT_EQ(Fw::ParamValid::INVALID, this->invoke_to_getPrm(0, ID_B, pBuff).e);
1438+
pBuff.resetSer();
1439+
EXPECT_EQ(Fw::ParamValid::INVALID, this->invoke_to_getPrm(0, ID_C, pBuff).e);
1440+
}
1441+
13201442
PrmDbTester* PrmDbTester::PrmDbTestFile::s_tester = nullptr;
13211443

13221444
void PrmDbTester::PrmDbTestFile::setTester(Svc::PrmDbTester* tester) {

Svc/PrmDb/test/ut/PrmDbTester.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class PrmDbTester : public PrmDbGTestBase {
3232
void runPrmFileLoadNominal();
3333
void runPrmFileLoadWithErrors();
3434
void runPrmFileLoadIllegal();
35+
void runShorterSaveDoesNotCorrupt();
3536

3637
void runRefPrmFile();
3738

0 commit comments

Comments
 (0)