mirror of
https://github.com/torvalds/linux.git
synced 2026-05-31 10:33:41 +02:00
batman-adv: fix fragment reassembly length accounting
batman-adv keeps a running payload length for queued fragments and uses it
to validate a fragment chain before reassembly.
That accounting currently allows the accumulated fragment length to be
truncated during updates. 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 storing the accumulated length in a length-typed
field and rejecting update overflows before the existing validation logic
runs.
The fix was verified against the original reproducer and against valid
fragment reassembly paths.
Fixes: 610bfc6bc9 ("batman-adv: Receive fragmented packets and merge")
Cc: stable@kernel.org
Reported-by: Yuan Tan <yuantan098@gmail.com>
Reported-by: Yifan Wu <yifanwucs@gmail.com>
Reported-by: Juefei Pu <tomapufckgml@gmail.com>
Reported-by: Xin Liu <bird@lzu.edu.cn>
Signed-off-by: Ruide Cao <caoruide123@gmail.com>
Tested-by: Ren Wei <enjou1224z@gmail.com>
Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
Signed-off-by: Sven Eckelmann <sven@narfation.org>
This commit is contained in:
parent
99d9958fa1
commit
9cd3f16c32
|
|
@ -17,6 +17,7 @@
|
|||
#include <linux/lockdep.h>
|
||||
#include <linux/minmax.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/overflow.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
|
@ -80,9 +81,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 size_t batadv_frag_size_limit(void)
|
||||
{
|
||||
int limit = BATADV_FRAG_MAX_FRAG_SIZE;
|
||||
size_t limit = BATADV_FRAG_MAX_FRAG_SIZE;
|
||||
|
||||
limit -= sizeof(struct batadv_frag_packet);
|
||||
limit *= BATADV_FRAG_MAX_FRAGMENTS;
|
||||
|
|
@ -143,7 +144,9 @@ static bool batadv_frag_insert_packet(struct batadv_orig_node *orig_node,
|
|||
struct batadv_frag_packet *frag_packet;
|
||||
u8 bucket;
|
||||
u16 seqno, hdr_size = sizeof(struct batadv_frag_packet);
|
||||
bool overflow = false;
|
||||
bool ret = false;
|
||||
size_t data_len;
|
||||
|
||||
/* Linearize packet to avoid linearizing 16 packets in a row when doing
|
||||
* the later merge. Non-linear merge should be added to remove this
|
||||
|
|
@ -153,6 +156,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 +175,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 +192,11 @@ 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;
|
||||
|
||||
if (check_add_overflow(chain->size, data_len,
|
||||
&chain->size))
|
||||
overflow = true;
|
||||
|
||||
chain->timestamp = jiffies;
|
||||
ret = true;
|
||||
goto out;
|
||||
|
|
@ -201,13 +209,16 @@ 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;
|
||||
|
||||
if (check_add_overflow(chain->size, data_len, &chain->size))
|
||||
overflow = true;
|
||||
|
||||
chain->timestamp = jiffies;
|
||||
ret = true;
|
||||
}
|
||||
|
||||
out:
|
||||
if (chain->size > batadv_frag_size_limit() ||
|
||||
if (overflow || chain->size > batadv_frag_size_limit() ||
|
||||
chain->total_size != ntohs(frag_packet->total_size) ||
|
||||
chain->total_size > batadv_frag_size_limit()) {
|
||||
/* Clear chain if total size of either the list or the packet
|
||||
|
|
|
|||
|
|
@ -301,7 +301,7 @@ struct batadv_frag_table_entry {
|
|||
u16 seqno;
|
||||
|
||||
/** @size: accumulated size of packets in list */
|
||||
u16 size;
|
||||
size_t size;
|
||||
|
||||
/** @total_size: expected size of the assembled packet */
|
||||
u16 total_size;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user