Skip to content

Conversation

flavio-fernandes
Copy link
Contributor

@flavio-fernandes flavio-fernandes commented Dec 24, 2019

Fixes #109
Fixes #122

After spending a long time pulling my hair to understand why I was
hitting a panic when attempting to read from my registered
subscriptions, I found out that the subscriptions member of the
Adafruit_MQTT instance was corrupted. :(

Turns out the memory corruption was caused by my publish
call, where the payload I was providing was bigger than the
allocated space in the buffer for construction of the packet
(see buffer[MAXBUFFERSIZE]).

To protect myself from ever making this mistake again, I am
proposing a simple logic in publishPacket where instead of
silently corrupting memory, the code uses as much payload
as it can fit in the available space. By seeing the
truncated payload, user can decide whether he/she should
1)break it up into different topics, 2) put the payload on
a diet, or 3) increase MAXBUFFERSIZE.

====

  • Describe the scope of your change--i.e. what the change does and what parts
    of the code were modified. This will help us understand any risks of integrating
    the code.

This pull requests improves the handling of buffer for packet creation of
Adafruit_MQTT::publishPacket. It adds argument maxPacketLen to keep track
of how much space is available in buffer.

  • Describe any known limitations with your change. For example if the change
    doesn't apply to a supported platform of the library please mention it.

This change is not modifying the sizes of any buffers. Instead, it truncates
the payload to avoid memory corruption of the class Adafruit_MQTT by keeping
packet creation from going beyond MAXBUFFERSIZE.

  • Please run any tests or examples that can exercise your modified code. We
    strive to not break users of the code and running tests/examples helps with this
    process.

See this gist with an example code where a packet has a payload that exceeds
MAXBUFFERSIZE and corrupts packet_id_counter and subscriptions of the
Adafruit_MQTT instance.

See comments below that gist with the crash and stack trace of code above, where memory is
corrupted and the LoadProhibited exception takes place.

Lastly, see the comment that follows with the proposed modification, where the same code
simply gets the payload truncated instead of allowing memory to get messed up.

flavio-fernandes added a commit to flavio-fernandes/Adafruit_MQTT_Library that referenced this pull request Dec 25, 2019
Avoid memory corruption from happening when data payload provided
in Adafruit_MQTT::publishPacket is greater than MAXBUFFERSIZE.

In order to do that, a helper function is being added to calculate
how much space is available for the payload after subtracting what
is used as header.

Pull request adafruit#166
Fixes adafruit#109
Fixes adafruit#122

Signed-off-by: Flavio Fernandes <[email protected]>
flavio-fernandes added a commit to flavio-fernandes/Adafruit_MQTT_Library that referenced this pull request Dec 25, 2019
Avoid memory corruption from happening when data payload provided
in Adafruit_MQTT::publishPacket is greater than MAXBUFFERSIZE.

In order to do that, a helper function is being added to calculate
how much space is available for the payload after subtracting what
is used as the header.

Pull request adafruit#166
Fixes adafruit#109
Fixes adafruit#122

Signed-off-by: Flavio Fernandes <[email protected]>
flavio-fernandes added a commit to flavio-fernandes/Adafruit_MQTT_Library that referenced this pull request Dec 25, 2019
Avoid memory corruption from happening when data payload provided
in Adafruit_MQTT::publishPacket is greater than MAXBUFFERSIZE.

In order to do that, a helper function is being added to calculate
how much space is available for the payload after subtracting what
is used as the header.

Pull request adafruit#166
Fixes adafruit#109
Fixes adafruit#122

Signed-off-by: Flavio Fernandes <[email protected]>
flavio-fernandes added a commit to flavio-fernandes/Adafruit_MQTT_Library that referenced this pull request Dec 25, 2019
Avoid memory corruption from happening when data payload provided
in Adafruit_MQTT::publishPacket is greater than MAXBUFFERSIZE.

In order to do that, a helper function is being added to calculate
how much space is available for the payload after subtracting what
is used as the header.

Pull request adafruit#166
Fixes adafruit#109
Fixes adafruit#122

