Skip to content

Add buffer safety checks to prevent out-of-bounds access#96

Open
hmueller01 wants to merge 12 commits into
masterfrom
safety-checks-new
Open

Add buffer safety checks to prevent out-of-bounds access#96
hmueller01 wants to merge 12 commits into
masterfrom
safety-checks-new

Conversation

@hmueller01

Copy link
Copy Markdown
Owner

Applied safety checks introduced by @MarcAntoineCRUE in #91

  • connect(): null-buffer guard to prevent crash if buffer allocation failed
  • disconnect(): null-buffer guard
  • loop(): buffer existence and size validation before packet processing
  • handlePacket(): three ordered boundary checks for topic length, payload offset, and msgId fields
  • Detailed error messages with context for debugging

- connect(): null-buffer guard to prevent crash if buffer allocation failed
- disconnect(): null-buffer guard
- loop(): buffer existence and size validation before packet processing
- handlePacket(): three ordered boundary checks for topic length, payload offset, and msgId fields
- Detailed error messages with context for debugging
@hmueller01

Copy link
Copy Markdown
Owner Author

@TD-er @MarcAntoineCRUE This PR is just about the safety checks (and some needed mods to satisfy new linter).
This was the output of several Claude AI attempts to split the changes in logical PRs. Unfortunately somethings with the original branch (safety-checks) that Claude created went wrong and the commits to that branch also got to master :-( Maybe my VS Code got confused ...

@hmueller01 hmueller01 requested a review from TD-er June 7, 2026 20:55
@TD-er

TD-er commented Jun 7, 2026

Copy link
Copy Markdown
Collaborator

At first glance I don't see any obvious issues, but since I was just about to go to bed, I will have to look at it tomorrow with some fresh eyes :)

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

Memory usage change @ d3973b9

Board flash % RAM for global variables %
arduino:avr:uno 🔺 +122 - +122 +0.38 - +0.38 0 - 0 0.0 - 0.0
arduino:megaavr:uno2018 🔺 +122 - +122 +0.25 - +0.25 0 - 0 0.0 - 0.0
arduino:samd:mkr1000 🔺 +44 - +44 +0.02 - +0.02 0 - 0 0.0 - 0.0
esp32:esp32:esp32 🔺 +44 - +44 0.0 - 0.0 0 - 0 0.0 - 0.0
esp8266:esp8266:generic N/A N/A N/A N/A
Click for full report table
Board examples/mqtt_auth
flash
% examples/mqtt_auth
RAM for global variables
% examples/mqtt_basic
flash
% examples/mqtt_basic
RAM for global variables
% examples/mqtt_progmem
flash
% examples/mqtt_progmem
RAM for global variables
% examples/mqtt_publish_in_callback
flash
% examples/mqtt_publish_in_callback
RAM for global variables
% examples/mqtt_reconnect_nonblocking
flash
% examples/mqtt_reconnect_nonblocking
RAM for global variables
% examples/mqtt_stream
flash
% examples/mqtt_stream
RAM for global variables
% examples/mqtt_esp
flash
% examples/mqtt_esp
RAM for global variables
% examples/mqtt_esp_large_message
flash
% examples/mqtt_esp_large_message
RAM for global variables
%
arduino:avr:uno 122 0.38 0 0.0 122 0.38 0 0.0 122 0.38 0 0.0 122 0.38 0 0.0 122 0.38 0 0.0 122 0.38 0 0.0
arduino:megaavr:uno2018 122 0.25 0 0.0 122 0.25 0 0.0 122 0.25 0 0.0 122 0.25 0 0.0 122 0.25 0 0.0 122 0.25 0 0.0
arduino:samd:mkr1000 44 0.02 0 0.0 44 0.02 0 0.0 44 0.02 0 0.0 44 0.02 0 0.0 44 0.02 0 0.0 44 0.02 0 0.0
esp32:esp32:esp32 44 0.0 0 0.0 44 0.0 0 0.0 44 0.0 0 0.0 44 0.0 0 0.0 44 0.0 0 0.0 44 0.0 0 0.0 44 0.0 0 0.0
esp8266:esp8266:generic N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A
Click for full report CSV
Board,examples/mqtt_auth<br>flash,%,examples/mqtt_auth<br>RAM for global variables,%,examples/mqtt_basic<br>flash,%,examples/mqtt_basic<br>RAM for global variables,%,examples/mqtt_progmem<br>flash,%,examples/mqtt_progmem<br>RAM for global variables,%,examples/mqtt_publish_in_callback<br>flash,%,examples/mqtt_publish_in_callback<br>RAM for global variables,%,examples/mqtt_reconnect_nonblocking<br>flash,%,examples/mqtt_reconnect_nonblocking<br>RAM for global variables,%,examples/mqtt_stream<br>flash,%,examples/mqtt_stream<br>RAM for global variables,%,examples/mqtt_esp<br>flash,%,examples/mqtt_esp<br>RAM for global variables,%,examples/mqtt_esp_large_message<br>flash,%,examples/mqtt_esp_large_message<br>RAM for global variables,%
arduino:avr:uno,122,0.38,0,0.0,122,0.38,0,0.0,122,0.38,0,0.0,122,0.38,0,0.0,122,0.38,0,0.0,122,0.38,0,0.0
arduino:megaavr:uno2018,122,0.25,0,0.0,122,0.25,0,0.0,122,0.25,0,0.0,122,0.25,0,0.0,122,0.25,0,0.0,122,0.25,0,0.0
arduino:samd:mkr1000,44,0.02,0,0.0,44,0.02,0,0.0,44,0.02,0,0.0,44,0.02,0,0.0,44,0.02,0,0.0,44,0.02,0,0.0
esp32:esp32:esp32,44,0.0,0,0.0,44,0.0,0,0.0,44,0.0,0,0.0,44,0.0,0,0.0,44,0.0,0,0.0,,,,,44,0.0,0,0.0,44,0.0,0,0.0
esp8266:esp8266:generic,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A

Comment thread src/PubSubClient.cpp Outdated
@TD-er

TD-er commented Jun 8, 2026

Copy link
Copy Markdown
Collaborator

Only found one thing to think about.
The rest looks fine to me.

@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown

Memory usage change @ 32b657c

Board flash % RAM for global variables %
arduino:avr:uno 🔺 +86 - +98 +0.27 - +0.3 0 - 0 0.0 - 0.0
arduino:megaavr:uno2018 🔺 +68 - +86 +0.14 - +0.18 0 - 0 0.0 - 0.0
arduino:samd:mkr1000 🔺 +44 - +44 +0.02 - +0.02 0 - 0 0.0 - 0.0
esp32:esp32:esp32 🔺 +44 - +52 0.0 - 0.0 0 - 0 0.0 - 0.0
esp8266:esp8266:generic N/A N/A N/A N/A
Click for full report table
Board examples/mqtt_auth
flash
% examples/mqtt_auth
RAM for global variables
% examples/mqtt_basic
flash
% examples/mqtt_basic
RAM for global variables
% examples/mqtt_progmem
flash
% examples/mqtt_progmem
RAM for global variables
% examples/mqtt_publish_in_callback
flash
% examples/mqtt_publish_in_callback
RAM for global variables
% examples/mqtt_reconnect_nonblocking
flash
% examples/mqtt_reconnect_nonblocking
RAM for global variables
% examples/mqtt_stream
flash
% examples/mqtt_stream
RAM for global variables
% examples/mqtt_esp
flash
% examples/mqtt_esp
RAM for global variables
% examples/mqtt_esp_large_message
flash
% examples/mqtt_esp_large_message
RAM for global variables
%
arduino:avr:uno 86 0.27 0 0.0 98 0.3 0 0.0 86 0.27 0 0.0 86 0.27 0 0.0 96 0.3 0 0.0 86 0.27 0 0.0
arduino:megaavr:uno2018 86 0.18 0 0.0 68 0.14 0 0.0 86 0.18 0 0.0 86 0.18 0 0.0 86 0.18 0 0.0 86 0.18 0 0.0
arduino:samd:mkr1000 44 0.02 0 0.0 44 0.02 0 0.0 44 0.02 0 0.0 44 0.02 0 0.0 44 0.02 0 0.0 44 0.02 0 0.0
esp32:esp32:esp32 44 0.0 0 0.0 44 0.0 0 0.0 44 0.0 0 0.0 44 0.0 0 0.0 44 0.0 0 0.0 52 0.0 0 0.0 44 0.0 0 0.0
esp8266:esp8266:generic N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A
Click for full report CSV
Board,examples/mqtt_auth<br>flash,%,examples/mqtt_auth<br>RAM for global variables,%,examples/mqtt_basic<br>flash,%,examples/mqtt_basic<br>RAM for global variables,%,examples/mqtt_progmem<br>flash,%,examples/mqtt_progmem<br>RAM for global variables,%,examples/mqtt_publish_in_callback<br>flash,%,examples/mqtt_publish_in_callback<br>RAM for global variables,%,examples/mqtt_reconnect_nonblocking<br>flash,%,examples/mqtt_reconnect_nonblocking<br>RAM for global variables,%,examples/mqtt_stream<br>flash,%,examples/mqtt_stream<br>RAM for global variables,%,examples/mqtt_esp<br>flash,%,examples/mqtt_esp<br>RAM for global variables,%,examples/mqtt_esp_large_message<br>flash,%,examples/mqtt_esp_large_message<br>RAM for global variables,%
arduino:avr:uno,86,0.27,0,0.0,98,0.3,0,0.0,86,0.27,0,0.0,86,0.27,0,0.0,96,0.3,0,0.0,86,0.27,0,0.0
arduino:megaavr:uno2018,86,0.18,0,0.0,68,0.14,0,0.0,86,0.18,0,0.0,86,0.18,0,0.0,86,0.18,0,0.0,86,0.18,0,0.0
arduino:samd:mkr1000,44,0.02,0,0.0,44,0.02,0,0.0,44,0.02,0,0.0,44,0.02,0,0.0,44,0.02,0,0.0,44,0.02,0,0.0
esp32:esp32:esp32,44,0.0,0,0.0,44,0.0,0,0.0,44,0.0,0,0.0,44,0.0,0,0.0,44,0.0,0,0.0,,,,,52,0.0,0,0.0,44,0.0,0,0.0
esp8266:esp8266:generic,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A

Comment thread src/PubSubClient.cpp Outdated
Comment thread src/PubSubClient.cpp Outdated
@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown

Memory usage change @ b67cac8

Board flash % RAM for global variables %
arduino:avr:uno 🔺 +70 - +72 +0.22 - +0.22 0 - 0 0.0 - 0.0
arduino:megaavr:uno2018 🔺 +70 - +74 +0.14 - +0.15 0 - 0 0.0 - 0.0
arduino:samd:mkr1000 🔺 +24 - +24 +0.01 - +0.01 0 - 0 0.0 - 0.0
esp32:esp32:esp32 🔺 +24 - +32 0.0 - 0.0 0 - 0 0.0 - 0.0
esp8266:esp8266:generic N/A N/A N/A N/A
Click for full report table
Board examples/mqtt_auth
flash
% examples/mqtt_auth
RAM for global variables
% examples/mqtt_basic
flash
% examples/mqtt_basic
RAM for global variables
% examples/mqtt_progmem
flash
% examples/mqtt_progmem
RAM for global variables
% examples/mqtt_publish_in_callback
flash
% examples/mqtt_publish_in_callback
RAM for global variables
% examples/mqtt_reconnect_nonblocking
flash
% examples/mqtt_reconnect_nonblocking
RAM for global variables
% examples/mqtt_stream
flash
% examples/mqtt_stream
RAM for global variables
% examples/mqtt_esp
flash
% examples/mqtt_esp
RAM for global variables
% examples/mqtt_esp_large_message
flash
% examples/mqtt_esp_large_message
RAM for global variables
%
arduino:avr:uno 70 0.22 0 0.0 72 0.22 0 0.0 70 0.22 0 0.0 70 0.22 0 0.0 70 0.22 0 0.0 70 0.22 0 0.0
arduino:megaavr:uno2018 70 0.14 0 0.0 74 0.15 0 0.0 70 0.14 0 0.0 70 0.14 0 0.0 70 0.14 0 0.0 70 0.14 0 0.0
arduino:samd:mkr1000 24 0.01 0 0.0 24 0.01 0 0.0 24 0.01 0 0.0 24 0.01 0 0.0 24 0.01 0 0.0 24 0.01 0 0.0
esp32:esp32:esp32 24 0.0 0 0.0 24 0.0 0 0.0 24 0.0 0 0.0 24 0.0 0 0.0 24 0.0 0 0.0 32 0.0 0 0.0 24 0.0 0 0.0
esp8266:esp8266:generic N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A
Click for full report CSV
Board,examples/mqtt_auth<br>flash,%,examples/mqtt_auth<br>RAM for global variables,%,examples/mqtt_basic<br>flash,%,examples/mqtt_basic<br>RAM for global variables,%,examples/mqtt_progmem<br>flash,%,examples/mqtt_progmem<br>RAM for global variables,%,examples/mqtt_publish_in_callback<br>flash,%,examples/mqtt_publish_in_callback<br>RAM for global variables,%,examples/mqtt_reconnect_nonblocking<br>flash,%,examples/mqtt_reconnect_nonblocking<br>RAM for global variables,%,examples/mqtt_stream<br>flash,%,examples/mqtt_stream<br>RAM for global variables,%,examples/mqtt_esp<br>flash,%,examples/mqtt_esp<br>RAM for global variables,%,examples/mqtt_esp_large_message<br>flash,%,examples/mqtt_esp_large_message<br>RAM for global variables,%
arduino:avr:uno,70,0.22,0,0.0,72,0.22,0,0.0,70,0.22,0,0.0,70,0.22,0,0.0,70,0.22,0,0.0,70,0.22,0,0.0
arduino:megaavr:uno2018,70,0.14,0,0.0,74,0.15,0,0.0,70,0.14,0,0.0,70,0.14,0,0.0,70,0.14,0,0.0,70,0.14,0,0.0
arduino:samd:mkr1000,24,0.01,0,0.0,24,0.01,0,0.0,24,0.01,0,0.0,24,0.01,0,0.0,24,0.01,0,0.0,24,0.01,0,0.0
esp32:esp32:esp32,24,0.0,0,0.0,24,0.0,0,0.0,24,0.0,0,0.0,24,0.0,0,0.0,24,0.0,0,0.0,,,,,32,0.0,0,0.0,24,0.0,0,0.0
esp8266:esp8266:generic,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A

…etBufferSize(), no need to check every time in loop()
@hmueller01

Copy link
Copy Markdown
Owner Author

These branches that the AI (Claude) created are really strange. Now branch type-improvements got commited into master. Damn it. I did the other way around!

Merge branch 'master' of https://github.com/hmueller01/pubsubclient3 into type-improvements

@hmueller01

Copy link
Copy Markdown
Owner Author

@TD-er If you find time again, can you please take a last look on it. Thanks.

Comment thread src/PubSubClient.cpp Outdated
Comment thread src/PubSubClient.cpp Outdated
Comment thread src/PubSubClient.cpp Outdated
Comment thread src/PubSubClient.cpp Outdated
// Cannot set it back to 0
// Buffer must be large enough to hold at least a minimal MQTT packet.
// Note: MQTT_MAX_HEADER_SIZE (5) + protocol (9) + flags (1) + keepalive (2) covers a minmal CONNECT message.
if (size < MQTT_MAX_HEADER_SIZE + 9 + 1 + 2) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hmm I guess there might be good reasons to completely disable the buffer to save up all available resources, only to later set the buffer again.
So maybe it is good to accept sizes which are too small and then simply delete the buffer and set its pointer to null.
This value you're now checking against may be a good bottom value to just clear the buffer.

Not sure though what the return value should be for this function.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Ok, I now allow setting the buffer to 0, but not to lower values than the min buffer size. It this fine for you?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

@TD-er Are you ok with the new implementation?

@TD-er

TD-er commented Jun 14, 2026

Copy link
Copy Markdown
Collaborator

There are quite a few functions in the code that try to access _buffer[... or just refer to _buffer as a pointer, which still don't check for _buffer not being a nullptr .

For example:

  • PubSubClient::buildHeader
  • PubSubClient::writeStringImpl
  • PubSubClient::appendBuffer (also no check for _bufferWritePos )
  • PubSubClient::subscribeImpl
  • PubSubClient::readPacket

@hmueller01

Copy link
Copy Markdown
Owner Author

There are quite a few functions in the code that try to access _buffer[... or just refer to _buffer as a pointer, which still don't check for _buffer not being a nullptr .

For example:

  • PubSubClient::buildHeader
  • PubSubClient::writeStringImpl
  • PubSubClient::appendBuffer (also no check for _bufferWritePos )
  • PubSubClient::subscribeImpl
  • PubSubClient::readPacket

Hmm, this is the question. Do we make every function failsafe by it's own, or do we trust that it was checked before. I see only one case that might fail here: PubSubClient::writeString, that uses PubSubClient::writeStringImpl and can be called from user application. But as long as allocation of _buffer and value of _bufferSize is in sync (and this should be always be the case) there is no problem.

size_t PubSubClient::writeStringImpl(bool progmem, const char* string, size_t pos) {
if (!string) return pos;
size_t sLen = progmem ? strlen_P(string) : strlen(string);
if ((pos + 2 + sLen <= _bufferSize) && (sLen <= 0xFFFF)) {

@github-actions

Copy link
Copy Markdown

Memory usage change @ c856d48

Board flash % RAM for global variables %
arduino:avr:uno 🔺 +62 - +68 +0.19 - +0.21 0 - 0 0.0 - 0.0
arduino:megaavr:uno2018 🔺 +62 - +70 +0.13 - +0.14 0 - 0 0.0 - 0.0
arduino:samd:mkr1000 🔺 +44 - +44 +0.02 - +0.02 0 - 0 0.0 - 0.0
esp32:esp32:esp32 🔺 +68 - +88 +0.01 - +0.01 0 - 0 0.0 - 0.0
esp8266:esp8266:generic N/A N/A N/A N/A
Click for full report table
Board examples/mqtt_auth
flash
% examples/mqtt_auth
RAM for global variables
% examples/mqtt_basic
flash
% examples/mqtt_basic
RAM for global variables
% examples/mqtt_progmem
flash
% examples/mqtt_progmem
RAM for global variables
% examples/mqtt_publish_in_callback
flash
% examples/mqtt_publish_in_callback
RAM for global variables
% examples/mqtt_reconnect_nonblocking
flash
% examples/mqtt_reconnect_nonblocking
RAM for global variables
% examples/mqtt_stream
flash
% examples/mqtt_stream
RAM for global variables
% examples/mqtt_esp
flash
% examples/mqtt_esp
RAM for global variables
% examples/mqtt_esp_large_message
flash
% examples/mqtt_esp_large_message
RAM for global variables
%
arduino:avr:uno 68 0.21 0 0.0 68 0.21 0 0.0 68 0.21 0 0.0 68 0.21 0 0.0 62 0.19 0 0.0 68 0.21 0 0.0
arduino:megaavr:uno2018 68 0.14 0 0.0 70 0.14 0 0.0 68 0.14 0 0.0 68 0.14 0 0.0 62 0.13 0 0.0 68 0.14 0 0.0
arduino:samd:mkr1000 44 0.02 0 0.0 44 0.02 0 0.0 44 0.02 0 0.0 44 0.02 0 0.0 44 0.02 0 0.0 44 0.02 0 0.0
esp32:esp32:esp32 68 0.01 0 0.0 68 0.01 0 0.0 68 0.01 0 0.0 68 0.01 0 0.0 68 0.01 0 0.0 88 0.01 0 0.0 76 0.01 0 0.0
esp8266:esp8266:generic N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A
Click for full report CSV
Board,examples/mqtt_auth<br>flash,%,examples/mqtt_auth<br>RAM for global variables,%,examples/mqtt_basic<br>flash,%,examples/mqtt_basic<br>RAM for global variables,%,examples/mqtt_progmem<br>flash,%,examples/mqtt_progmem<br>RAM for global variables,%,examples/mqtt_publish_in_callback<br>flash,%,examples/mqtt_publish_in_callback<br>RAM for global variables,%,examples/mqtt_reconnect_nonblocking<br>flash,%,examples/mqtt_reconnect_nonblocking<br>RAM for global variables,%,examples/mqtt_stream<br>flash,%,examples/mqtt_stream<br>RAM for global variables,%,examples/mqtt_esp<br>flash,%,examples/mqtt_esp<br>RAM for global variables,%,examples/mqtt_esp_large_message<br>flash,%,examples/mqtt_esp_large_message<br>RAM for global variables,%
arduino:avr:uno,68,0.21,0,0.0,68,0.21,0,0.0,68,0.21,0,0.0,68,0.21,0,0.0,62,0.19,0,0.0,68,0.21,0,0.0
arduino:megaavr:uno2018,68,0.14,0,0.0,70,0.14,0,0.0,68,0.14,0,0.0,68,0.14,0,0.0,62,0.13,0,0.0,68,0.14,0,0.0
arduino:samd:mkr1000,44,0.02,0,0.0,44,0.02,0,0.0,44,0.02,0,0.0,44,0.02,0,0.0,44,0.02,0,0.0,44,0.02,0,0.0
esp32:esp32:esp32,68,0.01,0,0.0,68,0.01,0,0.0,68,0.01,0,0.0,68,0.01,0,0.0,68,0.01,0,0.0,,,,,88,0.01,0,0.0,76,0.01,0,0.0
esp8266:esp8266:generic,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A

@hmueller01

Copy link
Copy Markdown
Owner Author

@MarcAntoineCRUE Heard nothing from you. Hope you are doing well. If no further comments I'll merge this soon.

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.

2 participants