Skip to content

Commit 0913e9e

Browse files
committed
Added fix for crashing response with large headers
There was a case when we exhaust all available room in skb and tso_fragment couldn't correctly split such skb. Now max size of skb data is: SS_SKB_MAX_DATA_LEN (SKB_MAX_HEADER + MAX_SKB_FRAGS * PAGE_SIZE)
1 parent 44f5aca commit 0913e9e

File tree

3 files changed

+54
-25
lines changed

3 files changed

+54
-25
lines changed

fw/http_msg.c

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,22 +1338,28 @@ tfw_http_msg_alloc_from_pool(TfwHttpTransIter *mit, TfwPool* pool, size_t size)
13381338
bool np;
13391339
char* addr;
13401340
TfwMsgIter *it = &mit->iter;
1341-
struct skb_shared_info *si = skb_shinfo(it->skb);
1341+
struct sk_buff *skb = it->skb;
1342+
struct skb_shared_info *si = skb_shinfo(skb);
13421343

13431344
addr = tfw_pool_alloc_not_align_np(pool, size, &np);
13441345
if (!addr)
13451346
return -ENOMEM;
13461347

1347-
if (np) {
1348-
r = ss_skb_add_frag(it->skb_head, it->skb, addr, ++it->frag,
1349-
size);
1348+
if (np || it->frag == -1) {
1349+
r = ss_skb_add_frag(it->skb_head, skb, addr, ++it->frag, size);
13501350
if (unlikely(r))
13511351
return r;
1352+
1353+
if (it->frag == MAX_SKB_FRAGS - 1) {
1354+
it->frag = -1;
1355+
if (skb != it->skb_head)
1356+
it->skb = it->skb->next;
1357+
}
13521358
} else {
13531359
skb_frag_size_add(&si->frags[it->frag], size);
13541360
}
13551361

1356-
ss_skb_adjust_data_len(it->skb, size);
1362+
ss_skb_adjust_data_len(skb, size);
13571363
mit->curr_ptr = addr;
13581364

13591365
return 0;
@@ -1378,6 +1384,7 @@ tfw_http_msg_setup_transform_pool(TfwHttpTransIter *mit, TfwPool* pool)
13781384
unsigned int room = TFW_POOL_CHUNK_ROOM(pool);
13791385

13801386
BUG_ON(room < 0);
1387+
BUG_ON(mit->iter.frag > 0);
13811388

13821389
/* Alloc a full page if room smaller than MIN_FRAG_SIZE. */
13831390
if (room < MIN_HDR_FRAG_SIZE)
@@ -1411,15 +1418,18 @@ __tfw_http_msg_expand_from_pool(TfwHttpTransIter *mit, TfwPool* pool,
14111418
void cpy(void *dest, const void *src, size_t n))
14121419
{
14131420
const TfwStr *c, *end;
1414-
unsigned int room, n_copy, rlen, off;
1421+
unsigned int room, skb_room, n_copy, rlen, off;
14151422
int r;
1423+
TfwMsgIter *it = &mit->iter;
14161424

1417-
BUG_ON(mit->iter.frag < 0);
1425+
BUG_ON(it->skb->len > SS_SKB_MAX_DATA_LEN);
14181426

14191427
TFW_STR_FOR_EACH_CHUNK(c, str, end) {
14201428
rlen = c->len;
14211429

14221430
while (rlen) {
1431+
unsigned char nr_frags;
1432+
14231433
room = TFW_POOL_CHUNK_ROOM(pool);
14241434
BUG_ON(room < 0);
14251435

@@ -1429,6 +1439,27 @@ __tfw_http_msg_expand_from_pool(TfwHttpTransIter *mit, TfwPool* pool,
14291439
*/
14301440
n_copy = room == 0 ? rlen : min(room, rlen);
14311441
off = c->len - rlen;
1442+
skb_room = SS_SKB_MAX_DATA_LEN - it->skb->len;
1443+
nr_frags = skb_shinfo(it->skb)->nr_frags;
1444+
1445+
if (skb_room == 0 || (it->skb == it->skb_head
1446+
&& it->frag == -1
1447+
&& nr_frags == MAX_SKB_FRAGS))
1448+
{
1449+
struct sk_buff *nskb;
1450+
1451+
nskb = ss_skb_alloc(0);
1452+
if (!nskb)
1453+
return -ENOMEM;
1454+
skb_shinfo(nskb)->tx_flags =
1455+
skb_shinfo(it->skb)->tx_flags;
1456+
ss_skb_insert_after(it->skb, nskb);
1457+
it->frag = -1;
1458+
it->skb = nskb;
1459+
skb_room = SS_SKB_MAX_DATA_LEN;
1460+
}
1461+
1462+
n_copy = min(n_copy, skb_room);
14321463

14331464
r = tfw_http_msg_alloc_from_pool(mit, pool, n_copy);
14341465
if (unlikely(r))

fw/ss_skb.c

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -173,21 +173,6 @@ __it_next_data(struct sk_buff *skb, int i, TfwStr *it, int *fragn)
173173
}
174174
}
175175

176-
/*
177-
* Insert @nskb in the list after @skb. Note that standard
178-
* kernel 'skb_insert()' function does not suit here, as it
179-
* works with 'sk_buff_head' structure with additional fields
180-
* @qlen and @lock; we don't need these fields for our skb
181-
* list, so a custom function had been introduced.
182-
*/
183-
static inline void
184-
__skb_insert_after(struct sk_buff *skb, struct sk_buff *nskb)
185-
{
186-
nskb->next = skb->next;
187-
nskb->prev = skb;
188-
nskb->next->prev = skb->next = nskb;
189-
}
190-
191176
/**
192177
* Similar to skb_shift().
193178
* Make room for @n fragments starting with slot @from.
@@ -237,7 +222,7 @@ __extend_pgfrags(struct sk_buff *skb_head, struct sk_buff *skb, int from, int n)
237222
if (nskb == NULL)
238223
return -ENOMEM;
239224
skb_shinfo(nskb)->tx_flags = skb_shinfo(skb)->tx_flags;
240-
__skb_insert_after(skb, nskb);
225+
ss_skb_insert_after(skb, nskb);
241226
skb_shinfo(nskb)->nr_frags = n_excess;
242227
}
243228

@@ -1619,8 +1604,6 @@ ss_skb_add_frag(struct sk_buff *skb_head, struct sk_buff *skb, char* addr,
16191604
if (unlikely(r))
16201605
return r;
16211606

1622-
/* Will fall with sysctl_max_frags != MAX_SKB_FRAGS? */
1623-
skb = (frag_idx < MAX_SKB_FRAGS - 1) ? skb : skb->next;
16241607
__skb_fill_page_desc(skb, frag_idx, page, offset, frag_sz);
16251608
__skb_frag_ref(&skb_shinfo(skb)->frags[frag_idx]);
16261609

fw/ss_skb.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,21 @@ ss_skb_queue_split(struct sk_buff *skb_head, struct sk_buff *skb)
166166
skb_head->prev = prev;
167167
}
168168

169+
/*
170+
* Insert @nskb in the list after @skb. Note that standard
171+
* kernel 'skb_insert()' function does not suit here, as it
172+
* works with 'sk_buff_head' structure with additional fields
173+
* @qlen and @lock; we don't need these fields for our skb
174+
* list, so a custom function had been introduced.
175+
*/
176+
static inline void
177+
ss_skb_insert_after(struct sk_buff *skb, struct sk_buff *nskb)
178+
{
179+
nskb->next = skb->next;
180+
nskb->prev = skb;
181+
nskb->next->prev = skb->next = nskb;
182+
}
183+
169184
/**
170185
* Almost a copy of standard skb_dequeue() except it works with skb list
171186
* instead of sk_buff_head. Several crucial data include skb list and we don't

0 commit comments

Comments
 (0)