Signed-off-by: Flavio Fernandes <[email protected]>
@brentru brentru removed their request for review February 27, 2020 19:32
flavio-fernandes added a commit to flavio-fernandes/Adafruit_MQTT_Library that referenced this pull request Aug 29, 2020
Avoid memory corruption from happening when data payload provided
in Adafruit_MQTT::publishPacket is greater than MAXBUFFERSIZE.

In order to do that, a helper function is being added to calculate
how much space is available for the payload after subtracting what
is used as the header.

Pull request adafruit#166
Fixes adafruit#109
Fixes adafruit#122

Signed-off-by: Flavio Fernandes <[email protected]>
flavio-fernandes added a commit to flavio-fernandes/Adafruit_MQTT_Library that referenced this pull request Aug 29, 2020
Avoid memory corruption from happening when data payload provided
in Adafruit_MQTT::publishPacket is greater than MAXBUFFERSIZE.

In order to do that, a helper function is being added to calculate
how much space is available for the payload after subtracting what
is used as the header.

Pull request adafruit#166
Fixes adafruit#109
Fixes adafruit#122

Signed-off-by: Flavio Fernandes <[email protected]>
flavio-fernandes added a commit to flavio-fernandes/Adafruit_MQTT_Library that referenced this pull request Aug 29, 2020
Avoid memory corruption from happening when data payload provided
in Adafruit_MQTT::publishPacket is greater than MAXBUFFERSIZE.

In order to do that, a helper function is being added to calculate
how much space is available for the payload after subtracting what
is used as the header.

Pull request adafruit#166
Fixes adafruit#109
Fixes adafruit#122

Signed-off-by: Flavio Fernandes <[email protected]>
@brentru brentru self-requested a review October 7, 2020 14:32
@BrunoMartins11
Copy link

It's 2021 and I still struggled to find that this is the reason why my program is crashing. Please review this PR asap for the sake of our mental health.

@Duckle29
Copy link

