-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Split udp.cpp into per-protocol files, encapsulate two low-fanout globals #5774
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,109 @@ | ||||||||||||||||||||||||||||||||||||||||
| #include "wled.h" | ||||||||||||||||||||||||||||||||||||||||
| #include "sync.h" | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| /* | ||||||||||||||||||||||||||||||||||||||||
| * ESP-NOW transport for the WLED Notifier sync protocol. Not a protocol of its own - | ||||||||||||||||||||||||||||||||||||||||
| * reassembles fragmented packets and hands the result to parseNotifyPacket() | ||||||||||||||||||||||||||||||||||||||||
| * (sync_notifier.cpp), the same decoder used by the UDP transport. | ||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| #ifndef WLED_DISABLE_ESPNOW | ||||||||||||||||||||||||||||||||||||||||
| // ESP-NOW message sent callback function | ||||||||||||||||||||||||||||||||||||||||
| void espNowSentCB(uint8_t* address, uint8_t status) { | ||||||||||||||||||||||||||||||||||||||||
| DEBUG_PRINTF_P(PSTR("Message sent to " MACSTR ", status: %d\n"), MAC2STR(address), status); | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| // ESP-NOW message receive callback function | ||||||||||||||||||||||||||||||||||||||||
| void espNowReceiveCB(uint8_t* address, uint8_t* data, uint8_t len, signed int rssi, bool broadcast) { | ||||||||||||||||||||||||||||||||||||||||
| sprintf_P(last_signal_src, PSTR("%02x%02x%02x%02x%02x%02x"), address[0], address[1], address[2], address[3], address[4], address[5]); | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| #ifdef WLED_DEBUG | ||||||||||||||||||||||||||||||||||||||||
| DEBUG_PRINT(F("ESP-NOW: ")); DEBUG_PRINT(last_signal_src); DEBUG_PRINT(F(" -> ")); DEBUG_PRINTLN(len); | ||||||||||||||||||||||||||||||||||||||||
| for (int i=0; i<len; i++) DEBUG_PRINTF_P(PSTR("%02x "), data[i]); | ||||||||||||||||||||||||||||||||||||||||
| DEBUG_PRINTLN(); | ||||||||||||||||||||||||||||||||||||||||
| #endif | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| // usermods hook can override processing | ||||||||||||||||||||||||||||||||||||||||
| if (UsermodManager::onEspNowMessage(address, data, len)) return; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| bool knownRemote = false; | ||||||||||||||||||||||||||||||||||||||||
| for (const auto& mac : linked_remotes) { | ||||||||||||||||||||||||||||||||||||||||
| if (strlen(mac.data()) == 12 && strcmp(last_signal_src, mac.data()) == 0) { | ||||||||||||||||||||||||||||||||||||||||
| knownRemote = true; | ||||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| if (!knownRemote) { | ||||||||||||||||||||||||||||||||||||||||
| DEBUG_PRINT(F("ESP Now Message Received from Unlinked Sender: ")); | ||||||||||||||||||||||||||||||||||||||||
| DEBUG_PRINTLN(last_signal_src); | ||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| // handle WiZ Mote data | ||||||||||||||||||||||||||||||||||||||||
| if (data[0] == 0x91 || data[0] == 0x81 || data[0] == 0x80) { | ||||||||||||||||||||||||||||||||||||||||
| handleWiZdata(data, len); | ||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| partial_packet_t *buffer = reinterpret_cast<partial_packet_t *>(data); | ||||||||||||||||||||||||||||||||||||||||
| if (len < 3 || !broadcast || buffer->magic != 'W' || !useESPNowSync || WLED_CONNECTED) { | ||||||||||||||||||||||||||||||||||||||||
| DEBUG_PRINTLN(F("ESP-NOW unexpected packet, not syncing or connected to WiFi.")); | ||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| static uint8_t *udpIn = nullptr; | ||||||||||||||||||||||||||||||||||||||||
| static uint8_t packetsReceived = 0; | ||||||||||||||||||||||||||||||||||||||||
| static uint8_t segsReceived = 0; | ||||||||||||||||||||||||||||||||||||||||
| static unsigned long lastProcessed = 0; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| if (buffer->packet == 0) { | ||||||||||||||||||||||||||||||||||||||||
| packetsReceived = 0; // it will increment later (this is to make sure we start counting packets correctly) | ||||||||||||||||||||||||||||||||||||||||
| if (udpIn == nullptr) { | ||||||||||||||||||||||||||||||||||||||||
| udpIn = (uint8_t *)malloc(WLEDPACKETSIZE); // we cannot use stack as we are in callback | ||||||||||||||||||||||||||||||||||||||||
| if (!udpIn) return; // memory alocation failed | ||||||||||||||||||||||||||||||||||||||||
| DEBUG_PRINTLN(F("ESP-NOW inited UDP buffer.")); | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| memcpy(udpIn, buffer->data, len-3); // global data (41 bytes + up to 5 segments) | ||||||||||||||||||||||||||||||||||||||||
| segsReceived = (len - 3 - 41) / UDP_SEG_SIZE; | ||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+59
to
+67
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win A short first fragment leaves the reassembly buffer partly uninitialized.
Reject a first fragment shorter than 44 bytes, and clamp the copy to the destination size. 🛡️ Proposed fix if (buffer->packet == 0) {
+ if (len < 44 || (size_t)(len - 3) > WLEDPACKETSIZE) return; // need at least the 41-byte global block
packetsReceived = 0; // it will increment later (this is to make sure we start counting packets correctly)
if (udpIn == nullptr) {
udpIn = (uint8_t *)malloc(WLEDPACKETSIZE); // we cannot use stack as we are in callback
if (!udpIn) return; // memory alocation failed
DEBUG_PRINTLN(F("ESP-NOW inited UDP buffer."));
}
memcpy(udpIn, buffer->data, len-3); // global data (41 bytes + up to 5 segments)
segsReceived = (len - 3 - 41) / UDP_SEG_SIZE;This code is moved rather than newly written, so the defect may predate the PR. As per path instructions, ESP-NOW raw messages input is an untrusted ingress point where input validation must be enforced. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI AgentsSource: Path instructions |
||||||||||||||||||||||||||||||||||||||||
| } else if (buffer->packet == packetsReceived && udpIn && ((len - 3) / UDP_SEG_SIZE) * UDP_SEG_SIZE == (len-3)) { | ||||||||||||||||||||||||||||||||||||||||
| // we received a packet full of segments | ||||||||||||||||||||||||||||||||||||||||
| if (segsReceived >= MAX_NUM_SEGMENTS) { | ||||||||||||||||||||||||||||||||||||||||
| // we are already past max segments, just ignore | ||||||||||||||||||||||||||||||||||||||||
| DEBUG_PRINTLN(F("ESP-NOW received segments past maximum.")); | ||||||||||||||||||||||||||||||||||||||||
| len = 3; | ||||||||||||||||||||||||||||||||||||||||
| } else if ((segsReceived + ((len - 3) / UDP_SEG_SIZE)) >= MAX_NUM_SEGMENTS) { | ||||||||||||||||||||||||||||||||||||||||
| len = ((MAX_NUM_SEGMENTS - segsReceived) * UDP_SEG_SIZE) + 3; // we have reached max number of segments | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| if (len > 3) { | ||||||||||||||||||||||||||||||||||||||||
| memcpy(udpIn + 41 + (segsReceived * UDP_SEG_SIZE), buffer->data, len-3); | ||||||||||||||||||||||||||||||||||||||||
| segsReceived += (len - 3) / UDP_SEG_SIZE; | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||
| // any out of order packet or incorrectly sized packet or if we have no UDP buffer will abort | ||||||||||||||||||||||||||||||||||||||||
| DEBUG_PRINTF_P(PSTR("ESP-NOW incorrect packet: %d (%d) [%d]\n"), (int)buffer->packet, (int)len-3, (int)UDP_SEG_SIZE); | ||||||||||||||||||||||||||||||||||||||||
| if (udpIn) free(udpIn); | ||||||||||||||||||||||||||||||||||||||||
| udpIn = nullptr; | ||||||||||||||||||||||||||||||||||||||||
| packetsReceived = 0; | ||||||||||||||||||||||||||||||||||||||||
| segsReceived = 0; | ||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| if (!udpIn) return; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| packetsReceived++; | ||||||||||||||||||||||||||||||||||||||||
| DEBUG_PRINTF_P(PSTR("ESP-NOW packet received: %d (%d/%d) s:[%d/%d]\n"), (int)buffer->packet, (int)packetsReceived, (int)buffer->noOfPackets, (int)segsReceived, MAX_NUM_SEGMENTS); | ||||||||||||||||||||||||||||||||||||||||
| if (packetsReceived >= buffer->noOfPackets) { | ||||||||||||||||||||||||||||||||||||||||
| // last packet received | ||||||||||||||||||||||||||||||||||||||||
| if (millis() - lastProcessed > 250) { | ||||||||||||||||||||||||||||||||||||||||
| DEBUG_PRINTLN(F("ESP-NOW processing complete message.")); | ||||||||||||||||||||||||||||||||||||||||
| parseNotifyPacket(udpIn); | ||||||||||||||||||||||||||||||||||||||||
| lastProcessed = millis(); | ||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||
| DEBUG_PRINTLN(F("ESP-NOW ignoring complete message.")); | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| free(udpIn); | ||||||||||||||||||||||||||||||||||||||||
| udpIn = nullptr; | ||||||||||||||||||||||||||||||||||||||||
| packetsReceived = 0; | ||||||||||||||||||||||||||||||||||||||||
| segsReceived = 0; | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| #endif | ||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| #include "wled.h" | ||
|
|
||
| /* | ||
| * Realtime state machine shared by every "external source pushes raw pixel data at us" | ||
| * protocol (Hyperion, TPM2.NET, legacy UDP realtime - see realtime_udp.cpp). This is | ||
| * infrastructure, not a protocol of its own. | ||
| */ | ||
|
|
||
| // realtimeLock() is called from UDP notifications, JSON API or serial Ada | ||
| void realtimeLock(uint32_t timeoutMs, byte md) | ||
| { | ||
| if (!realtimeMode && !realtimeOverride) { | ||
| if (useMainSegmentOnly) { | ||
| Segment& mainseg = strip.getMainSegment(); | ||
| mainseg.clear(); // clear entire segment (in case sender transmits less pixels) | ||
| mainseg.freeze = true; | ||
| // if WLED was off and using main segment only, freeze non-main segments so they stay off | ||
| if (bri == 0) { | ||
| for (size_t s = 0; s < strip.getSegmentsNum(); s++) strip.getSegment(s).freeze = true; | ||
| } | ||
| } else { | ||
| // clear entire strip | ||
| strip.fill(BLACK); | ||
| } | ||
| // if strip is off (bri==0) and not already in RTM | ||
| if (briT == 0) { | ||
| strip.setBrightness(briLast, true); | ||
| } | ||
| } | ||
|
|
||
| if (realtimeTimeout != UINT32_MAX) { | ||
| realtimeTimeout = (timeoutMs == 255001 || timeoutMs == 65000) ? UINT32_MAX : millis() + timeoutMs; | ||
| } | ||
| realtimeMode = md; | ||
|
|
||
| if (realtimeOverride) return; | ||
| if (arlsForceMaxBri) strip.setBrightness(255, true); | ||
| if (briT > 0 && md == REALTIME_MODE_GENERIC) strip.show(); | ||
| } | ||
|
|
||
| void exitRealtime() { | ||
| if (!realtimeMode) return; | ||
| if (realtimeOverride == REALTIME_OVERRIDE_ONCE) realtimeOverride = REALTIME_OVERRIDE_NONE; | ||
| strip.setBrightness(bri, true); | ||
| realtimeTimeout = 0; // cancel realtime mode immediately | ||
| realtimeMode = REALTIME_MODE_INACTIVE; // inform UI immediately | ||
| realtimeIP[0] = 0; | ||
| if (useMainSegmentOnly) { // unfreeze live segment again | ||
| strip.getMainSegment().freeze = false; | ||
| strip.trigger(); | ||
| } else { | ||
| strip.show(); // possible fix for #3589 | ||
| } | ||
| updateInterfaces(CALL_MODE_WS_SEND); | ||
| } | ||
|
|
||
| void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w) | ||
| { | ||
| unsigned pix = i + arlsOffset; | ||
| strip.setRealtimePixelColor(pix, RGBW32(r,g,b,w)); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,153 @@ | ||
| #include "wled.h" | ||
|
|
||
| /*********************************************************************************************\ | ||
| * Art-Net, DDP, E131 output - work in progress | ||
| * (this is the outbound/sender side; see e131.cpp for the inbound receiver side of these | ||
| * same wire protocols) | ||
| \*********************************************************************************************/ | ||
|
|
||
| // | ||
| // Send real time UDP updates to the specified client | ||
| // | ||
| // type - protocol type (0=DDP, 1=E1.31, 2=ArtNet) | ||
| // client - the IP address to send to | ||
| // length - the number of pixels | ||
| // buffer - a buffer of at least length*4 bytes long | ||
| // isRGBW - true if the buffer contains 4 components per pixel | ||
|
|
||
| static size_t sequenceNumber = 0; // this needs to be shared across all outputs | ||
| static const size_t ART_NET_HEADER_SIZE = 12; | ||
| static const byte ART_NET_HEADER[] PROGMEM = {0x41,0x72,0x74,0x2d,0x4e,0x65,0x74,0x00,0x00,0x50,0x00,0x0e}; | ||
|
|
||
| uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, const uint8_t *buffer, uint8_t bri, bool isRGBW) { | ||
| if (!(apActive || interfacesInited) || !client[0] || !length) return 1; // network not initialised or dummy/unset IP address 031522 ajn added check for ap | ||
|
|
||
| WiFiUDP ddpUdp; | ||
|
|
||
| switch (type) { | ||
| case 0: // DDP | ||
| { | ||
| // calculate the number of UDP packets we need to send | ||
| size_t channelCount = length * (isRGBW? 4:3); // 1 channel for every R,G,B value | ||
| size_t packetCount = ((channelCount-1) / DDP_CHANNELS_PER_PACKET) +1; | ||
|
|
||
| // there are 3 channels per RGB pixel | ||
| uint32_t channel = 0; // TODO: allow specifying the start channel | ||
| // the current position in the buffer | ||
| size_t bufferOffset = 0; | ||
|
|
||
| for (size_t currentPacket = 0; currentPacket < packetCount; currentPacket++) { | ||
| if (sequenceNumber > 15) sequenceNumber = 0; | ||
|
|
||
| if (!ddpUdp.beginPacket(client, DDP_DEFAULT_PORT)) { // port defined in ESPAsyncE131.h | ||
| //DEBUG_PRINTLN(F("WiFiUDP.beginPacket returned an error")); | ||
| return 1; // problem | ||
| } | ||
|
|
||
| // the amount of data is AFTER the header in the current packet | ||
| size_t packetSize = DDP_CHANNELS_PER_PACKET; | ||
|
|
||
| uint8_t flags = DDP_FLAGS_VER1; | ||
| if (currentPacket == (packetCount - 1U)) { | ||
| // last packet, set the push flag | ||
| // TODO: determine if we want to send an empty push packet to each destination after sending the pixel data | ||
| flags = DDP_FLAGS_VER1 | DDP_FLAGS_PUSH; | ||
| if (channelCount % DDP_CHANNELS_PER_PACKET) { | ||
| packetSize = channelCount % DDP_CHANNELS_PER_PACKET; | ||
| } | ||
| } | ||
|
|
||
| // write the header | ||
| /*0*/ddpUdp.write(flags); | ||
| // TODO: sequence number should be 1-15 as 0 means "unused", it has no bad consequences other than out of sequence packet may be accepted | ||
| /*1*/ddpUdp.write(sequenceNumber++ & 0x0F); // sequence may be unnecessary unless we are sending twice (as requested in Sync settings) | ||
| /*2*/ddpUdp.write(isRGBW ? DDP_TYPE_RGBW32 : DDP_TYPE_RGB24); | ||
| /*3*/ddpUdp.write(DDP_ID_DISPLAY); | ||
| // data offset in bytes, 32-bit number, MSB first | ||
| /*4*/ddpUdp.write(0xFF & (channel >> 24)); | ||
| /*5*/ddpUdp.write(0xFF & (channel >> 16)); | ||
| /*6*/ddpUdp.write(0xFF & (channel >> 8)); | ||
| /*7*/ddpUdp.write(0xFF & (channel )); | ||
| // data length in bytes, 16-bit number, MSB first | ||
| /*8*/ddpUdp.write(0xFF & (packetSize >> 8)); | ||
| /*9*/ddpUdp.write(0xFF & (packetSize )); | ||
|
|
||
| // write the colors, the write write(const uint8_t *buffer, size_t size) | ||
| // function is just a loop internally too | ||
| for (size_t i = 0; i < packetSize; i += (isRGBW?4:3)) { | ||
| ddpUdp.write(scale8(buffer[bufferOffset++], bri)); // R | ||
| ddpUdp.write(scale8(buffer[bufferOffset++], bri)); // G | ||
| ddpUdp.write(scale8(buffer[bufferOffset++], bri)); // B | ||
| if (isRGBW) ddpUdp.write(scale8(buffer[bufferOffset++], bri)); // W | ||
| } | ||
|
|
||
| if (!ddpUdp.endPacket()) { | ||
| //DEBUG_PRINTLN(F("WiFiUDP.endPacket returned an error")); | ||
| return 1; // problem | ||
| } | ||
|
|
||
| channel += packetSize; | ||
| } | ||
| } break; | ||
|
|
||
| case 1: //E1.31 | ||
| { | ||
| } break; | ||
|
|
||
| case 2: //ArtNet | ||
| { | ||
| // calculate the number of UDP packets we need to send | ||
| const size_t channelCount = length * (isRGBW?4:3); // 1 channel for every R,G,B,(W?) value | ||
| const size_t ARTNET_CHANNELS_PER_PACKET = isRGBW?512:510; // 512/4=128 RGBW LEDs, 510/3=170 RGB LEDs | ||
| const size_t packetCount = ((channelCount-1)/ARTNET_CHANNELS_PER_PACKET)+1; | ||
|
|
||
| uint32_t channel = 0; | ||
| size_t bufferOffset = 0; | ||
|
|
||
| sequenceNumber++; | ||
|
|
||
| for (size_t currentPacket = 0; currentPacket < packetCount; currentPacket++) { | ||
|
|
||
| if (sequenceNumber > 255) sequenceNumber = 0; | ||
|
|
||
| if (!ddpUdp.beginPacket(client, ARTNET_DEFAULT_PORT)) { | ||
| DEBUG_PRINTLN(F("Art-Net WiFiUDP.beginPacket returned an error")); | ||
| return 1; // borked | ||
| } | ||
|
|
||
| size_t packetSize = ARTNET_CHANNELS_PER_PACKET; | ||
|
|
||
| if (currentPacket == (packetCount - 1U)) { | ||
| // last packet | ||
| if (channelCount % ARTNET_CHANNELS_PER_PACKET) { | ||
| packetSize = channelCount % ARTNET_CHANNELS_PER_PACKET; | ||
| } | ||
| } | ||
|
|
||
| byte header_buffer[ART_NET_HEADER_SIZE]; | ||
| memcpy_P(header_buffer, ART_NET_HEADER, ART_NET_HEADER_SIZE); | ||
| ddpUdp.write(header_buffer, ART_NET_HEADER_SIZE); // This doesn't change. Hard coded ID, OpCode, and protocol version. | ||
| ddpUdp.write(sequenceNumber & 0xFF); // sequence number. 1..255 | ||
| ddpUdp.write(0x00); // physical - more an FYI, not really used for anything. 0..3 | ||
| ddpUdp.write((currentPacket) & 0xFF); // Universe LSB. 1 full packet == 1 full universe, so just use current packet number. | ||
| ddpUdp.write(0x00); // Universe MSB, unused. | ||
| ddpUdp.write(0xFF & (packetSize >> 8)); // 16-bit length of channel data, MSB | ||
| ddpUdp.write(0xFF & (packetSize )); // 16-bit length of channel data, LSB | ||
|
|
||
| for (size_t i = 0; i < packetSize; i += (isRGBW?4:3)) { | ||
| ddpUdp.write(scale8(buffer[bufferOffset++], bri)); // R | ||
| ddpUdp.write(scale8(buffer[bufferOffset++], bri)); // G | ||
| ddpUdp.write(scale8(buffer[bufferOffset++], bri)); // B | ||
| if (isRGBW) ddpUdp.write(scale8(buffer[bufferOffset++], bri)); // W | ||
| } | ||
|
|
||
| if (!ddpUdp.endPacket()) { | ||
| DEBUG_PRINTLN(F("Art-Net WiFiUDP.endPacket returned an error")); | ||
| return 1; // borked | ||
| } | ||
| channel += packetSize; | ||
| } | ||
| } break; | ||
| } | ||
| return 0; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Validate
lenbefore readingdata[0].Line 43 reads
data[0], and line 22 iteratesdata[i]in the debug block. The only length check is at line 49. If a peer sends a zero-length ESP-NOW frame, line 43 reads out of bounds. Move a minimum-length guard to the top of the callback.🛡️ Proposed fix
void espNowReceiveCB(uint8_t* address, uint8_t* data, uint8_t len, signed int rssi, bool broadcast) { + if (len == 0) return; sprintf_P(last_signal_src, PSTR("%02x%02x%02x%02x%02x%02x"), address[0], address[1], address[2], address[3], address[4], address[5]);This code is moved rather than newly written, so the defect may predate the PR.
As per path instructions, ESP-NOW raw messages input is an untrusted ingress point where bounds checking must be enforced.
📝 Committable suggestion
🤖 Prompt for AI Agents
Source: Path instructions