Conversation
jgromes
left a comment
There was a problem hiding this comment.
While I agree that this is indeed a bug, honest question, why do you feel the need to generate a novel-length wall of text for it? Seriously, the only problem was that the assignment operator implementation was missing some of the members. This happened because as these new member variables were added to the Module class, they were not added to the operator.
The provided test does not guard this in the future BTW - if we add some member variable to the Module class and not add it to the test, it will happily pass as it is not checking the newly added variable. So why is the test here?
|
|
||
| BOOST_FIXTURE_TEST_SUITE(suite_Module, ModuleFixture) | ||
|
|
||
| BOOST_FIXTURE_TEST_CASE(Module_copy_assignment_copies_spi_config_without_mutating_source, ModuleFixture) |
There was a problem hiding this comment.
No human in the history of the world would ever name a test case like this. Why is Module_copy_assignment insufficient?
| { | ||
| BOOST_TEST_MESSAGE("--- Test Module copy assignment copies SPI config correctly ---"); | ||
|
|
||
| Module other(hal, EMULATED_RADIO_NSS_PIN, EMULATED_RADIO_IRQ_PIN, EMULATED_RADIO_RST_PIN, EMULATED_RADIO_GPIO_PIN); |
There was a problem hiding this comment.
If you really want to ensure the source is unchanged, you have to set different pins and check them.
| this->irqPin = mod.irqPin; | ||
| this->rstPin = mod.rstPin; | ||
| this->gpioPin = mod.gpioPin; | ||
| memcpy(this->rfSwitchPins, mod.rfSwitchPins, sizeof(this->rfSwitchPins)); |
There was a problem hiding this comment.
Why is size of this copy not checked against Module::RFSWITCH_MAX_PINS?
| mod->spiConfig.timeout = 4321; | ||
| mod->rfSwitchPins[0] = 1001; | ||
| mod->rfSwitchPins[1] = 1002; | ||
| mod->rfSwitchTable = reinterpret_cast<const Module::RfSwitchMode_t*>(0x1234); |
There was a problem hiding this comment.
Why are you reusing the module fixture to do this? It seems prone to breaking in the future if the approach to its setup/teardown changes. Especially since mod->rfSwitchTable is pointing a an arbitrary memory address.
Summary
Module::operator=so it copies from the source object into the destinationspiConfigModulecopy assignment behaviorWhy this is a bug
The original
Module::operator=copiedthis->spiConfigintomod.spiConfiginstead of copying frommodintothis.That had two bad effects:
The original operator also did not copy the RF switch state, so assignment produced an incomplete
Modulecopy even aside from the reversedspiConfigcopy.History
Modulecopy constructor and assignment were introduced in commitb8de06288("Added copy ctor and assignment operator", July 5, 2020). The bad copy direction inoperator=dates back to that introduction.I did not find a later mitigation elsewhere in-tree. In-tree usage of
Moduleassignment appears to be rare, which likely kept this bug latent, but the operator is public API and its current behavior is incorrect.Validation
I ran the full existing unit suite in
extras/test/unitwith Homebrew GCC/Boost/fmt paths configured.Against original code
I copied only the new regression test into a clean worktree at the original
HEADand reran the full suite. The new test failed, showing that:spiConfigwas overwritten by assignmentAgainst this fix
I ran the full unit suite after the fix.
Result:
*** No errors detectedTest coverage
Adds
Module_copy_assignment_copies_spi_config_without_mutating_sourcein:extras/test/unit/tests/TestModule.cppThe test verifies:
spiConfig