I share the frustration :( There seems to be a lot of nice PRs that have been sitting for more than a year. It's a shame. Thankfully platformIO lets me use git repos for lib_deps, so I forked your fix @flavio-fernandes, and used that. Thanks for the work you put in! :)

I'd switch to something like Async MQTT, but I need to use a library that uses a Client object like this, to work with my bearSSL certificate store.

@flavio-fernandes
Copy link
Contributor Author

I share the frustration :( There seems to be a lot of nice PRs that have been sitting for more than a year. It's a shame. Thankfully platformIO lets me use git repos for lib_deps, so I forked your fix @flavio-fernandes, and used that. Thanks for the work you put in! :)

I'd switch to something like Async MQTT, but I need to use a library that uses a Client object like this, to work with my bearSSL certificate store.

Maybe @ladyada or @brentru can listen to our cries. I do not mind if a different PR gets used; just wish for a better experience using this awesome library.

Copy link
Member

@brentru brentru left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@flavio-fernandes Made two small comments. Rest looks good and is similar to a portion of MiniMQTT which I've implemented to address a similar issue.

After you're done addressing them, please bump the patch version number in https://github.com/adafruit/Adafruit_MQTT_Library/blob/master/library.properties#L2 by 1 so we can get this picked up by the Arduino Library Manager tool.

Adafruit_MQTT.h Outdated
uint16_t publishPacket(uint8_t *packet, const char *topic, uint8_t *payload,
uint16_t bLen, uint8_t qos);
uint16_t bLen, uint8_t qos, uint16_t maxPacketLen);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@flavio-fernandes is there a way to protect against memory corruption without changing the function signature here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really. 😄 I think that the only way we can protect from the memory corruption is by knowing how much space is available to begin with.
This is a private member function, so there should not be a concern about changing its signature, agree?
Regardless, I made the extra parameter have the default value of 0, so caller will still work should we try to use the old function arguments.

@@ -665,11 +666,22 @@ uint8_t Adafruit_MQTT::connectPacket(uint8_t *packet) {
return len;
}

uint16_t Adafruit_MQTT::packetAdditionalLen(uint16_t currLen) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could this instead be incorporated into publishPacket?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay. I will use that function in cpp file as a static. So this new line is now removed.

@flavio-fernandes
Copy link
Contributor Author

@flavio-fernandes Made two small comments. Rest looks good and is similar to a portion of MiniMQTT which I've implemented to address a similar issue.

After you're done addressing them, please bump the patch version number in https://github.com/adafruit/Adafruit_MQTT_Library/blob/master/library.properties#L2 by 1 so we can get this picked up by the Arduino Library Manager tool.

@flavio-fernandes Made two small comments. Rest looks good and is similar to a portion of MiniMQTT which I've implemented to address a similar issue.

After you're done addressing them, please bump the patch version number in https://github.com/adafruit/Adafruit_MQTT_Library/blob/master/library.properties#L2 by 1 so we can get this picked up by the Arduino Library Manager tool.

Ack! TBH, it has been a while since I thought about this PR.
Let me think a little bit about what you said and make the changes.

@brentru
Copy link
Member

brentru commented Mar 31, 2021

@flavio-fernandes ok, let me know when its ready to review. I have a pretty large MQTT application which uses this library and I'd like to test with that.

flavio-fernandes added a commit to flavio-fernandes/Adafruit_MQTT_Library that referenced this pull request Apr 2, 2021
Avoid memory corruption from happening when data payload provided
in Adafruit_MQTT::publishPacket is greater than MAXBUFFERSIZE.

In order to do that, a helper function is being added to calculate
how much space is available for the payload after subtracting what
is used as the header.

Pull request adafruit#166
Fixes adafruit#109
Fixes adafruit#122

Signed-off-by: Flavio Fernandes <[email protected]>
flavio-fernandes added a commit to flavio-fernandes/Adafruit_MQTT_Library that referenced this pull request Apr 2, 2021
Avoid memory corruption from happening when data payload provided
in Adafruit_MQTT::publishPacket is greater than MAXBUFFERSIZE.

In order to do that, a helper function is being added to calculate
how much space is available for the payload after subtracting what
is used as the header.

Pull request adafruit#166
Fixes adafruit#109
Fixes adafruit#122

Signed-off-by: Flavio Fernandes <[email protected]>
Avoid memory corruption from happening when data payload provided
in Adafruit_MQTT::publishPacket is greater than MAXBUFFERSIZE.

In order to do that, a helper function is being added to calculate
how much space is available for the payload after subtracting what
is used as the header.

Pull request adafruit#166
Fixes adafruit#109
Fixes adafruit#122

Signed-off-by: Flavio Fernandes <[email protected]>
@flavio-fernandes
Copy link
Contributor Author

@flavio-fernandes ok, let me know when its ready to review. I have a pretty large MQTT application which uses this library and I'd like to test with that.

Hi @brentru ! Okay, I made the changes and pushed an update. Let me know what you think, please.
As for testing the bug, consider using this gist to easily make the corruption take place without the fix; and the non-crashing version once the pr changes are added. Lastly, I bumped the version to 2.1.1, as you requested.

Best!

@brentru
Copy link
Member

brentru commented May 12, 2021

@flavio-fernandes Could you bump to 2.2.0? I tested this against a SECRET project and it prevents corruption there as well.

@flavio-fernandes
Copy link
Contributor Author

@flavio-fernandes Could you bump to 2.2.0? I tested this against a SECRET project and it prevents corruption there as well.

@brentru : Done bumping to 2.2.0 . Secret project? You make me curious! :)
Best,
-- flaviof

return 0;
if (currLen < 16384)
return 1;
if (currLen < 2097151)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think currLen (unsigned 16-bit) will always be less than 65536, so this comparison might give an unexpected result (I made the same mistake, still trying to think of a solution. I've noticed that many types in this class assume maximum lengths that might not be compatible with all message/packet lengths -- some discussion might be helpful there).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you are right! I still think that the compare should work, granted the compiler promote the type. If not, we could explicitly cast the variable to uitn32u, but that may not be doable on older arduino boards?

@brentru brentru merged commit ca12d21 into adafruit:master May 14, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Buffer overflow in publishPacket What's the max size for publish in esp8266
5 participants