Skip to content

Commit 0b19d94

Browse files
authored
[BUG] Illegal Heap write in rawbuf when the capture has overflowed. (#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
1 parent 3c1862f commit 0b19d94

File tree

3 files changed

+56
-1
lines changed

3 files changed

+56
-1
lines changed

src/IRrecv.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,9 @@ bool IRrecv::decode(decode_results *results, irparams_t *save,
518518
// interrupt. decode() is not stored in ICACHE_RAM.
519519
// Another better option would be to zero the entire irparams.rawbuf[] on
520520
// resume() but that is a much more expensive operation compare to this.
521-
params.rawbuf[params.rawlen] = 0;
521+
// However, don't do this if rawbuf is already full as we stomp over the heap.
522+
// See: https://github.com/crankyoldgit/IRremoteESP8266/issues/1516
523+
if (!params.overflow) params.rawbuf[params.rawlen] = 0;
522524

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

@@ -1882,4 +1884,11 @@ uint16_t IRrecv::matchManchesterData(volatile const uint16_t *data_ptr,
18821884
*result_ptr = GETBITS64(data, 0, nbits);
18831885
return offset;
18841886
}
1887+
1888+
#if UNIT_TEST
1889+
/// Unit test helper to get access to the params structure.
1890+
volatile irparams_t *IRrecv::_getParamsPtr(void) {
1891+
return &params;
1892+
}
1893+
#endif // UNIT_TEST
18851894
// End of IRrecv class -------------------

src/IRrecv.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,9 @@ class IRrecv {
162162
#if DECODE_HASH
163163
uint16_t _unknown_threshold;
164164
#endif
165+
#ifdef UNIT_TEST
166+
volatile irparams_t *_getParamsPtr(void);
167+
#endif // UNIT_TEST
165168
// These are called by decode
166169
uint8_t _validTolerance(const uint8_t percentage);
167170
void copyIrParams(volatile irparams_t *src, irparams_t *dst);

test/IRrecv_test.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,49 @@ TEST(TestIRrecv, IRrecvDestructor) {
4242
delete irrecv_ptr;
4343
}
4444

45+
TEST(TestIRrecv, DecodeHeapOverflow) {
46+
// Check that we handle the rawbuf correctly when we fill it. e.g. overflow.
47+
// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1516
48+
IRrecv irrecv(1);
49+
irrecv.enableIRIn();
50+
ASSERT_EQ(kRawBuf, irrecv.getBufSize());
51+
volatile irparams_t *params_ptr = irrecv._getParamsPtr();
52+
// replace the buffer with a slightly bigger one to see if we go past the end
53+
// accidentally.
54+
params_ptr->rawbuf = new uint16_t[kRawBuf + 10];
55+
ASSERT_EQ(kRawBuf, irrecv.getBufSize()); // Should not change.
56+
// Fill the raw buffer with canaries
57+
// Values of 100 for the proper buffer size, & values of 99 for the extras
58+
for (uint16_t i = 0; i < irrecv.getBufSize() + 10; i++) {
59+
params_ptr->rawbuf[i] = 100;
60+
if (i >= irrecv.getBufSize()) params_ptr->rawbuf[i]--;
61+
}
62+
ASSERT_EQ(100, params_ptr->rawbuf[kRawBuf - 1]);
63+
EXPECT_EQ(99, params_ptr->rawbuf[kRawBuf]);
64+
EXPECT_EQ(99, params_ptr->rawbuf[kRawBuf + 1]);
65+
ASSERT_EQ(kRawBuf, params_ptr->bufsize);
66+
decode_results results;
67+
// Mock up the rest of params like we've received a message that has used
68+
// all the rawbuf.
69+
params_ptr->rawlen = kRawBuf;
70+
params_ptr->overflow = true;
71+
params_ptr->rcvstate = kStopState;
72+
// Need to tweak results structure too.
73+
results.rawbuf = params_ptr->rawbuf;
74+
results.rawlen = params_ptr->rawlen;
75+
results.overflow = params_ptr->overflow;
76+
77+
// Do the decode.
78+
ASSERT_TRUE(irrecv.decode(&results));
79+
// Yay, nothing exploded! Now check everything is as we expect
80+
// w.r.t. the buffer.
81+
ASSERT_EQ(kRawBuf, params_ptr->rawlen);
82+
ASSERT_TRUE(params_ptr->overflow);
83+
ASSERT_EQ(100, params_ptr->rawbuf[params_ptr->rawlen - 1]);
84+
EXPECT_EQ(99, params_ptr->rawbuf[params_ptr->rawlen]);
85+
EXPECT_EQ(99, params_ptr->rawbuf[params_ptr->rawlen + 1]);
86+
}
87+
4588
// Tests for copyIrParams()
4689

4790
TEST(TestCopyIrParams, CopyEmpty) {

0 commit comments

Comments
 (0)