Skip to content

Commit

Permalink
tipc: enforce valid ratio between skb truesize and contents
Browse files Browse the repository at this point in the history
The socket level flow control is based on the assumption that incoming
buffers meet the condition (skb->truesize / roundup(skb->len) <= 4),
where the latter value is rounded off upwards to the nearest 1k number.
This does empirically hold true for the device drivers we know, but we
cannot trust that it will always be so, e.g., in a system with jumbo
frames and very small packets.

We now introduce a check for this condition at packet arrival, and if
we find it to be false, we copy the packet to a new, smaller buffer,
where the condition will be true. We expect this to affect only a small
fraction of all incoming packets, if at all.

Acked-by: Ying Xue <ying.xue@windriver.com>
Signed-off-by: Jon Maloy <jon.maloy@ericsson.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Jon Maloy authored and David S. Miller committed Nov 16, 2017
1 parent 8252fce commit d618d09
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 9 deletions.
24 changes: 17 additions & 7 deletions net/tipc/msg.c
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ int tipc_buf_append(struct sk_buff **headbuf, struct sk_buff **buf)

if (fragid == LAST_FRAGMENT) {
TIPC_SKB_CB(head)->validated = false;
if (unlikely(!tipc_msg_validate(head)))
if (unlikely(!tipc_msg_validate(&head)))
goto err;
*buf = head;
TIPC_SKB_CB(head)->tail = NULL;
Expand All @@ -201,11 +201,21 @@ int tipc_buf_append(struct sk_buff **headbuf, struct sk_buff **buf)
* TIPC will ignore the excess, under the assumption that it is optional info
* introduced by a later release of the protocol.
*/
bool tipc_msg_validate(struct sk_buff *skb)
bool tipc_msg_validate(struct sk_buff **_skb)
{
struct tipc_msg *msg;
struct sk_buff *skb = *_skb;
struct tipc_msg *hdr;
int msz, hsz;

/* Ensure that flow control ratio condition is satisfied */
if (unlikely(skb->truesize / buf_roundup_len(skb) > 4)) {
skb = skb_copy(skb, GFP_ATOMIC);
if (!skb)
return false;
kfree_skb(*_skb);
*_skb = skb;
}

if (unlikely(TIPC_SKB_CB(skb)->validated))
return true;
if (unlikely(!pskb_may_pull(skb, MIN_H_SIZE)))
Expand All @@ -217,11 +227,11 @@ bool tipc_msg_validate(struct sk_buff *skb)
if (unlikely(!pskb_may_pull(skb, hsz)))
return false;

msg = buf_msg(skb);
if (unlikely(msg_version(msg) != TIPC_VERSION))
hdr = buf_msg(skb);
if (unlikely(msg_version(hdr) != TIPC_VERSION))
return false;

msz = msg_size(msg);
msz = msg_size(hdr);
if (unlikely(msz < hsz))
return false;
if (unlikely((msz - hsz) > TIPC_MAX_USER_MSG_SIZE))
Expand Down Expand Up @@ -411,7 +421,7 @@ bool tipc_msg_extract(struct sk_buff *skb, struct sk_buff **iskb, int *pos)
skb_pull(*iskb, offset);
imsz = msg_size(buf_msg(*iskb));
skb_trim(*iskb, imsz);
if (unlikely(!tipc_msg_validate(*iskb)))
if (unlikely(!tipc_msg_validate(iskb)))
goto none;
*pos += align(imsz);
return true;
Expand Down
7 changes: 6 additions & 1 deletion net/tipc/msg.h
Original file line number Diff line number Diff line change
Expand Up @@ -926,7 +926,7 @@ static inline bool msg_is_reset(struct tipc_msg *hdr)
}

struct sk_buff *tipc_buf_acquire(u32 size, gfp_t gfp);
bool tipc_msg_validate(struct sk_buff *skb);
bool tipc_msg_validate(struct sk_buff **_skb);
bool tipc_msg_reverse(u32 own_addr, struct sk_buff **skb, int err);
void tipc_skb_reject(struct net *net, int err, struct sk_buff *skb,
struct sk_buff_head *xmitq);
Expand Down Expand Up @@ -954,6 +954,11 @@ static inline u16 buf_seqno(struct sk_buff *skb)
return msg_seqno(buf_msg(skb));
}

static inline int buf_roundup_len(struct sk_buff *skb)
{
return (skb->len / 1024 + 1) * 1024;
}

/* tipc_skb_peek(): peek and reserve first buffer in list
* @list: list to be peeked in
* Returns pointer to first buffer in list, if any
Expand Down
2 changes: 1 addition & 1 deletion net/tipc/node.c
Original file line number Diff line number Diff line change
Expand Up @@ -1539,7 +1539,7 @@ void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b)
__skb_queue_head_init(&xmitq);

/* Ensure message is well-formed before touching the header */
if (unlikely(!tipc_msg_validate(skb)))
if (unlikely(!tipc_msg_validate(&skb)))
goto discard;
hdr = buf_msg(skb);
usr = msg_user(hdr);
Expand Down

0 comments on commit d618d09

Please sign in to comment.