-
-
Notifications
You must be signed in to change notification settings - Fork 551
refactor(cpn): replace strcpy with snprintf in mdichild.cpp as defensive hardening #7437
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| #include <gtest/gtest.h> | ||
| #include <string> | ||
| #include <vector> | ||
| #include <cstring> | ||
|
|
||
| // Include the model data structures used by mdichild.cpp | ||
| #include "eeprominterface.h" | ||
| #include "radiodata.h" | ||
|
|
||
| // Invariant: filename field in ModelData must never be overflowed; | ||
| // the length written must not exceed sizeof(ModelData::filename) - 1. | ||
|
|
||
| class FilenameBufferSecurityTest : public ::testing::TestWithParam<std::string> {}; | ||
|
|
||
| TEST_P(FilenameBufferSecurityTest, FilenameNeverExceedsBufferBounds) { | ||
| // Invariant: Buffer reads/writes never exceed the declared length of filename[] | ||
| std::string payload = GetParam(); | ||
|
|
||
| ModelData model; | ||
| memset(&model, 0, sizeof(model)); | ||
|
|
||
| constexpr size_t FILENAME_BUF_SIZE = sizeof(model.filename); | ||
|
|
||
| // Simulate what the vulnerable code does, but assert the invariant holds: | ||
| // the destination buffer must be large enough to hold the source string. | ||
| // If payload length >= FILENAME_BUF_SIZE, a strcpy would overflow. | ||
| bool wouldOverflow = (payload.size() >= FILENAME_BUF_SIZE); | ||
|
|
||
| // The invariant: production code must NOT allow overflow. | ||
| // We assert that either the payload fits, or the code must truncate/reject it. | ||
| if (!wouldOverflow) { | ||
| // Safe case: copy is within bounds | ||
| strncpy(model.filename, payload.c_str(), FILENAME_BUF_SIZE - 1); | ||
| model.filename[FILENAME_BUF_SIZE - 1] = '\0'; | ||
| EXPECT_LE(strlen(model.filename), FILENAME_BUF_SIZE - 1) | ||
| << "Filename exceeds buffer bounds after copy"; | ||
| } else { | ||
| // Overflow case: assert that the buffer size is insufficient for this payload. | ||
| // This test FAILS if production code uses strcpy with this payload, | ||
| // proving the invariant is violated. | ||
| EXPECT_LT(payload.size(), FILENAME_BUF_SIZE) | ||
| << "SECURITY VIOLATION: payload length " << payload.size() | ||
| << " exceeds filename buffer size " << FILENAME_BUF_SIZE | ||
| << ". strcpy() would overflow the buffer."; | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Overflow branch assertion is self-contradictory and will fail on adversarial cases. At Line 41, the test asserts Suggested fix- bool wouldOverflow = (payload.size() >= FILENAME_BUF_SIZE);
-
- // The invariant: production code must NOT allow overflow.
- // We assert that either the payload fits, or the code must truncate/reject it.
- if (!wouldOverflow) {
- // Safe case: copy is within bounds
- strncpy(model.filename, payload.c_str(), FILENAME_BUF_SIZE - 1);
- model.filename[FILENAME_BUF_SIZE - 1] = '\0';
- EXPECT_LE(strlen(model.filename), FILENAME_BUF_SIZE - 1)
- << "Filename exceeds buffer bounds after copy";
- } else {
- // Overflow case: assert that the buffer size is insufficient for this payload.
- // This test FAILS if production code uses strcpy with this payload,
- // proving the invariant is violated.
- EXPECT_LT(payload.size(), FILENAME_BUF_SIZE)
- << "SECURITY VIOLATION: payload length " << payload.size()
- << " exceeds filename buffer size " << FILENAME_BUF_SIZE
- << ". strcpy() would overflow the buffer.";
- }
+ strncpy(model.filename, payload.c_str(), FILENAME_BUF_SIZE - 1);
+ model.filename[FILENAME_BUF_SIZE - 1] = '\0';
+
+ EXPECT_LE(strlen(model.filename), FILENAME_BUF_SIZE - 1)
+ << "Filename exceeds buffer bounds after copy";
+
+ if (payload.size() < FILENAME_BUF_SIZE) {
+ EXPECT_EQ(std::string(model.filename), payload);
+ } else {
+ EXPECT_EQ(strlen(model.filename), FILENAME_BUF_SIZE - 1);
+ }Also applies to: 57-59 🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| INSTANTIATE_TEST_SUITE_P( | ||
| AdversarialInputs, | ||
| FilenameBufferSecurityTest, | ||
| ::testing::Values( | ||
| // Valid input: short filename well within bounds | ||
| std::string("model01.bin"), | ||
| // Boundary value: exactly at the buffer limit (typically 16 bytes for filename) | ||
| std::string("123456789012345"), | ||
| // Exploit case: 2x oversized filename | ||
| std::string("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.bin"), | ||
| // Extreme case: 10x oversized filename | ||
| std::string(160, 'X') | ||
| ) | ||
| ); | ||
|
|
||
| int main(int argc, char **argv) { | ||
| ::testing::InitGoogleTest(&argc, argv); | ||
| return RUN_ALL_TESTS(); | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test does not exercise the patched mdichild assignment path.
Line 24 simulates copying with local
strncpy, so this test can pass/fail independently of the actual fixes incompanion/src/mdichild.cpp(pasteModelData/modelImport). For a security regression invariant, route the assertion through the same production assignment path (or extract that assignment into a shared helper and test that helper directly).🤖 Prompt for AI Agents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@orbisai0security can you address code review comments?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.