GCC Code Coverage Report


Directory: ./
File: Svc/FpySequencer/FpySequencerValidationState.cpp
Date: 2026-11-03 22:10:14
Exec Total Coverage
Lines: 0 134 0.0%
Functions: 0 7 0.0%
Branches: 0 131 0.0%

Line Branch Exec Source
1 #include <Utils/Hash/Hash.hpp>
2 #include "Svc/FpySequencer/FppConstantsAc.hpp"
3 #include "Svc/FpySequencer/FpySequencer.hpp"
4
5 namespace Svc {
6
7 ✗ void FpySequencer::allocateBuffer(FwEnumStoreType identifier, Fw::MemAllocator& allocator, FwSizeType bytes) {
8 // if this assertion fails, you aren't allocating enough bytes for the
9 // FpySequencer. this is because you must have a buffer big enough to fit the
10 // header of a sequence
11 ✗ FW_ASSERT(bytes >= Fpy::Header::SERIALIZED_SIZE, static_cast<FwAssertArgType>(bytes));
12 ✗ FwSizeType originalBytes = bytes;
13 ✗ bool recoverable = false;
14 ✗ this->m_allocatorId = identifier;
15 ✗ U8* allocatedMemory = static_cast<U8*>(allocator.allocate(identifier, bytes, recoverable));
16 // if this fails, unable to allocate the requested amount of money
17 ✗ FW_ASSERT(bytes >= originalBytes, static_cast<FwAssertArgType>(bytes));
18 ✗ this->m_sequenceBuffer.setExtBuffer(allocatedMemory, bytes);
19 ✗ }
20
21 ✗ void FpySequencer::deallocateBuffer(Fw::MemAllocator& allocator) {
22 ✗ allocator.deallocate(this->m_allocatorId, this->m_sequenceBuffer.getBuffAddr());
23 ✗ this->m_sequenceBuffer.clear();
24 ✗ }
25
26 // loads the sequence in memory, and does header/crc/integrity checks.
27 // return SUCCESS if sequence is valid, FAILURE otherwise
28 ✗ Fw::Success FpySequencer::validate() {
29 ✗ if (this->m_sequenceFilePath.length() == 0) {
30 ✗ this->log_WARNING_HI_FileOpenError(this->m_sequenceFilePath, static_cast<I32>(Os::File::INVALID_ARGUMENT));
31 ✗ return Fw::Success::FAILURE;
32 }
33
34 ✗ if (!this->checkSandbox(this->m_sequenceFilePath)) {
35 ✗ return Fw::Success::FAILURE;
36 }
37
38 // crc needs to be initialized with a particular value
39 // for the calculation to work
40 ✗ this->m_computedCRC.init();
41
42 ✗ Os::File sequenceFile;
43 ✗ Os::File::Status openStatus = sequenceFile.open(this->m_sequenceFilePath.toChar(), Os::File::OPEN_READ);
44
45 ✗ if (openStatus != Os::File::Status::OP_OK) {
46 ✗ this->log_WARNING_HI_FileOpenError(this->m_sequenceFilePath, static_cast<I32>(openStatus));
47 ✗ return Fw::Success::FAILURE;
48 }
49
50 Fw::Success readStatus =
51 ✗ this->readBytes(sequenceFile, Fpy::Header::SERIALIZED_SIZE, FpySequencer_FileReadStage::HEADER);
52
53 ✗ if (readStatus != Fw::Success::SUCCESS) {
54 ✗ return Fw::Success::FAILURE;
55 }
56
57 ✗ readStatus = this->readHeader();
58
59 ✗ if (readStatus != Fw::Success::SUCCESS) {
60 ✗ return Fw::Success::FAILURE;
61 }
62
63 readStatus =
64 ✗ readBytes(sequenceFile, this->m_sequenceObj.get_header().get_bodySize(), FpySequencer_FileReadStage::BODY);
65
66 ✗ if (readStatus != Fw::Success::SUCCESS) {
67 ✗ return Fw::Success::FAILURE;
68 }
69
70 ✗ readStatus = this->readBody();
71
72 ✗ if (readStatus != Fw::Success::SUCCESS) {
73 ✗ return Fw::Success::FAILURE;
74 }
75
76 // read footer bytes but don't include in CRC
77 ✗ readStatus = this->readBytes(sequenceFile, Fpy::Footer::SERIALIZED_SIZE, FpySequencer_FileReadStage::FOOTER, false);
78
79 ✗ if (readStatus != Fw::Success::SUCCESS) {
80 ✗ return Fw::Success::FAILURE;
81 }
82
83 ✗ readStatus = this->readFooter();
84
85 ✗ if (readStatus != Fw::Success::SUCCESS) {
86 ✗ return Fw::Success::FAILURE;
87 }
88
89 // make sure we're at EOF. The size() and position() OS calls can fail
90 // on filesystem errors or if the file was concurrently modified by
91 // another FileManager command (e.g. RemoveFile or AppendFile) between
92 // the file open and this point. Treat the failure as a validation
93 // failure rather than aborting the FSW process.
94 FwSizeType sequenceFileSize;
95 ✗ Os::File::Status sizeStatus = sequenceFile.size(sequenceFileSize);
96 ✗ if (sizeStatus != Os::File::Status::OP_OK) {
97 ✗ this->log_WARNING_HI_FileApiError(this->m_sequenceFilePath, static_cast<I32>(sizeStatus));
98 ✗ return Fw::Success::FAILURE;
99 }
100
101 FwSizeType sequenceFilePosition;
102 ✗ Os::File::Status positionStatus = sequenceFile.position(sequenceFilePosition);
103 ✗ if (positionStatus != Os::File::Status::OP_OK) {
104 ✗ this->log_WARNING_HI_FileApiError(this->m_sequenceFilePath, static_cast<I32>(positionStatus));
105 ✗ return Fw::Success::FAILURE;
106 }
107
108 ✗ if (sequenceFileSize != sequenceFilePosition) {
109 ✗ this->log_WARNING_HI_ExtraBytesInSequence(static_cast<FwSizeType>(sequenceFileSize - sequenceFilePosition));
110 ✗ return Fw::Success::FAILURE;
111 }
112
113 ✗ if (this->m_sequenceArgs.get_size() > Fpy::MAX_STACK_SIZE) {
114 ✗ this->log_WARNING_HI_ArgTotalSizeExceedsStackLimit(
115 ✗ static_cast<Fpy::StackSizeType>(this->m_sequenceArgs.get_size()));
116 ✗ return Fw::Success::FAILURE;
117 }
118
119 // The argument size arrives separately from the argument buffer, so it can describe more bytes
120 // than that buffer holds. Reject before anything reads the buffer by that size.
121 ✗ const FwSizeType argCapacity = static_cast<FwSizeType>(sizeof(this->m_sequenceArgs.get_buffer()));
122 ✗ if (this->m_sequenceArgs.get_size() > argCapacity) {
123 ✗ this->log_WARNING_HI_ArgSizeExceedsCapacity(this->m_sequenceArgs.get_size(), argCapacity);
124 ✗ return Fw::Success::FAILURE;
125 }
126
127 ✗ return Fw::Success::SUCCESS;
128 ✗ }
129
130 // reads and validates the header from the m_sequenceBuffer
131 // return SUCCESS if sequence is valid, FAILURE otherwise
132 ✗ Fw::Success FpySequencer::readHeader() {
133 // deser header
134 ✗ Fw::SerializeStatus deserStatus = this->m_sequenceBuffer.deserializeTo(this->m_sequenceObj.get_header());
135 ✗ if (deserStatus != Fw::SerializeStatus::FW_SERIALIZE_OK) {
136 ✗ this->log_WARNING_HI_FileReadDeserializeError(
137 FpySequencer_FileReadStage::HEADER, this->m_sequenceFilePath, static_cast<I32>(deserStatus),
138 this->m_sequenceBuffer.getDeserializeSizeLeft(), this->m_sequenceBuffer.getSize());
139 ✗ return Fw::Success::FAILURE;
140 }
141
142 // check matching schema version
143 ✗ if (this->m_sequenceObj.get_header().get_schemaVersion() != Fpy::SCHEMA_VERSION) {
144 ✗ this->log_WARNING_HI_WrongSchemaVersion(Fpy::SCHEMA_VERSION,
145 ✗ this->m_sequenceObj.get_header().get_schemaVersion());
146 ✗ return Fw::Success::FAILURE;
147 }
148
149 ✗ if (this->m_sequenceObj.get_header().get_argumentCount() > Fpy::MAX_SEQUENCE_ARG_COUNT) {
150 ✗ this->log_WARNING_HI_TooManySequenceArgs(m_sequenceObj.get_header().get_argumentCount(),
151 Fpy::MAX_SEQUENCE_ARG_COUNT);
152 ✗ return Fw::Success::FAILURE;
153 }
154
155 ✗ if (this->m_sequenceObj.get_header().get_statementCount() > Fpy::MAX_SEQUENCE_STATEMENT_COUNT) {
156 ✗ this->log_WARNING_HI_TooManySequenceDirectives(this->m_sequenceObj.get_header().get_statementCount(),
157 Fpy::MAX_SEQUENCE_STATEMENT_COUNT);
158 ✗ return Fw::Success::FAILURE;
159 }
160 ✗ return Fw::Success::SUCCESS;
161 }
162
163 // reads and validates the body from the m_sequenceBuffer
164 // return SUCCESS if sequence is valid, FAILURE otherwise
165 ✗ Fw::Success FpySequencer::readBody() {
166 Fw::SerializeStatus deserStatus;
167
168 ✗ const U8 argumentCount = this->m_sequenceObj.get_header().get_argumentCount();
169 ✗ this->m_totalExpectedArgSize = 0;
170
171 // deser arguments
172 // Read and deserialize each arg_spec incrementally since they're variable-length
173 ✗ for (U8 i = 0; i < argumentCount; i++) {
174 ✗ Fpy::ArgSpec& argSpec = this->m_sequenceObj.get_args()[i];
175 ✗ deserStatus = this->m_sequenceBuffer.deserializeTo(argSpec);
176 ✗ if (deserStatus != Fw::SerializeStatus::FW_SERIALIZE_OK) {
177 ✗ this->log_WARNING_HI_FileReadDeserializeError(
178 FpySequencer_FileReadStage::BODY, this->m_sequenceFilePath, static_cast<I32>(deserStatus),
179 this->m_sequenceBuffer.getDeserializeSizeLeft(), this->m_sequenceBuffer.getSize());
180 ✗ return Fw::Success::FAILURE;
181 }
182
183 ✗ m_totalExpectedArgSize += argSpec.get_argSize();
184 }
185
186 // Check for overflow
187 ✗ if (m_totalExpectedArgSize > Fpy::MAX_STACK_SIZE) {
188 ✗ this->log_WARNING_HI_ArgTotalSizeExceedsStackLimit(m_totalExpectedArgSize);
189 ✗ return Fw::Success::FAILURE;
190 }
191
192 // Validate total argument size
193 ✗ if (this->m_totalExpectedArgSize != this->m_sequenceArgs.get_size()) {
194 ✗ this->log_WARNING_HI_ArgSizeMismatch(this->m_totalExpectedArgSize, this->m_sequenceArgs.get_size(),
195 this->m_sequenceFilePath);
196 ✗ return Fw::Success::FAILURE;
197 }
198
199 // deser statements
200 ✗ const U16 statementCount = this->m_sequenceObj.get_header().get_statementCount();
201 ✗ for (U16 statementIdx = 0; statementIdx < statementCount; statementIdx++) {
202 // deser statement
203 ✗ deserStatus = this->m_sequenceBuffer.deserializeTo(this->m_sequenceObj.get_statements()[statementIdx]);
204 ✗ if (deserStatus != Fw::FW_SERIALIZE_OK) {
205 ✗ this->log_WARNING_HI_FileReadDeserializeError(
206 FpySequencer_FileReadStage::BODY, this->m_sequenceFilePath, static_cast<I32>(deserStatus),
207 this->m_sequenceBuffer.getDeserializeSizeLeft(), this->m_sequenceBuffer.getSize());
208 ✗ return Fw::Success::FAILURE;
209 }
210 }
211 ✗ return Fw::Success::SUCCESS;
212 }
213
214 // reads and validates the footer from the m_sequenceBuffer
215 // return SUCCESS if sequence is valid, FAILURE otherwise
216 ✗ Fw::Success FpySequencer::readFooter() {
217 ✗ Fw::SerializeStatus deserStatus = this->m_sequenceBuffer.deserializeTo(this->m_sequenceObj.get_footer());
218 ✗ if (deserStatus != Fw::FW_SERIALIZE_OK) {
219 ✗ this->log_WARNING_HI_FileReadDeserializeError(
220 FpySequencer_FileReadStage::FOOTER, this->m_sequenceFilePath, static_cast<I32>(deserStatus),
221 this->m_sequenceBuffer.getDeserializeSizeLeft(), this->m_sequenceBuffer.getSize());
222 ✗ return Fw::Success::FAILURE;
223 }
224
225 // need this for some reason to "finalize" the crc TODO get an explanation on this
226 ✗ U32 computedCRC = 0;
227 ✗ this->m_computedCRC.finalize(computedCRC);
228
229 ✗ if (computedCRC != this->m_sequenceObj.get_footer().get_crc()) {
230 ✗ this->log_WARNING_HI_WrongCRC(this->m_sequenceObj.get_footer().get_crc(), computedCRC);
231 ✗ return Fw::Success::FAILURE;
232 }
233
234 ✗ return Fw::Success::SUCCESS;
235 }
236
237 // reads some bytes from the open file into the m_sequenceBuffer.
238 // return success if successful
239 ✗ Fw::Success FpySequencer::readBytes(Os::File& file,
240 FwSizeType expectedReadLen,
241 const FpySequencer_FileReadStage& readStage,
242 bool updateCrc) {
243 ✗ FW_ASSERT(file.isOpen());
244 // this has to be declared a var because file.read must take a ref
245 ✗ FwSizeType actualReadLen = expectedReadLen;
246
247 ✗ const FwSizeType capacity = this->m_sequenceBuffer.getCapacity();
248
249 // if this fails, then you need to give the sequencer more buffer memory. pass in a bigger number
250 // to fpySeq.allocateBuffer(). This is usually done in topology setup CPP
251 ✗ if (expectedReadLen > capacity) {
252 ✗ this->log_WARNING_HI_InsufficientBufferSpace(static_cast<U64>(capacity), this->m_sequenceFilePath);
253 ✗ return Fw::Success::FAILURE;
254 }
255
256 ✗ Os::File::Status fileStatus = file.read(this->m_sequenceBuffer.getBuffAddr(), actualReadLen);
257
258 ✗ if (fileStatus != Os::File::OP_OK) {
259 ✗ this->log_WARNING_HI_FileReadError(readStage, this->m_sequenceFilePath, static_cast<I32>(fileStatus));
260 ✗ return Fw::Success::FAILURE;
261 }
262
263 ✗ if (actualReadLen < expectedReadLen) {
264 ✗ this->log_WARNING_HI_EndOfFileError(readStage, this->m_sequenceFilePath);
265 ✗ return Fw::Success::FAILURE;
266 }
267
268 // should probably fail if we read in MORE bytes than we ask for
269 ✗ FW_ASSERT(expectedReadLen == actualReadLen, static_cast<FwAssertArgType>(expectedReadLen),
270 static_cast<FwAssertArgType>(actualReadLen));
271
272 Fw::SerializeStatus serializeStatus =
273 ✗ this->m_sequenceBuffer.setBuffLen(static_cast<Fw::Serializable::SizeType>(expectedReadLen));
274 ✗ FW_ASSERT(serializeStatus == Fw::FW_SERIALIZE_OK, serializeStatus);
275
276 ✗ if (updateCrc) {
277 ✗ this->m_computedCRC.update(this->m_sequenceBuffer.getBuffAddr(), expectedReadLen);
278 }
279
280 ✗ return Fw::Success::SUCCESS;
281 }
282
283 } // namespace Svc
284