-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
less restrictive DDP header acceptance, add safety checks to all protocols #5547
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
Merged
Merged
Changes from 3 commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
3be525c
less restrictive DDP header acceptance, add safety checks to all prot…
DedeHai c9c337a
typo
DedeHai 5fe9806
indentation
DedeHai 16c59d4
update comments
DedeHai 017c583
Add DDP_TYPE_UNDEF definition for undefined type
DedeHai 2749883
reject query, reply and "from storage" requests
DedeHai 8255f94
fix two off-by-1 errors
DedeHai e17983e
missed a "="
DedeHai 446b2cd
another check for malformed packet
DedeHai dedafc4
clamp dmxChannels to MAX_CHANNELS_PER_UNIVERSE
DedeHai 8a5e7e4
bugfix in data length calculation
DedeHai e5463fa
bugfix in parsePacket(): accept short artned packets
DedeHai File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
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.
@coderabbitai please check if WLED handling of sequence numbers is compatible with DDP protocol specs. Accepting an "outdated" packet may be acceptable, but a wrong rejection should be avoided, even when more than 15 packets are needed to transmit a full "frame". Also check against the real implementation of known DDP senders like LedFx, hyperion, xlights, OpenRGB, FPP.
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.
Let me research the DDP protocol spec and how known senders handle sequence numbers.
@softhack007Here is a detailed analysis of WLED's DDP sequence number handling against the spec and known senders.DDP Sequence Number Spec
The DDP spec (byte 1) defines
nnnnas a sequence number from 1–15, or zero if not used, and states the sequence number should be incremented with each new packet sent. A sender can send duplicate packets with the same sequence number and DDP header for redundancy. The sequence number is ignored if zero.Key point: the sequence number is per-packet (not per-frame), and wraps in the ring 1→2→…→15→1.
WLED's Rejection Logic
WLED's current code (after this PR):
lastPushSeqis the sequence number of the last push packet received. The window rejects any packet whosesnfalls in the 5 slots immediately precedinglastPushSeq, on the assumption those are late-arriving packets from the previous frame.Where This Breaks Down
The 5-slot window hardcodes an assumption of ≤5 packets per frame.
Consider a large installation with, e.g., 5760 LEDs (12 packets of 480 RGB pixels @ 1440 bytes each, a common xLights packet size):
lastPushSeq = 11sn ∈ {7, 8, 9, 10}12, 13, 14, 15, 1, 2, 3, 4, 5, 6, 7, 8→ push atsn = 9sn = 7andsn = 8from the new frame fall inside the reject window and are wrongly dropped, causing visual corruption/flicker on those LED ranges mid-frame.This means for any installation needing roughly >10 packets per frame (with sequence numbers enabled), current-frame packets will be wrongly rejected. OpenRGB's DDP implementation uses sequence numbers from 1–15 per spec, and xLights defaults to 1440 bytes per DDP packet, which is 480 RGB pixels — meaning only ~7–8 packets are needed before wrapping issues arise for mid-size installs.
Known Sender Behaviors
Senders that set
sn = 0(disabled) bypass this check entirely and are unaffected.Summary of Issues
Wrong rejections are possible: For frames requiring >~10 packets with sequence numbers enabled, valid new-frame packets whose
snfalls in the 5-slot window behindlastPushSeq(after wrap) are silently dropped. The PR comment "if more are used and packets are very late, they are still accepted" is not fully accurate for this wrap-around case.The 15-value ring is too small: With only 15 sequence numbers and a 5-slot window, there's only room for about 10 "safe" slots per frame. Any frame needing more packets will cycle through the reject window.
Safe path exists: If the sender sets
sn = 0(sequence checking disabled), WLED's code correctly skips all rejection.Recommendations
sn = 0for large installations.sn = 0in WLED's own output), since DDP spec explicitly allows ignoring it. The spec does not mandate receivers implement rejection.🧠 Learnings used
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.
@softhack007 I looked at this code in detail, it is not perfect but rejecting "late packets" seems reasonable. if large setups are needed, just disable this option in the sync settings (it is disabled by default), it is not possible to have both, late packet rejection AND a large setup as the sequence number for DDP is limited to 1-15 and to check for late packets, a max window of 7 would be possible.
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.
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.
@DedeHai @coderabbitai I was actually wondering if we could make improvements for #5412 while working on the code.
The DDP spec reads like they wanted to support packet re-transmit within one "push frame", so the sequence counter could be used to identify duplicates (in a history window of 15 packets max). I think dups (same frame) are not a problem in WLED, because they simply cause a re-painting of some pixels. You're right that rejecting late packets - from the previous "push frame" - might be useful in some setups - especially with high latency / unstable WiFi and with lower pixel counts. My fear is that users will simply enable "Skip out-of-sequence packets" (which makes sense for other protocols like Art-Net), and later complain that a bigger setup always misses pixel updates when sequence checks are enabled.
I'm not sure atm where is the "breaking limit" (matrix size) when sequence checking will start to mis-behave. But as this is a legacy "buggy feature" I could live with a use-as-is decision 🤔, so we still support the previous "proper use" cases.
What do you think?
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.
🧩 Analysis chain
@softhack007, here's my analysis of the sequence number situation and whether improvements are practical for this PR.Exact Breaking-Point Calculation
The rejection window always excludes exactly 4 sequence numbers as "late from previous frame":
lastPushSeq > 5: rejectssn ∈ (lastPushSeq−5, lastPushSeq)— e.g. with push at sn=10, rejects {6,7,8,9}lastPushSeq ≤ 5: rejects viasn > (10+lastPushSeq) || sn < lastPushSeq— e.g. with push at sn=3, rejects {14,15,1,2}So: 15 total − 1 (zero/disabled) − 4 (rejected window) = 10 safe data-packet slots + 1 for the push packet itself = 11 packets max per frame without false-rejection.
At xLights' default 1440 bytes/packet:
For typical WLED ESP32 setups that max out around 1,000–2,500 LEDs, this is usually fine. The problematic zone is large matrix setups (2,000–8,000+ LEDs with RGBW), which is exactly the target use case for
#5412.Can We Improve It?
Several options with different trade-offs:
sn=0pathThe fundamental constraint is the DDP spec's 4-bit sequence number (1-15 ring). No algorithm can safely distinguish "late from previous frame" from "early in new frame" with only 15 values and an unbounded number of packets per frame.
Recommendation
Given that:
e131SkipOutOfSequence), as@DedeHainotedI'd suggest accepting use-as-is for this PR but adding a comment near the rejection logic that documents the limit, so future contributors understand why the feature can't safely be extended to large setups:
This way the behavior is documented, and users/maintainers know the safe operating envelope without any code risk from changing the window algorithm. The
#5412improvement (if it relates to large-matrix sequence behavior) could then be tracked as a separate, more surgical change.🏁 Script executed:
Length of output: 0
🌐 Web query:
💡 Result:
GitHub issue wled/WLED
#5412(opened Mar 9, 2026) reports a potential bug in WLED’s DDP packet sequence / out‑of‑order handling when the setting “skip out-of sequence packets” is enabled. [1]wled00/e131.cpp, WLED uses only the low 4 bits of the DDPsequenceNum(sequenceNum & 0xF). [1]lastPushSeqand a 5-sequence range) to reject “late packets belonging to previous frame”. [1]DDP itself is the UDP protocol WLED listens for on port 4048. [2]
Sources: [1] [2]
@softhack007, confirmed — issue#5412reports exactly this problem: large installations like HUB75 64×64 streaming split a single frame across more than ~4–5 DDP packets, and the code's "4 packets max before push" assumption causes valid packets to be discarded. My earlier analysis lines up with the issue report.To summarize the math concisely:
sn > (lastPushSeq−5) && sn < lastPushSeq, which spans 4 values, not 5).For this PR, I'd suggest the pragmatic "use-as-is + document the limit" approach, since:
e131SkipOutOfSequence)A targeted comment referencing
#5412would help future contributors understand the constraint:This keeps the PR focused on the safety fixes it targets, and creates a clear breadcrumb for whoever addresses
#5412properly in a future dedicated PR.🧠 Learnings used
Uh oh!
There was an error while loading. Please reload this page.
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.
Ok, so with RGB (not RGBW which is totally uncommon in large displays, and not supported for HUB75), false rejections start above ~ 82x64 pixels.
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.
It's not perfect and the fix is to uncheck that option, its more a question of documentation. Until looking into this code for this PR I did not even know that option existed :)
We can always refine at a later point if this becomes an issue with growing number of HUB75 users. The key point of this PR is to make DDP as compatible as we dare to at the cost of accepting wrong packets that lead to a screwed up display (which will be rare).
What I am most concerned about in this PR is rejecting valid packets in non DDP protocols, which may be the case if the rabbit is correct in its statement above on artnet packets.
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.
so if we can not verify that to be working in a real setup, I'd rather comment out those packet size checks for now as this PR will block 16.0 release until done.
Uh oh!
There was an error while loading. Please reload this page.
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.
Yes, good point. As testing is very hard to do, especially we don't know much about all the possible senders - maybe this PR should strictly focus on "prevent array bounds violations" and on DDP. Which means, any clamping that is not driven by array/buffer bounds should be commented out.