Skip to content

Commit

Permalink
[BUG] Illegal Heap write in rawbuf when the capture has overflowed. (#…
Browse files Browse the repository at this point in the history
…1517)

* Fix an issue where we write past the end of the capture buffer when it is full. Two options to fix this:
  1. Extend all capture buffers by 1 entry. i.e. upto 4 bytes of extra unused heap and some FLASH/PROGMEM bytes. _or_
  2. Skip the memory write when we have overflowed. i.e. Possibly slightly more than 4 bytes of FLASH/PROGMEM used.
  - CPU overhead should be about the same.
  - Given heap & memory is a more critical resource than Flash/PROGMEM, opting for Option 2.
* Add a helper method `IRrecv::_getParamsPtr` to access `params` in Unit tests.
* Unit tests so we can be sure it is fixed, and it doesn't happen again.

Kudos to @davepl for reporting the issue and diagnosing the offending line of code.

Fixes #1516
  • Loading branch information
crankyoldgit committed Jul 6, 2021
1 parent 3c1862f commit 0b19d94
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 1 deletion.
11 changes: 10 additions & 1 deletion src/IRrecv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,9 @@ bool IRrecv::decode(decode_results *results, irparams_t *save,
// interrupt. decode() is not stored in ICACHE_RAM.
// Another better option would be to zero the entire irparams.rawbuf[] on
// resume() but that is a much more expensive operation compare to this.
params.rawbuf[params.rawlen] = 0;
// However, don't do this if rawbuf is already full as we stomp over the heap.
// See: https://github.com/crankyoldgit/IRremoteESP8266/issues/1516
if (!params.overflow) params.rawbuf[params.rawlen] = 0;

bool resumed = false; // Flag indicating if we have resumed.

Expand Down Expand Up @@ -1882,4 +1884,11 @@ uint16_t IRrecv::matchManchesterData(volatile const uint16_t *data_ptr,
*result_ptr = GETBITS64(data, 0, nbits);
return offset;
}

#if UNIT_TEST
/// Unit test helper to get access to the params structure.
volatile irparams_t *IRrecv::_getParamsPtr(void) {
return &params;
}
#endif // UNIT_TEST
// End of IRrecv class -------------------
3 changes: 3 additions & 0 deletions src/IRrecv.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@ class IRrecv {
#if DECODE_HASH
uint16_t _unknown_threshold;
#endif
#ifdef UNIT_TEST
volatile irparams_t *_getParamsPtr(void);
#endif // UNIT_TEST
// These are called by decode
uint8_t _validTolerance(const uint8_t percentage);
void copyIrParams(volatile irparams_t *src, irparams_t *dst);
Expand Down
43 changes: 43 additions & 0 deletions test/IRrecv_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,49 @@ TEST(TestIRrecv, IRrecvDestructor) {
delete irrecv_ptr;
}

TEST(TestIRrecv, DecodeHeapOverflow) {
// Check that we handle the rawbuf correctly when we fill it. e.g. overflow.
// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1516
IRrecv irrecv(1);
irrecv.enableIRIn();
ASSERT_EQ(kRawBuf, irrecv.getBufSize());
volatile irparams_t *params_ptr = irrecv._getParamsPtr();
// replace the buffer with a slightly bigger one to see if we go past the end
// accidentally.
params_ptr->rawbuf = new uint16_t[kRawBuf + 10];
ASSERT_EQ(kRawBuf, irrecv.getBufSize()); // Should not change.
// Fill the raw buffer with canaries
// Values of 100 for the proper buffer size, & values of 99 for the extras
for (uint16_t i = 0; i < irrecv.getBufSize() + 10; i++) {
params_ptr->rawbuf[i] = 100;
if (i >= irrecv.getBufSize()) params_ptr->rawbuf[i]--;
}
ASSERT_EQ(100, params_ptr->rawbuf[kRawBuf - 1]);
EXPECT_EQ(99, params_ptr->rawbuf[kRawBuf]);
EXPECT_EQ(99, params_ptr->rawbuf[kRawBuf + 1]);
ASSERT_EQ(kRawBuf, params_ptr->bufsize);
decode_results results;
// Mock up the rest of params like we've received a message that has used
// all the rawbuf.
params_ptr->rawlen = kRawBuf;
params_ptr->overflow = true;
params_ptr->rcvstate = kStopState;
// Need to tweak results structure too.
results.rawbuf = params_ptr->rawbuf;
results.rawlen = params_ptr->rawlen;
results.overflow = params_ptr->overflow;

// Do the decode.
ASSERT_TRUE(irrecv.decode(&results));
// Yay, nothing exploded! Now check everything is as we expect
// w.r.t. the buffer.
ASSERT_EQ(kRawBuf, params_ptr->rawlen);
ASSERT_TRUE(params_ptr->overflow);
ASSERT_EQ(100, params_ptr->rawbuf[params_ptr->rawlen - 1]);
EXPECT_EQ(99, params_ptr->rawbuf[params_ptr->rawlen]);
EXPECT_EQ(99, params_ptr->rawbuf[params_ptr->rawlen + 1]);
}

// Tests for copyIrParams()

TEST(TestCopyIrParams, CopyEmpty) {
Expand Down

0 comments on commit 0b19d94

Please sign in to comment.