From: Ruide Cao <[email protected]>

batman-adv keeps a running payload length for queued fragments and uses it
to validate a fragment chain before reassembly.

That accounting currently uses a type that can truncate the accumulated
value. As a result, malformed fragment chains can bypass the intended
validation and drive reassembly with inconsistent length state, leading to
a local denial of service.

Fix the accounting by using a non-truncating type for the accumulated
fragment length so the existing validation logic always operates on the
real value.

The fix was verified against the original reproducer and against valid
fragment reassembly paths.

Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge")
Cc: [email protected]
Reported-by: Yuan Tan <[email protected]>
Reported-by: Yifan Wu <[email protected]>
Reported-by: Juefei Pu <[email protected]>
Reported-by: Xin Liu <[email protected]>
Signed-off-by: Ruide Cao <[email protected]>
Tested-by: Ren Wei <[email protected]>
Signed-off-by: Ren Wei <[email protected]>
---
 net/batman-adv/fragmentation.c | 12 +++++++-----
 net/batman-adv/types.h         |  2 +-
 2 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c
index f4e45cc25816..e4472dbef4d9 100644
--- a/net/batman-adv/fragmentation.c
+++ b/net/batman-adv/fragmentation.c
@@ -80,9 +80,9 @@ void batadv_frag_purge_orig(struct batadv_orig_node 
*orig_node,
  *
  * Return: the maximum size of payload that can be fragmented.
  */
-static int batadv_frag_size_limit(void)
+static u32 batadv_frag_size_limit(void)
 {
-       int limit = BATADV_FRAG_MAX_FRAG_SIZE;
+       u32 limit = BATADV_FRAG_MAX_FRAG_SIZE;
 
        limit -= sizeof(struct batadv_frag_packet);
        limit *= BATADV_FRAG_MAX_FRAGMENTS;
@@ -141,6 +141,7 @@ static bool batadv_frag_insert_packet(struct 
batadv_orig_node *orig_node,
        struct batadv_frag_list_entry *frag_entry_new = NULL, *frag_entry_curr;
        struct batadv_frag_list_entry *frag_entry_last = NULL;
        struct batadv_frag_packet *frag_packet;
-       u8 bucket;
-       u16 seqno, hdr_size = sizeof(struct batadv_frag_packet);
+       u32 data_len;
+       u16 seqno, hdr_size = sizeof(struct batadv_frag_packet);
+       u8 bucket;
        bool ret = false;
@@ -153,6 +154,7 @@ static bool batadv_frag_insert_packet(struct 
batadv_orig_node *orig_node,
                goto err;
 
        frag_packet = (struct batadv_frag_packet *)skb->data;
+       data_len = skb->len - hdr_size;
        seqno = ntohs(frag_packet->seqno);
        bucket = seqno % BATADV_FRAG_BUFFER_COUNT;
 
@@ -171,7 +173,7 @@ static bool batadv_frag_insert_packet(struct 
batadv_orig_node *orig_node,
        spin_lock_bh(&chain->lock);
        if (batadv_frag_init_chain(chain, seqno)) {
                hlist_add_head(&frag_entry_new->list, &chain->fragment_list);
-               chain->size = skb->len - hdr_size;
+               chain->size = data_len;
                chain->timestamp = jiffies;
                chain->total_size = ntohs(frag_packet->total_size);
                ret = true;
@@ -188,7 +190,7 @@ static bool batadv_frag_insert_packet(struct 
batadv_orig_node *orig_node,
                if (frag_entry_curr->no < frag_entry_new->no) {
                        hlist_add_before(&frag_entry_new->list,
                                         &frag_entry_curr->list);
-                       chain->size += skb->len - hdr_size;
+                       chain->size += data_len;
                        chain->timestamp = jiffies;
                        ret = true;
                        goto out;
@@ -201,7 +203,7 @@ static bool batadv_frag_insert_packet(struct 
batadv_orig_node *orig_node,
        /* Reached the end of the list, so insert after 'frag_entry_last'. */
        if (likely(frag_entry_last)) {
                hlist_add_behind(&frag_entry_new->list, &frag_entry_last->list);
-               chain->size += skb->len - hdr_size;
+               chain->size += data_len;
                chain->timestamp = jiffies;
                ret = true;
        }
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index 8fc5fe0e9b05..96ea4c70fd59 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -300,7 +300,7 @@ struct batadv_frag_table_entry {
        u16 seqno;
 
        /** @size: accumulated size of packets in list */
-       u16 size;
+       u32 size;
 
        /** @total_size: expected size of the assembled packet */
        u16 total_size;
-- 
2.34.1

Reply via email to