fram_utils: Improve parsing to handle truncated HSM FRAM log reads
The Cavium HSM FRU parser can fail or produce incorrect data if the
FRAM log read is truncated (i.e., ends mid-line without a newline).
If a truncated line contains partial key information like
"Board Information:EB", this could overwrite a complete value like
"Board Information:EBB6100..." read from an earlier line.
This change updates ParseFramData to prevent partial data from
overwriting complete data. Since a line not terminated by a newline is likely incomplete, it is safer and simpler to discard it entirely rather than attempting to parse potentially partial data. This commit refactors `ParseFramData` to
ignore the final line segment if the input data does not end with a
newline character.This resolves issues where incorrect data was parsed due to
truncation.
In `fru_scanner_fram_test.cc`:
* Updated tests with new coverage
* Updated test suite comments to explain test behavior
In `fru_scanner_fram_test.cc`:
* `SuccessfulPartialRead` previously read 160 bytes, which ended in a
truncated line. Because this line is now ignored, downstream
validation failed. The test is updated to read 166 bytes, ensuring
the partial data ends in a newline, allowing validation to succeed.
Tested: Unit test pass
PiperOrigin-RevId: 822751274
Change-Id: Id79a339e87dc152929c939414ba194b02dac0817
diff --git a/tlbmc/utils/fram_utils.cc b/tlbmc/utils/fram_utils.cc
index 6570b35..166f80f 100644
--- a/tlbmc/utils/fram_utils.cc
+++ b/tlbmc/utils/fram_utils.cc
@@ -1,5 +1,6 @@
#include "tlbmc/utils/fram_utils.h"
+#include <cstddef>
#include <cstdint>
#include <memory>
#include <string>
@@ -58,11 +59,20 @@
}
}
+ const bool last_line_is_complete =
+ !content.empty() && content.back() == '\n';
+
// Split the content into lines.
std::vector<absl::string_view> lines = absl::StrSplit(content, '\n');
+ // If the last line is not complete, it's likely truncated, so discard it.
+ if (!last_line_is_complete && !lines.empty()) {
+ lines.pop_back();
+ }
+
// Iterate through each line to find relevant information.
- for (const auto& line : lines) {
+ for (size_t i = 0; i < lines.size(); ++i) {
+ absl::string_view line = lines[i];
absl::string_view trimmed_line = absl::StripAsciiWhitespace(line);
// We only care about lines that start with "INFO:".
if (!absl::StartsWith(trimmed_line, "INFO:")) {
@@ -83,9 +93,12 @@
if (module == "Linux") {
// Handle Linux:Board Serial Number: <value>
if (absl::StartsWith(message, kBoardSerialNumberKey)) {
- parsed_data.board_serial_number =
+ const std::string board_serial_number =
std::string(absl::StripAsciiWhitespace(
message.substr(kBoardSerialNumberKey.length())));
+ if (!board_serial_number.empty()) {
+ parsed_data.board_serial_number = board_serial_number;
+ }
}
continue;
}
@@ -93,8 +106,12 @@
if (module == "Bootloader") {
// Handle Bootloader:Board Information: <value>
if (absl::StartsWith(message, kBoardInfoKey)) {
- parsed_data.board_info = std::string(
+ const std::string board_info_str = std::string(
absl::StripAsciiWhitespace(message.substr(kBoardInfoKey.length())));
+
+ if (!board_info_str.empty()) {
+ parsed_data.board_info = board_info_str;
+ }
// Attempt to extract the product name from the Board Information.
// This assumes the product name is the first word in the Board
// Information string. This is based on the observed format:
@@ -104,8 +121,8 @@
// Example: "EBB6100 board revision major:1, minor:0, serial #: unknown"
// Here, "EBB6100" is the product name.
std::vector<std::string> board_parts =
- absl::StrSplit(parsed_data.board_info, ' ');
- if (!board_parts.empty()) {
+ absl::StrSplit(parsed_data.board_info, ' ', absl::SkipWhitespace());
+ if (!board_parts.empty() && !board_parts[0].empty()) {
parsed_data.board_product_name = board_parts[0];
}
}