mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 09:36:22 +02:00
firewire: net: Fix fragmented datagram reassembly
fwnet_frag_new() keeps a sorted list of received fragments for a partial
datagram. When a new fragment is adjacent to an existing fragment, the
code checks whether the new fragment also closes the gap to the next or
previous list entry.
Those neighbor lookups currently assume that the current fragment always
has a real next or previous fragment. At a list edge, the next or
previous entry is the list head, not a struct fwnet_fragment_info.
The gap checks also compare against the old edge of the current fragment
instead of the edge after adding the new fragment. As a result, a
fragment that bridges two existing ranges may leave two adjacent ranges
unmerged, so fwnet_pd_is_complete() can miss a complete datagram.
Check for the list head before looking up the neighboring fragment, and
compare the neighbor against the new fragment's far edge when deciding
whether to merge all three ranges.
This issue was found by a static analysis checker and confirmed by
manual source review.
Fixes: c76acec6d5 ("firewire: add IPv4 support")
Signed-off-by: Ruoyu Wang <ruoyuw560@gmail.com>
Link: https://lore.kernel.org/r/20260707150454.2265951-1-ruoyuw560@gmail.com
Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
This commit is contained in:
parent
a13c140cc2
commit
d52a13adbb
|
|
@ -297,31 +297,34 @@ static struct fwnet_fragment_info *fwnet_frag_new(
|
|||
if (fi->offset + fi->len == offset) {
|
||||
/* The new fragment can be tacked on to the end */
|
||||
/* Did the new fragment plug a hole? */
|
||||
fi2 = list_entry(fi->fi_link.next,
|
||||
struct fwnet_fragment_info, fi_link);
|
||||
if (fi->offset + fi->len == fi2->offset) {
|
||||
/* glue fragments together */
|
||||
fi->len += len + fi2->len;
|
||||
list_del(&fi2->fi_link);
|
||||
kfree(fi2);
|
||||
} else {
|
||||
fi->len += len;
|
||||
if (!list_is_last(&fi->fi_link, &pd->fi_list)) {
|
||||
fi2 = list_next_entry(fi, fi_link);
|
||||
if (offset + len == fi2->offset) {
|
||||
/* glue fragments together */
|
||||
fi->len += len + fi2->len;
|
||||
list_del(&fi2->fi_link);
|
||||
kfree(fi2);
|
||||
|
||||
return fi;
|
||||
}
|
||||
}
|
||||
fi->len += len;
|
||||
|
||||
return fi;
|
||||
}
|
||||
if (offset + len == fi->offset) {
|
||||
/* The new fragment can be tacked on to the beginning */
|
||||
/* Did the new fragment plug a hole? */
|
||||
fi2 = list_entry(fi->fi_link.prev,
|
||||
struct fwnet_fragment_info, fi_link);
|
||||
if (fi2->offset + fi2->len == fi->offset) {
|
||||
/* glue fragments together */
|
||||
fi2->len += fi->len + len;
|
||||
list_del(&fi->fi_link);
|
||||
kfree(fi);
|
||||
if (!list_is_first(&fi->fi_link, &pd->fi_list)) {
|
||||
fi2 = list_prev_entry(fi, fi_link);
|
||||
if (fi2->offset + fi2->len == offset) {
|
||||
/* glue fragments together */
|
||||
fi2->len += fi->len + len;
|
||||
list_del(&fi->fi_link);
|
||||
kfree(fi);
|
||||
|
||||
return fi2;
|
||||
return fi2;
|
||||
}
|
||||
}
|
||||
fi->offset = offset;
|
||||
fi->len += len;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user