Skip to content

Fix UBSan error in stream trim when processing last entry#14669

Merged
sundb merged 1 commit intoredis:unstablefrom
sundb:fix-stream-invalid-delta
Jan 7, 2026
Merged

Fix UBSan error in stream trim when processing last entry#14669
sundb merged 1 commit intoredis:unstablefrom
sundb:fix-stream-invalid-delta

Conversation

@sundb
Copy link
Copy Markdown
Collaborator

@sundb sundb commented Jan 7, 2026

Summary

This bus was introduced by #14623

Before PR #14623, when a stream node was going to be fully removed, we would just delete the whole node directly instead of iterating through and deleting each entry.

Now, with the XTRIM / XADD flags, we have to iterate and delete entries one by one. However, the implementation in issue #8169 didn’t consider the case where all entries are removed, so p can end up being NULL.

Fixes an UndefinedBehaviorSanitizer error in streamTrim() when marking the last entry in a listpack as deleted. The issue occurs when performing pointer arithmetic on a NULL pointer after lpNext() reaches the end of the listpack.

Solution

If p is NULL, we skip the delta calculation and skip the calculation of new p.

Failed CI: https://github.com/redis/redis/actions/runs/20766104241/job/59632440335

@sundb sundb requested a review from sggeorgiev January 7, 2026 04:17
@sundb sundb added this to Redis 8.6 Jan 7, 2026
@github-project-automation github-project-automation bot moved this to Todo in Redis 8.6 Jan 7, 2026
intptr_t delta = p - lp;
intptr_t delta = p ? (p - lp) : 0; /* p may be NULL if this was the last entry */
flags |= STREAM_ITEM_FLAG_DELETED;
lp = lpReplaceInteger(lp, &pcopy, flags);
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.

It looks like lpReplaceInteger doesn't modify the lp because the size of flags is same. In this case you can remove delta and updating of p.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

lpReplaceInteger will still change its size because the encoding is determined by the size of lval, not the type of lval. However, in theory, the length will not change afte | STREAM_ITEM_FLAG_DELETED(1), but i still prefer to keep it.

@sundb
Copy link
Copy Markdown
Collaborator Author

sundb commented Jan 7, 2026

Fully CI: https://github.com/sundb/redis/actions/runs/20770473823
the failed tests are not related to this PR.

@sundb sundb merged commit 85ab4ca into redis:unstable Jan 7, 2026
19 checks passed
@github-project-automation github-project-automation bot moved this from Todo to Done in Redis 8.6 Jan 7, 2026
@sundb sundb deleted the fix-stream-invalid-delta branch January 7, 2026 12:51
sundb added a commit to sundb/redis that referenced this pull request Jan 27, 2026
## Summary

This bus was introduced by redis#14623

Before PR redis#14623, when a stream node was going to be fully removed, we
would just delete the whole node directly instead of iterating through
and deleting each entry.

Now, with the XTRIM/XADD flags, we have to iterate and delete entries
one by one. However, the implementation in issue redis#8169 didn’t consider
the case where all entries are removed, so `p` can end up being NULL.

Fixes an UndefinedBehaviorSanitizer error in `streamTrim()` when marking
the last entry in a listpack as deleted. The issue occurs when
performing pointer arithmetic on a NULL pointer after `lpNext()` reaches
the end of the listpack.

## Solution
If p is NULL, we skip the delta calculation and the calculation of
new `p`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants