[NETFILTER]: {ip,ip6,nfnetlink}_queue: fix SKB_LINEAR_ASSERT when mangling packet data
As reported by Tomas Simonaitis <tomas.simonaitis@gmail.com>, inserting new data in skbs queued over {ip,ip6,nfnetlink}_queue triggers a SKB_LINEAR_ASSERT in skb_put(). Going back through the git history, it seems this bug is present since at least 2.6.12-rc2, probably even since the removal of skb_linearize() for netfilter. Linearize non-linear skbs through skb_copy_expand() when enlarging them. Tested by Thomas, fixes bugzilla #9933. Signed-off-by: Patrick McHardy <kaber@trash.net> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
94cb1503c7
commit
e2b58a67b9
|
@ -283,8 +283,8 @@ static int
|
||||||
ipq_mangle_ipv4(ipq_verdict_msg_t *v, struct nf_queue_entry *e)
|
ipq_mangle_ipv4(ipq_verdict_msg_t *v, struct nf_queue_entry *e)
|
||||||
{
|
{
|
||||||
int diff;
|
int diff;
|
||||||
int err;
|
|
||||||
struct iphdr *user_iph = (struct iphdr *)v->payload;
|
struct iphdr *user_iph = (struct iphdr *)v->payload;
|
||||||
|
struct sk_buff *nskb;
|
||||||
|
|
||||||
if (v->data_len < sizeof(*user_iph))
|
if (v->data_len < sizeof(*user_iph))
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -296,14 +296,16 @@ ipq_mangle_ipv4(ipq_verdict_msg_t *v, struct nf_queue_entry *e)
|
||||||
if (v->data_len > 0xFFFF)
|
if (v->data_len > 0xFFFF)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (diff > skb_tailroom(e->skb)) {
|
if (diff > skb_tailroom(e->skb)) {
|
||||||
err = pskb_expand_head(e->skb, 0,
|
nskb = skb_copy_expand(e->skb, 0,
|
||||||
diff - skb_tailroom(e->skb),
|
diff - skb_tailroom(e->skb),
|
||||||
GFP_ATOMIC);
|
GFP_ATOMIC);
|
||||||
if (err) {
|
if (!nskb) {
|
||||||
printk(KERN_WARNING "ip_queue: error "
|
printk(KERN_WARNING "ip_queue: error "
|
||||||
"in mangle, dropping packet: %d\n", -err);
|
"in mangle, dropping packet\n");
|
||||||
return err;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
kfree_skb(e->skb);
|
||||||
|
e->skb = nskb;
|
||||||
}
|
}
|
||||||
skb_put(e->skb, diff);
|
skb_put(e->skb, diff);
|
||||||
}
|
}
|
||||||
|
|
|
@ -285,8 +285,8 @@ static int
|
||||||
ipq_mangle_ipv6(ipq_verdict_msg_t *v, struct nf_queue_entry *e)
|
ipq_mangle_ipv6(ipq_verdict_msg_t *v, struct nf_queue_entry *e)
|
||||||
{
|
{
|
||||||
int diff;
|
int diff;
|
||||||
int err;
|
|
||||||
struct ipv6hdr *user_iph = (struct ipv6hdr *)v->payload;
|
struct ipv6hdr *user_iph = (struct ipv6hdr *)v->payload;
|
||||||
|
struct sk_buff *nskb;
|
||||||
|
|
||||||
if (v->data_len < sizeof(*user_iph))
|
if (v->data_len < sizeof(*user_iph))
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -298,14 +298,16 @@ ipq_mangle_ipv6(ipq_verdict_msg_t *v, struct nf_queue_entry *e)
|
||||||
if (v->data_len > 0xFFFF)
|
if (v->data_len > 0xFFFF)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (diff > skb_tailroom(e->skb)) {
|
if (diff > skb_tailroom(e->skb)) {
|
||||||
err = pskb_expand_head(e->skb, 0,
|
nskb = skb_copy_expand(e->skb, 0,
|
||||||
diff - skb_tailroom(e->skb),
|
diff - skb_tailroom(e->skb),
|
||||||
GFP_ATOMIC);
|
GFP_ATOMIC);
|
||||||
if (err) {
|
if (!nskb) {
|
||||||
printk(KERN_WARNING "ip6_queue: OOM "
|
printk(KERN_WARNING "ip6_queue: OOM "
|
||||||
"in mangle, dropping packet\n");
|
"in mangle, dropping packet\n");
|
||||||
return err;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
kfree_skb(e->skb);
|
||||||
|
e->skb = nskb;
|
||||||
}
|
}
|
||||||
skb_put(e->skb, diff);
|
skb_put(e->skb, diff);
|
||||||
}
|
}
|
||||||
|
|
|
@ -443,8 +443,8 @@ err_out:
|
||||||
static int
|
static int
|
||||||
nfqnl_mangle(void *data, int data_len, struct nf_queue_entry *e)
|
nfqnl_mangle(void *data, int data_len, struct nf_queue_entry *e)
|
||||||
{
|
{
|
||||||
|
struct sk_buff *nskb;
|
||||||
int diff;
|
int diff;
|
||||||
int err;
|
|
||||||
|
|
||||||
diff = data_len - e->skb->len;
|
diff = data_len - e->skb->len;
|
||||||
if (diff < 0) {
|
if (diff < 0) {
|
||||||
|
@ -454,14 +454,16 @@ nfqnl_mangle(void *data, int data_len, struct nf_queue_entry *e)
|
||||||
if (data_len > 0xFFFF)
|
if (data_len > 0xFFFF)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (diff > skb_tailroom(e->skb)) {
|
if (diff > skb_tailroom(e->skb)) {
|
||||||
err = pskb_expand_head(e->skb, 0,
|
nskb = skb_copy_expand(e->skb, 0,
|
||||||
diff - skb_tailroom(e->skb),
|
diff - skb_tailroom(e->skb),
|
||||||
GFP_ATOMIC);
|
GFP_ATOMIC);
|
||||||
if (err) {
|
if (!nskb) {
|
||||||
printk(KERN_WARNING "nf_queue: OOM "
|
printk(KERN_WARNING "nf_queue: OOM "
|
||||||
"in mangle, dropping packet\n");
|
"in mangle, dropping packet\n");
|
||||||
return err;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
kfree_skb(e->skb);
|
||||||
|
e->skb = nskb;
|
||||||
}
|
}
|
||||||
skb_put(e->skb, diff);
|
skb_put(e->skb, diff);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue