netfilter: x_tables: Add fixes for CVE-2016-4997, CVE-2016-4998
...and probably other issues never assigned an ID.
This commit is contained in:
parent
f5f99415a2
commit
9fd7bbdf1a
|
@ -9,6 +9,24 @@ linux (4.6.2-2) UNRELEASED; urgency=medium
|
|||
destruction (CVE-2016-4794)
|
||||
* percpu: fix synchronization between synchronous map extension and
|
||||
chunk destruction (CVE-2016-4794)
|
||||
* netfilter: x_tables: Fix parsing of IPT_SO_SET_REPLACE blobs
|
||||
(CVE-2016-4997, CVE-2016-4998)
|
||||
- don't move to non-existent next rule
|
||||
- validate targets of jumps
|
||||
- add and use xt_check_entry_offsets
|
||||
- kill check_entry helper
|
||||
- assert minimum target size
|
||||
- add compat version of xt_check_entry_offsets
|
||||
- check standard target size too
|
||||
- check for bogus target offset
|
||||
- validate all offsets and sizes in a rule
|
||||
- don't reject valid target size on some
|
||||
- arp_tables: simplify translate_compat_table args
|
||||
- ip_tables: simplify translate_compat_table args
|
||||
- ip6_tables: simplify translate_compat_table args
|
||||
- xt_compat_match_from_user doesn't need a retval
|
||||
- do compat validation via translate_table
|
||||
- introduce and use xt_copy_counters_from_user
|
||||
|
||||
-- Ben Hutchings <ben@decadent.org.uk> Thu, 16 Jun 2016 12:37:27 +0100
|
||||
|
||||
|
|
210
debian/patches/bugfix/all/netfilter-arp_tables-simplify-translate_compat_table.patch
vendored
Normal file
210
debian/patches/bugfix/all/netfilter-arp_tables-simplify-translate_compat_table.patch
vendored
Normal file
|
@ -0,0 +1,210 @@
|
|||
From: Florian Westphal <fw@strlen.de>
|
||||
Date: Fri, 1 Apr 2016 14:17:32 +0200
|
||||
Subject: netfilter: arp_tables: simplify translate_compat_table args
|
||||
Origin: https://git.kernel.org/linus/8dddd32756f6fe8e4e82a63361119b7e2384e02f
|
||||
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
---
|
||||
net/ipv4/netfilter/arp_tables.c | 82 ++++++++++++++++++-----------------------
|
||||
1 file changed, 36 insertions(+), 46 deletions(-)
|
||||
|
||||
--- a/net/ipv4/netfilter/arp_tables.c
|
||||
+++ b/net/ipv4/netfilter/arp_tables.c
|
||||
@@ -1214,6 +1214,18 @@ static int do_add_counters(struct net *n
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
+struct compat_arpt_replace {
|
||||
+ char name[XT_TABLE_MAXNAMELEN];
|
||||
+ u32 valid_hooks;
|
||||
+ u32 num_entries;
|
||||
+ u32 size;
|
||||
+ u32 hook_entry[NF_ARP_NUMHOOKS];
|
||||
+ u32 underflow[NF_ARP_NUMHOOKS];
|
||||
+ u32 num_counters;
|
||||
+ compat_uptr_t counters;
|
||||
+ struct compat_arpt_entry entries[0];
|
||||
+};
|
||||
+
|
||||
static inline void compat_release_entry(struct compat_arpt_entry *e)
|
||||
{
|
||||
struct xt_entry_target *t;
|
||||
@@ -1229,8 +1241,7 @@ check_compat_entry_size_and_hooks(struct
|
||||
const unsigned char *base,
|
||||
const unsigned char *limit,
|
||||
const unsigned int *hook_entries,
|
||||
- const unsigned int *underflows,
|
||||
- const char *name)
|
||||
+ const unsigned int *underflows)
|
||||
{
|
||||
struct xt_entry_target *t;
|
||||
struct xt_target *target;
|
||||
@@ -1301,7 +1312,7 @@ out:
|
||||
|
||||
static int
|
||||
compat_copy_entry_from_user(struct compat_arpt_entry *e, void **dstptr,
|
||||
- unsigned int *size, const char *name,
|
||||
+ unsigned int *size,
|
||||
struct xt_table_info *newinfo, unsigned char *base)
|
||||
{
|
||||
struct xt_entry_target *t;
|
||||
@@ -1334,14 +1345,9 @@ compat_copy_entry_from_user(struct compa
|
||||
return ret;
|
||||
}
|
||||
|
||||
-static int translate_compat_table(const char *name,
|
||||
- unsigned int valid_hooks,
|
||||
- struct xt_table_info **pinfo,
|
||||
+static int translate_compat_table(struct xt_table_info **pinfo,
|
||||
void **pentry0,
|
||||
- unsigned int total_size,
|
||||
- unsigned int number,
|
||||
- unsigned int *hook_entries,
|
||||
- unsigned int *underflows)
|
||||
+ const struct compat_arpt_replace *compatr)
|
||||
{
|
||||
unsigned int i, j;
|
||||
struct xt_table_info *newinfo, *info;
|
||||
@@ -1353,8 +1359,8 @@ static int translate_compat_table(const
|
||||
|
||||
info = *pinfo;
|
||||
entry0 = *pentry0;
|
||||
- size = total_size;
|
||||
- info->number = number;
|
||||
+ size = compatr->size;
|
||||
+ info->number = compatr->num_entries;
|
||||
|
||||
/* Init all hooks to impossible value. */
|
||||
for (i = 0; i < NF_ARP_NUMHOOKS; i++) {
|
||||
@@ -1365,40 +1371,39 @@ static int translate_compat_table(const
|
||||
duprintf("translate_compat_table: size %u\n", info->size);
|
||||
j = 0;
|
||||
xt_compat_lock(NFPROTO_ARP);
|
||||
- xt_compat_init_offsets(NFPROTO_ARP, number);
|
||||
+ xt_compat_init_offsets(NFPROTO_ARP, compatr->num_entries);
|
||||
/* Walk through entries, checking offsets. */
|
||||
- xt_entry_foreach(iter0, entry0, total_size) {
|
||||
+ xt_entry_foreach(iter0, entry0, compatr->size) {
|
||||
ret = check_compat_entry_size_and_hooks(iter0, info, &size,
|
||||
entry0,
|
||||
- entry0 + total_size,
|
||||
- hook_entries,
|
||||
- underflows,
|
||||
- name);
|
||||
+ entry0 + compatr->size,
|
||||
+ compatr->hook_entry,
|
||||
+ compatr->underflow);
|
||||
if (ret != 0)
|
||||
goto out_unlock;
|
||||
++j;
|
||||
}
|
||||
|
||||
ret = -EINVAL;
|
||||
- if (j != number) {
|
||||
+ if (j != compatr->num_entries) {
|
||||
duprintf("translate_compat_table: %u not %u entries\n",
|
||||
- j, number);
|
||||
+ j, compatr->num_entries);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/* Check hooks all assigned */
|
||||
for (i = 0; i < NF_ARP_NUMHOOKS; i++) {
|
||||
/* Only hooks which are valid */
|
||||
- if (!(valid_hooks & (1 << i)))
|
||||
+ if (!(compatr->valid_hooks & (1 << i)))
|
||||
continue;
|
||||
if (info->hook_entry[i] == 0xFFFFFFFF) {
|
||||
duprintf("Invalid hook entry %u %u\n",
|
||||
- i, hook_entries[i]);
|
||||
+ i, info->hook_entry[i]);
|
||||
goto out_unlock;
|
||||
}
|
||||
if (info->underflow[i] == 0xFFFFFFFF) {
|
||||
duprintf("Invalid underflow %u %u\n",
|
||||
- i, underflows[i]);
|
||||
+ i, info->underflow[i]);
|
||||
goto out_unlock;
|
||||
}
|
||||
}
|
||||
@@ -1408,17 +1413,17 @@ static int translate_compat_table(const
|
||||
if (!newinfo)
|
||||
goto out_unlock;
|
||||
|
||||
- newinfo->number = number;
|
||||
+ newinfo->number = compatr->num_entries;
|
||||
for (i = 0; i < NF_ARP_NUMHOOKS; i++) {
|
||||
newinfo->hook_entry[i] = info->hook_entry[i];
|
||||
newinfo->underflow[i] = info->underflow[i];
|
||||
}
|
||||
entry1 = newinfo->entries;
|
||||
pos = entry1;
|
||||
- size = total_size;
|
||||
- xt_entry_foreach(iter0, entry0, total_size) {
|
||||
+ size = compatr->size;
|
||||
+ xt_entry_foreach(iter0, entry0, compatr->size) {
|
||||
ret = compat_copy_entry_from_user(iter0, &pos, &size,
|
||||
- name, newinfo, entry1);
|
||||
+ newinfo, entry1);
|
||||
if (ret != 0)
|
||||
break;
|
||||
}
|
||||
@@ -1428,7 +1433,7 @@ static int translate_compat_table(const
|
||||
goto free_newinfo;
|
||||
|
||||
ret = -ELOOP;
|
||||
- if (!mark_source_chains(newinfo, valid_hooks, entry1))
|
||||
+ if (!mark_source_chains(newinfo, compatr->valid_hooks, entry1))
|
||||
goto free_newinfo;
|
||||
|
||||
i = 0;
|
||||
@@ -1439,7 +1444,7 @@ static int translate_compat_table(const
|
||||
break;
|
||||
}
|
||||
|
||||
- ret = check_target(iter1, name);
|
||||
+ ret = check_target(iter1, compatr->name);
|
||||
if (ret != 0) {
|
||||
xt_percpu_counter_free(iter1->counters.pcnt);
|
||||
break;
|
||||
@@ -1481,7 +1486,7 @@ static int translate_compat_table(const
|
||||
free_newinfo:
|
||||
xt_free_table_info(newinfo);
|
||||
out:
|
||||
- xt_entry_foreach(iter0, entry0, total_size) {
|
||||
+ xt_entry_foreach(iter0, entry0, compatr->size) {
|
||||
if (j-- == 0)
|
||||
break;
|
||||
compat_release_entry(iter0);
|
||||
@@ -1493,18 +1498,6 @@ out_unlock:
|
||||
goto out;
|
||||
}
|
||||
|
||||
-struct compat_arpt_replace {
|
||||
- char name[XT_TABLE_MAXNAMELEN];
|
||||
- u32 valid_hooks;
|
||||
- u32 num_entries;
|
||||
- u32 size;
|
||||
- u32 hook_entry[NF_ARP_NUMHOOKS];
|
||||
- u32 underflow[NF_ARP_NUMHOOKS];
|
||||
- u32 num_counters;
|
||||
- compat_uptr_t counters;
|
||||
- struct compat_arpt_entry entries[0];
|
||||
-};
|
||||
-
|
||||
static int compat_do_replace(struct net *net, void __user *user,
|
||||
unsigned int len)
|
||||
{
|
||||
@@ -1537,10 +1530,7 @@ static int compat_do_replace(struct net
|
||||
goto free_newinfo;
|
||||
}
|
||||
|
||||
- ret = translate_compat_table(tmp.name, tmp.valid_hooks,
|
||||
- &newinfo, &loc_cpu_entry, tmp.size,
|
||||
- tmp.num_entries, tmp.hook_entry,
|
||||
- tmp.underflow);
|
||||
+ ret = translate_compat_table(&newinfo, &loc_cpu_entry, &tmp);
|
||||
if (ret != 0)
|
||||
goto free_newinfo;
|
||||
|
185
debian/patches/bugfix/all/netfilter-ip6_tables-simplify-translate_compat_table.patch
vendored
Normal file
185
debian/patches/bugfix/all/netfilter-ip6_tables-simplify-translate_compat_table.patch
vendored
Normal file
|
@ -0,0 +1,185 @@
|
|||
From: Florian Westphal <fw@strlen.de>
|
||||
Date: Fri, 1 Apr 2016 14:17:31 +0200
|
||||
Subject: netfilter: ip6_tables: simplify translate_compat_table args
|
||||
Origin: https://git.kernel.org/linus/329a0807124f12fe1c8032f95d8a8eb47047fb0e
|
||||
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
---
|
||||
net/ipv6/netfilter/ip6_tables.c | 59 +++++++++++++++++------------------------
|
||||
1 file changed, 24 insertions(+), 35 deletions(-)
|
||||
|
||||
--- a/net/ipv6/netfilter/ip6_tables.c
|
||||
+++ b/net/ipv6/netfilter/ip6_tables.c
|
||||
@@ -1461,7 +1461,6 @@ compat_copy_entry_to_user(struct ip6t_en
|
||||
|
||||
static int
|
||||
compat_find_calc_match(struct xt_entry_match *m,
|
||||
- const char *name,
|
||||
const struct ip6t_ip6 *ipv6,
|
||||
int *size)
|
||||
{
|
||||
@@ -1498,8 +1497,7 @@ check_compat_entry_size_and_hooks(struct
|
||||
const unsigned char *base,
|
||||
const unsigned char *limit,
|
||||
const unsigned int *hook_entries,
|
||||
- const unsigned int *underflows,
|
||||
- const char *name)
|
||||
+ const unsigned int *underflows)
|
||||
{
|
||||
struct xt_entry_match *ematch;
|
||||
struct xt_entry_target *t;
|
||||
@@ -1535,7 +1533,7 @@ check_compat_entry_size_and_hooks(struct
|
||||
entry_offset = (void *)e - (void *)base;
|
||||
j = 0;
|
||||
xt_ematch_foreach(ematch, e) {
|
||||
- ret = compat_find_calc_match(ematch, name, &e->ipv6, &off);
|
||||
+ ret = compat_find_calc_match(ematch, &e->ipv6, &off);
|
||||
if (ret != 0)
|
||||
goto release_matches;
|
||||
++j;
|
||||
@@ -1584,7 +1582,7 @@ release_matches:
|
||||
|
||||
static int
|
||||
compat_copy_entry_from_user(struct compat_ip6t_entry *e, void **dstptr,
|
||||
- unsigned int *size, const char *name,
|
||||
+ unsigned int *size,
|
||||
struct xt_table_info *newinfo, unsigned char *base)
|
||||
{
|
||||
struct xt_entry_target *t;
|
||||
@@ -1664,14 +1662,9 @@ static int compat_check_entry(struct ip6
|
||||
|
||||
static int
|
||||
translate_compat_table(struct net *net,
|
||||
- const char *name,
|
||||
- unsigned int valid_hooks,
|
||||
struct xt_table_info **pinfo,
|
||||
void **pentry0,
|
||||
- unsigned int total_size,
|
||||
- unsigned int number,
|
||||
- unsigned int *hook_entries,
|
||||
- unsigned int *underflows)
|
||||
+ const struct compat_ip6t_replace *compatr)
|
||||
{
|
||||
unsigned int i, j;
|
||||
struct xt_table_info *newinfo, *info;
|
||||
@@ -1683,8 +1676,8 @@ translate_compat_table(struct net *net,
|
||||
|
||||
info = *pinfo;
|
||||
entry0 = *pentry0;
|
||||
- size = total_size;
|
||||
- info->number = number;
|
||||
+ size = compatr->size;
|
||||
+ info->number = compatr->num_entries;
|
||||
|
||||
/* Init all hooks to impossible value. */
|
||||
for (i = 0; i < NF_INET_NUMHOOKS; i++) {
|
||||
@@ -1695,40 +1688,39 @@ translate_compat_table(struct net *net,
|
||||
duprintf("translate_compat_table: size %u\n", info->size);
|
||||
j = 0;
|
||||
xt_compat_lock(AF_INET6);
|
||||
- xt_compat_init_offsets(AF_INET6, number);
|
||||
+ xt_compat_init_offsets(AF_INET6, compatr->num_entries);
|
||||
/* Walk through entries, checking offsets. */
|
||||
- xt_entry_foreach(iter0, entry0, total_size) {
|
||||
+ xt_entry_foreach(iter0, entry0, compatr->size) {
|
||||
ret = check_compat_entry_size_and_hooks(iter0, info, &size,
|
||||
entry0,
|
||||
- entry0 + total_size,
|
||||
- hook_entries,
|
||||
- underflows,
|
||||
- name);
|
||||
+ entry0 + compatr->size,
|
||||
+ compatr->hook_entry,
|
||||
+ compatr->underflow);
|
||||
if (ret != 0)
|
||||
goto out_unlock;
|
||||
++j;
|
||||
}
|
||||
|
||||
ret = -EINVAL;
|
||||
- if (j != number) {
|
||||
+ if (j != compatr->num_entries) {
|
||||
duprintf("translate_compat_table: %u not %u entries\n",
|
||||
- j, number);
|
||||
+ j, compatr->num_entries);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/* Check hooks all assigned */
|
||||
for (i = 0; i < NF_INET_NUMHOOKS; i++) {
|
||||
/* Only hooks which are valid */
|
||||
- if (!(valid_hooks & (1 << i)))
|
||||
+ if (!(compatr->valid_hooks & (1 << i)))
|
||||
continue;
|
||||
if (info->hook_entry[i] == 0xFFFFFFFF) {
|
||||
duprintf("Invalid hook entry %u %u\n",
|
||||
- i, hook_entries[i]);
|
||||
+ i, info->hook_entry[i]);
|
||||
goto out_unlock;
|
||||
}
|
||||
if (info->underflow[i] == 0xFFFFFFFF) {
|
||||
duprintf("Invalid underflow %u %u\n",
|
||||
- i, underflows[i]);
|
||||
+ i, info->underflow[i]);
|
||||
goto out_unlock;
|
||||
}
|
||||
}
|
||||
@@ -1738,17 +1730,17 @@ translate_compat_table(struct net *net,
|
||||
if (!newinfo)
|
||||
goto out_unlock;
|
||||
|
||||
- newinfo->number = number;
|
||||
+ newinfo->number = compatr->num_entries;
|
||||
for (i = 0; i < NF_INET_NUMHOOKS; i++) {
|
||||
newinfo->hook_entry[i] = info->hook_entry[i];
|
||||
newinfo->underflow[i] = info->underflow[i];
|
||||
}
|
||||
entry1 = newinfo->entries;
|
||||
pos = entry1;
|
||||
- size = total_size;
|
||||
- xt_entry_foreach(iter0, entry0, total_size) {
|
||||
+ size = compatr->size;
|
||||
+ xt_entry_foreach(iter0, entry0, compatr->size) {
|
||||
ret = compat_copy_entry_from_user(iter0, &pos, &size,
|
||||
- name, newinfo, entry1);
|
||||
+ newinfo, entry1);
|
||||
if (ret != 0)
|
||||
break;
|
||||
}
|
||||
@@ -1758,12 +1750,12 @@ translate_compat_table(struct net *net,
|
||||
goto free_newinfo;
|
||||
|
||||
ret = -ELOOP;
|
||||
- if (!mark_source_chains(newinfo, valid_hooks, entry1))
|
||||
+ if (!mark_source_chains(newinfo, compatr->valid_hooks, entry1))
|
||||
goto free_newinfo;
|
||||
|
||||
i = 0;
|
||||
xt_entry_foreach(iter1, entry1, newinfo->size) {
|
||||
- ret = compat_check_entry(iter1, net, name);
|
||||
+ ret = compat_check_entry(iter1, net, compatr->name);
|
||||
if (ret != 0)
|
||||
break;
|
||||
++i;
|
||||
@@ -1803,7 +1795,7 @@ translate_compat_table(struct net *net,
|
||||
free_newinfo:
|
||||
xt_free_table_info(newinfo);
|
||||
out:
|
||||
- xt_entry_foreach(iter0, entry0, total_size) {
|
||||
+ xt_entry_foreach(iter0, entry0, compatr->size) {
|
||||
if (j-- == 0)
|
||||
break;
|
||||
compat_release_entry(iter0);
|
||||
@@ -1848,10 +1840,7 @@ compat_do_replace(struct net *net, void
|
||||
goto free_newinfo;
|
||||
}
|
||||
|
||||
- ret = translate_compat_table(net, tmp.name, tmp.valid_hooks,
|
||||
- &newinfo, &loc_cpu_entry, tmp.size,
|
||||
- tmp.num_entries, tmp.hook_entry,
|
||||
- tmp.underflow);
|
||||
+ ret = translate_compat_table(net, &newinfo, &loc_cpu_entry, &tmp);
|
||||
if (ret != 0)
|
||||
goto free_newinfo;
|
||||
|
185
debian/patches/bugfix/all/netfilter-ip_tables-simplify-translate_compat_table-.patch
vendored
Normal file
185
debian/patches/bugfix/all/netfilter-ip_tables-simplify-translate_compat_table-.patch
vendored
Normal file
|
@ -0,0 +1,185 @@
|
|||
From: Florian Westphal <fw@strlen.de>
|
||||
Date: Fri, 1 Apr 2016 14:17:30 +0200
|
||||
Subject: netfilter: ip_tables: simplify translate_compat_table args
|
||||
Origin: https://git.kernel.org/linus/7d3f843eed29222254c9feab481f55175a1afcc9
|
||||
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
---
|
||||
net/ipv4/netfilter/ip_tables.c | 59 +++++++++++++++++-------------------------
|
||||
1 file changed, 24 insertions(+), 35 deletions(-)
|
||||
|
||||
--- a/net/ipv4/netfilter/ip_tables.c
|
||||
+++ b/net/ipv4/netfilter/ip_tables.c
|
||||
@@ -1449,7 +1449,6 @@ compat_copy_entry_to_user(struct ipt_ent
|
||||
|
||||
static int
|
||||
compat_find_calc_match(struct xt_entry_match *m,
|
||||
- const char *name,
|
||||
const struct ipt_ip *ip,
|
||||
int *size)
|
||||
{
|
||||
@@ -1486,8 +1485,7 @@ check_compat_entry_size_and_hooks(struct
|
||||
const unsigned char *base,
|
||||
const unsigned char *limit,
|
||||
const unsigned int *hook_entries,
|
||||
- const unsigned int *underflows,
|
||||
- const char *name)
|
||||
+ const unsigned int *underflows)
|
||||
{
|
||||
struct xt_entry_match *ematch;
|
||||
struct xt_entry_target *t;
|
||||
@@ -1523,7 +1521,7 @@ check_compat_entry_size_and_hooks(struct
|
||||
entry_offset = (void *)e - (void *)base;
|
||||
j = 0;
|
||||
xt_ematch_foreach(ematch, e) {
|
||||
- ret = compat_find_calc_match(ematch, name, &e->ip, &off);
|
||||
+ ret = compat_find_calc_match(ematch, &e->ip, &off);
|
||||
if (ret != 0)
|
||||
goto release_matches;
|
||||
++j;
|
||||
@@ -1572,7 +1570,7 @@ release_matches:
|
||||
|
||||
static int
|
||||
compat_copy_entry_from_user(struct compat_ipt_entry *e, void **dstptr,
|
||||
- unsigned int *size, const char *name,
|
||||
+ unsigned int *size,
|
||||
struct xt_table_info *newinfo, unsigned char *base)
|
||||
{
|
||||
struct xt_entry_target *t;
|
||||
@@ -1655,14 +1653,9 @@ compat_check_entry(struct ipt_entry *e,
|
||||
|
||||
static int
|
||||
translate_compat_table(struct net *net,
|
||||
- const char *name,
|
||||
- unsigned int valid_hooks,
|
||||
struct xt_table_info **pinfo,
|
||||
void **pentry0,
|
||||
- unsigned int total_size,
|
||||
- unsigned int number,
|
||||
- unsigned int *hook_entries,
|
||||
- unsigned int *underflows)
|
||||
+ const struct compat_ipt_replace *compatr)
|
||||
{
|
||||
unsigned int i, j;
|
||||
struct xt_table_info *newinfo, *info;
|
||||
@@ -1674,8 +1667,8 @@ translate_compat_table(struct net *net,
|
||||
|
||||
info = *pinfo;
|
||||
entry0 = *pentry0;
|
||||
- size = total_size;
|
||||
- info->number = number;
|
||||
+ size = compatr->size;
|
||||
+ info->number = compatr->num_entries;
|
||||
|
||||
/* Init all hooks to impossible value. */
|
||||
for (i = 0; i < NF_INET_NUMHOOKS; i++) {
|
||||
@@ -1686,40 +1679,39 @@ translate_compat_table(struct net *net,
|
||||
duprintf("translate_compat_table: size %u\n", info->size);
|
||||
j = 0;
|
||||
xt_compat_lock(AF_INET);
|
||||
- xt_compat_init_offsets(AF_INET, number);
|
||||
+ xt_compat_init_offsets(AF_INET, compatr->num_entries);
|
||||
/* Walk through entries, checking offsets. */
|
||||
- xt_entry_foreach(iter0, entry0, total_size) {
|
||||
+ xt_entry_foreach(iter0, entry0, compatr->size) {
|
||||
ret = check_compat_entry_size_and_hooks(iter0, info, &size,
|
||||
entry0,
|
||||
- entry0 + total_size,
|
||||
- hook_entries,
|
||||
- underflows,
|
||||
- name);
|
||||
+ entry0 + compatr->size,
|
||||
+ compatr->hook_entry,
|
||||
+ compatr->underflow);
|
||||
if (ret != 0)
|
||||
goto out_unlock;
|
||||
++j;
|
||||
}
|
||||
|
||||
ret = -EINVAL;
|
||||
- if (j != number) {
|
||||
+ if (j != compatr->num_entries) {
|
||||
duprintf("translate_compat_table: %u not %u entries\n",
|
||||
- j, number);
|
||||
+ j, compatr->num_entries);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/* Check hooks all assigned */
|
||||
for (i = 0; i < NF_INET_NUMHOOKS; i++) {
|
||||
/* Only hooks which are valid */
|
||||
- if (!(valid_hooks & (1 << i)))
|
||||
+ if (!(compatr->valid_hooks & (1 << i)))
|
||||
continue;
|
||||
if (info->hook_entry[i] == 0xFFFFFFFF) {
|
||||
duprintf("Invalid hook entry %u %u\n",
|
||||
- i, hook_entries[i]);
|
||||
+ i, info->hook_entry[i]);
|
||||
goto out_unlock;
|
||||
}
|
||||
if (info->underflow[i] == 0xFFFFFFFF) {
|
||||
duprintf("Invalid underflow %u %u\n",
|
||||
- i, underflows[i]);
|
||||
+ i, info->underflow[i]);
|
||||
goto out_unlock;
|
||||
}
|
||||
}
|
||||
@@ -1729,17 +1721,17 @@ translate_compat_table(struct net *net,
|
||||
if (!newinfo)
|
||||
goto out_unlock;
|
||||
|
||||
- newinfo->number = number;
|
||||
+ newinfo->number = compatr->num_entries;
|
||||
for (i = 0; i < NF_INET_NUMHOOKS; i++) {
|
||||
newinfo->hook_entry[i] = info->hook_entry[i];
|
||||
newinfo->underflow[i] = info->underflow[i];
|
||||
}
|
||||
entry1 = newinfo->entries;
|
||||
pos = entry1;
|
||||
- size = total_size;
|
||||
- xt_entry_foreach(iter0, entry0, total_size) {
|
||||
+ size = compatr->size;
|
||||
+ xt_entry_foreach(iter0, entry0, compatr->size) {
|
||||
ret = compat_copy_entry_from_user(iter0, &pos, &size,
|
||||
- name, newinfo, entry1);
|
||||
+ newinfo, entry1);
|
||||
if (ret != 0)
|
||||
break;
|
||||
}
|
||||
@@ -1749,12 +1741,12 @@ translate_compat_table(struct net *net,
|
||||
goto free_newinfo;
|
||||
|
||||
ret = -ELOOP;
|
||||
- if (!mark_source_chains(newinfo, valid_hooks, entry1))
|
||||
+ if (!mark_source_chains(newinfo, compatr->valid_hooks, entry1))
|
||||
goto free_newinfo;
|
||||
|
||||
i = 0;
|
||||
xt_entry_foreach(iter1, entry1, newinfo->size) {
|
||||
- ret = compat_check_entry(iter1, net, name);
|
||||
+ ret = compat_check_entry(iter1, net, compatr->name);
|
||||
if (ret != 0)
|
||||
break;
|
||||
++i;
|
||||
@@ -1794,7 +1786,7 @@ translate_compat_table(struct net *net,
|
||||
free_newinfo:
|
||||
xt_free_table_info(newinfo);
|
||||
out:
|
||||
- xt_entry_foreach(iter0, entry0, total_size) {
|
||||
+ xt_entry_foreach(iter0, entry0, compatr->size) {
|
||||
if (j-- == 0)
|
||||
break;
|
||||
compat_release_entry(iter0);
|
||||
@@ -1839,10 +1831,7 @@ compat_do_replace(struct net *net, void
|
||||
goto free_newinfo;
|
||||
}
|
||||
|
||||
- ret = translate_compat_table(net, tmp.name, tmp.valid_hooks,
|
||||
- &newinfo, &loc_cpu_entry, tmp.size,
|
||||
- tmp.num_entries, tmp.hook_entry,
|
||||
- tmp.underflow);
|
||||
+ ret = translate_compat_table(net, &newinfo, &loc_cpu_entry, &tmp);
|
||||
if (ret != 0)
|
||||
goto free_newinfo;
|
||||
|
151
debian/patches/bugfix/all/netfilter-x_tables-add-and-use-xt_check_entry_offset.patch
vendored
Normal file
151
debian/patches/bugfix/all/netfilter-x_tables-add-and-use-xt_check_entry_offset.patch
vendored
Normal file
|
@ -0,0 +1,151 @@
|
|||
From: Florian Westphal <fw@strlen.de>
|
||||
Date: Fri, 1 Apr 2016 14:17:23 +0200
|
||||
Subject: netfilter: x_tables: add and use xt_check_entry_offsets
|
||||
Origin: https://git.kernel.org/linus/7d35812c3214afa5b37a675113555259cfd67b98
|
||||
|
||||
Currently arp/ip and ip6tables each implement a short helper to check that
|
||||
the target offset is large enough to hold one xt_entry_target struct and
|
||||
that t->u.target_size fits within the current rule.
|
||||
|
||||
Unfortunately these checks are not sufficient.
|
||||
|
||||
To avoid adding new tests to all of ip/ip6/arptables move the current
|
||||
checks into a helper, then extend this helper in followup patches.
|
||||
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
---
|
||||
include/linux/netfilter/x_tables.h | 4 ++++
|
||||
net/ipv4/netfilter/arp_tables.c | 11 +----------
|
||||
net/ipv4/netfilter/ip_tables.c | 12 +-----------
|
||||
net/ipv6/netfilter/ip6_tables.c | 12 +-----------
|
||||
net/netfilter/x_tables.c | 34 ++++++++++++++++++++++++++++++++++
|
||||
5 files changed, 41 insertions(+), 32 deletions(-)
|
||||
|
||||
--- a/include/linux/netfilter/x_tables.h
|
||||
+++ b/include/linux/netfilter/x_tables.h
|
||||
@@ -242,6 +242,10 @@ void xt_unregister_match(struct xt_match
|
||||
int xt_register_matches(struct xt_match *match, unsigned int n);
|
||||
void xt_unregister_matches(struct xt_match *match, unsigned int n);
|
||||
|
||||
+int xt_check_entry_offsets(const void *base,
|
||||
+ unsigned int target_offset,
|
||||
+ unsigned int next_offset);
|
||||
+
|
||||
int xt_check_match(struct xt_mtchk_param *, unsigned int size, u_int8_t proto,
|
||||
bool inv_proto);
|
||||
int xt_check_target(struct xt_tgchk_param *, unsigned int size, u_int8_t proto,
|
||||
--- a/net/ipv4/netfilter/arp_tables.c
|
||||
+++ b/net/ipv4/netfilter/arp_tables.c
|
||||
@@ -496,19 +496,10 @@ next:
|
||||
|
||||
static inline int check_entry(const struct arpt_entry *e)
|
||||
{
|
||||
- const struct xt_entry_target *t;
|
||||
-
|
||||
if (!arp_checkentry(&e->arp))
|
||||
return -EINVAL;
|
||||
|
||||
- if (e->target_offset + sizeof(struct xt_entry_target) > e->next_offset)
|
||||
- return -EINVAL;
|
||||
-
|
||||
- t = arpt_get_target_c(e);
|
||||
- if (e->target_offset + t->u.target_size > e->next_offset)
|
||||
- return -EINVAL;
|
||||
-
|
||||
- return 0;
|
||||
+ return xt_check_entry_offsets(e, e->target_offset, e->next_offset);
|
||||
}
|
||||
|
||||
static inline int check_target(struct arpt_entry *e, const char *name)
|
||||
--- a/net/ipv4/netfilter/ip_tables.c
|
||||
+++ b/net/ipv4/netfilter/ip_tables.c
|
||||
@@ -590,20 +590,10 @@ static void cleanup_match(struct xt_entr
|
||||
static int
|
||||
check_entry(const struct ipt_entry *e)
|
||||
{
|
||||
- const struct xt_entry_target *t;
|
||||
-
|
||||
if (!ip_checkentry(&e->ip))
|
||||
return -EINVAL;
|
||||
|
||||
- if (e->target_offset + sizeof(struct xt_entry_target) >
|
||||
- e->next_offset)
|
||||
- return -EINVAL;
|
||||
-
|
||||
- t = ipt_get_target_c(e);
|
||||
- if (e->target_offset + t->u.target_size > e->next_offset)
|
||||
- return -EINVAL;
|
||||
-
|
||||
- return 0;
|
||||
+ return xt_check_entry_offsets(e, e->target_offset, e->next_offset);
|
||||
}
|
||||
|
||||
static int
|
||||
--- a/net/ipv6/netfilter/ip6_tables.c
|
||||
+++ b/net/ipv6/netfilter/ip6_tables.c
|
||||
@@ -602,20 +602,10 @@ static void cleanup_match(struct xt_entr
|
||||
static int
|
||||
check_entry(const struct ip6t_entry *e)
|
||||
{
|
||||
- const struct xt_entry_target *t;
|
||||
-
|
||||
if (!ip6_checkentry(&e->ipv6))
|
||||
return -EINVAL;
|
||||
|
||||
- if (e->target_offset + sizeof(struct xt_entry_target) >
|
||||
- e->next_offset)
|
||||
- return -EINVAL;
|
||||
-
|
||||
- t = ip6t_get_target_c(e);
|
||||
- if (e->target_offset + t->u.target_size > e->next_offset)
|
||||
- return -EINVAL;
|
||||
-
|
||||
- return 0;
|
||||
+ return xt_check_entry_offsets(e, e->target_offset, e->next_offset);
|
||||
}
|
||||
|
||||
static int check_match(struct xt_entry_match *m, struct xt_mtchk_param *par)
|
||||
--- a/net/netfilter/x_tables.c
|
||||
+++ b/net/netfilter/x_tables.c
|
||||
@@ -541,6 +541,40 @@ int xt_compat_match_to_user(const struct
|
||||
EXPORT_SYMBOL_GPL(xt_compat_match_to_user);
|
||||
#endif /* CONFIG_COMPAT */
|
||||
|
||||
+/**
|
||||
+ * xt_check_entry_offsets - validate arp/ip/ip6t_entry
|
||||
+ *
|
||||
+ * @base: pointer to arp/ip/ip6t_entry
|
||||
+ * @target_offset: the arp/ip/ip6_t->target_offset
|
||||
+ * @next_offset: the arp/ip/ip6_t->next_offset
|
||||
+ *
|
||||
+ * validates that target_offset and next_offset are sane.
|
||||
+ *
|
||||
+ * The arp/ip/ip6t_entry structure @base must have passed following tests:
|
||||
+ * - it must point to a valid memory location
|
||||
+ * - base to base + next_offset must be accessible, i.e. not exceed allocated
|
||||
+ * length.
|
||||
+ *
|
||||
+ * Return: 0 on success, negative errno on failure.
|
||||
+ */
|
||||
+int xt_check_entry_offsets(const void *base,
|
||||
+ unsigned int target_offset,
|
||||
+ unsigned int next_offset)
|
||||
+{
|
||||
+ const struct xt_entry_target *t;
|
||||
+ const char *e = base;
|
||||
+
|
||||
+ if (target_offset + sizeof(*t) > next_offset)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ t = (void *)(e + target_offset);
|
||||
+ if (target_offset + t->u.target_size > next_offset)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+EXPORT_SYMBOL(xt_check_entry_offsets);
|
||||
+
|
||||
int xt_check_target(struct xt_tgchk_param *par,
|
||||
unsigned int size, u_int8_t proto, bool inv_proto)
|
||||
{
|
105
debian/patches/bugfix/all/netfilter-x_tables-add-compat-version-of-xt_check_en.patch
vendored
Normal file
105
debian/patches/bugfix/all/netfilter-x_tables-add-compat-version-of-xt_check_en.patch
vendored
Normal file
|
@ -0,0 +1,105 @@
|
|||
From: Florian Westphal <fw@strlen.de>
|
||||
Date: Fri, 1 Apr 2016 14:17:26 +0200
|
||||
Subject: netfilter: x_tables: add compat version of xt_check_entry_offsets
|
||||
Origin: https://git.kernel.org/linus/fc1221b3a163d1386d1052184202d5dc50d302d1
|
||||
|
||||
32bit rulesets have different layout and alignment requirements, so once
|
||||
more integrity checks get added to xt_check_entry_offsets it will reject
|
||||
well-formed 32bit rulesets.
|
||||
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
---
|
||||
include/linux/netfilter/x_tables.h | 3 +++
|
||||
net/ipv4/netfilter/arp_tables.c | 3 ++-
|
||||
net/ipv4/netfilter/ip_tables.c | 3 ++-
|
||||
net/ipv6/netfilter/ip6_tables.c | 3 ++-
|
||||
net/netfilter/x_tables.c | 22 ++++++++++++++++++++++
|
||||
5 files changed, 31 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/include/linux/netfilter/x_tables.h
|
||||
+++ b/include/linux/netfilter/x_tables.h
|
||||
@@ -494,6 +494,9 @@ void xt_compat_target_from_user(struct x
|
||||
unsigned int *size);
|
||||
int xt_compat_target_to_user(const struct xt_entry_target *t,
|
||||
void __user **dstptr, unsigned int *size);
|
||||
+int xt_compat_check_entry_offsets(const void *base,
|
||||
+ unsigned int target_offset,
|
||||
+ unsigned int next_offset);
|
||||
|
||||
#endif /* CONFIG_COMPAT */
|
||||
#endif /* _X_TABLES_H */
|
||||
--- a/net/ipv4/netfilter/arp_tables.c
|
||||
+++ b/net/ipv4/netfilter/arp_tables.c
|
||||
@@ -1254,7 +1254,8 @@ check_compat_entry_size_and_hooks(struct
|
||||
if (!arp_checkentry(&e->arp))
|
||||
return -EINVAL;
|
||||
|
||||
- ret = xt_check_entry_offsets(e, e->target_offset, e->next_offset);
|
||||
+ ret = xt_compat_check_entry_offsets(e, e->target_offset,
|
||||
+ e->next_offset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
--- a/net/ipv4/netfilter/ip_tables.c
|
||||
+++ b/net/ipv4/netfilter/ip_tables.c
|
||||
@@ -1513,7 +1513,8 @@ check_compat_entry_size_and_hooks(struct
|
||||
if (!ip_checkentry(&e->ip))
|
||||
return -EINVAL;
|
||||
|
||||
- ret = xt_check_entry_offsets(e, e->target_offset, e->next_offset);
|
||||
+ ret = xt_compat_check_entry_offsets(e,
|
||||
+ e->target_offset, e->next_offset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
--- a/net/ipv6/netfilter/ip6_tables.c
|
||||
+++ b/net/ipv6/netfilter/ip6_tables.c
|
||||
@@ -1525,7 +1525,8 @@ check_compat_entry_size_and_hooks(struct
|
||||
if (!ip6_checkentry(&e->ipv6))
|
||||
return -EINVAL;
|
||||
|
||||
- ret = xt_check_entry_offsets(e, e->target_offset, e->next_offset);
|
||||
+ ret = xt_compat_check_entry_offsets(e,
|
||||
+ e->target_offset, e->next_offset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
--- a/net/netfilter/x_tables.c
|
||||
+++ b/net/netfilter/x_tables.c
|
||||
@@ -539,6 +539,27 @@ int xt_compat_match_to_user(const struct
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xt_compat_match_to_user);
|
||||
+
|
||||
+int xt_compat_check_entry_offsets(const void *base,
|
||||
+ unsigned int target_offset,
|
||||
+ unsigned int next_offset)
|
||||
+{
|
||||
+ const struct compat_xt_entry_target *t;
|
||||
+ const char *e = base;
|
||||
+
|
||||
+ if (target_offset + sizeof(*t) > next_offset)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ t = (void *)(e + target_offset);
|
||||
+ if (t->u.target_size < sizeof(*t))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (target_offset + t->u.target_size > next_offset)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+EXPORT_SYMBOL(xt_compat_check_entry_offsets);
|
||||
#endif /* CONFIG_COMPAT */
|
||||
|
||||
/**
|
||||
@@ -549,6 +570,7 @@ EXPORT_SYMBOL_GPL(xt_compat_match_to_use
|
||||
* @next_offset: the arp/ip/ip6_t->next_offset
|
||||
*
|
||||
* validates that target_offset and next_offset are sane.
|
||||
+ * Also see xt_compat_check_entry_offsets for CONFIG_COMPAT version.
|
||||
*
|
||||
* The arp/ip/ip6t_entry structure @base must have passed following tests:
|
||||
* - it must point to a valid memory location
|
25
debian/patches/bugfix/all/netfilter-x_tables-assert-minimum-target-size.patch
vendored
Normal file
25
debian/patches/bugfix/all/netfilter-x_tables-assert-minimum-target-size.patch
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
From: Florian Westphal <fw@strlen.de>
|
||||
Date: Fri, 1 Apr 2016 14:17:25 +0200
|
||||
Subject: netfilter: x_tables: assert minimum target size
|
||||
Origin: https://git.kernel.org/linus/a08e4e190b866579896c09af59b3bdca821da2cd
|
||||
|
||||
The target size includes the size of the xt_entry_target struct.
|
||||
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
---
|
||||
net/netfilter/x_tables.c | 3 +++
|
||||
1 file changed, 3 insertions(+)
|
||||
|
||||
--- a/net/netfilter/x_tables.c
|
||||
+++ b/net/netfilter/x_tables.c
|
||||
@@ -568,6 +568,9 @@ int xt_check_entry_offsets(const void *b
|
||||
return -EINVAL;
|
||||
|
||||
t = (void *)(e + target_offset);
|
||||
+ if (t->u.target_size < sizeof(*t))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
if (target_offset + t->u.target_size > next_offset)
|
||||
return -EINVAL;
|
||||
|
164
debian/patches/bugfix/all/netfilter-x_tables-check-for-bogus-target-offset.patch
vendored
Normal file
164
debian/patches/bugfix/all/netfilter-x_tables-check-for-bogus-target-offset.patch
vendored
Normal file
|
@ -0,0 +1,164 @@
|
|||
From: Florian Westphal <fw@strlen.de>
|
||||
Date: Fri, 1 Apr 2016 14:17:28 +0200
|
||||
Subject: netfilter: x_tables: check for bogus target offset
|
||||
Origin: https://git.kernel.org/linus/ce683e5f9d045e5d67d1312a42b359cb2ab2a13c
|
||||
|
||||
We're currently asserting that targetoff + targetsize <= nextoff.
|
||||
|
||||
Extend it to also check that targetoff is >= sizeof(xt_entry).
|
||||
Since this is generic code, add an argument pointing to the start of the
|
||||
match/target, we can then derive the base structure size from the delta.
|
||||
|
||||
We also need the e->elems pointer in a followup change to validate matches.
|
||||
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
---
|
||||
include/linux/netfilter/x_tables.h | 4 ++--
|
||||
net/ipv4/netfilter/arp_tables.c | 5 +++--
|
||||
net/ipv4/netfilter/ip_tables.c | 5 +++--
|
||||
net/ipv6/netfilter/ip6_tables.c | 5 +++--
|
||||
net/netfilter/x_tables.c | 17 +++++++++++++++--
|
||||
5 files changed, 26 insertions(+), 10 deletions(-)
|
||||
|
||||
--- a/include/linux/netfilter/x_tables.h
|
||||
+++ b/include/linux/netfilter/x_tables.h
|
||||
@@ -242,7 +242,7 @@ void xt_unregister_match(struct xt_match
|
||||
int xt_register_matches(struct xt_match *match, unsigned int n);
|
||||
void xt_unregister_matches(struct xt_match *match, unsigned int n);
|
||||
|
||||
-int xt_check_entry_offsets(const void *base,
|
||||
+int xt_check_entry_offsets(const void *base, const char *elems,
|
||||
unsigned int target_offset,
|
||||
unsigned int next_offset);
|
||||
|
||||
@@ -494,7 +494,7 @@ void xt_compat_target_from_user(struct x
|
||||
unsigned int *size);
|
||||
int xt_compat_target_to_user(const struct xt_entry_target *t,
|
||||
void __user **dstptr, unsigned int *size);
|
||||
-int xt_compat_check_entry_offsets(const void *base,
|
||||
+int xt_compat_check_entry_offsets(const void *base, const char *elems,
|
||||
unsigned int target_offset,
|
||||
unsigned int next_offset);
|
||||
|
||||
--- a/net/ipv4/netfilter/arp_tables.c
|
||||
+++ b/net/ipv4/netfilter/arp_tables.c
|
||||
@@ -592,7 +592,8 @@ static inline int check_entry_size_and_h
|
||||
if (!arp_checkentry(&e->arp))
|
||||
return -EINVAL;
|
||||
|
||||
- err = xt_check_entry_offsets(e, e->target_offset, e->next_offset);
|
||||
+ err = xt_check_entry_offsets(e, e->elems, e->target_offset,
|
||||
+ e->next_offset);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -1254,7 +1255,7 @@ check_compat_entry_size_and_hooks(struct
|
||||
if (!arp_checkentry(&e->arp))
|
||||
return -EINVAL;
|
||||
|
||||
- ret = xt_compat_check_entry_offsets(e, e->target_offset,
|
||||
+ ret = xt_compat_check_entry_offsets(e, e->elems, e->target_offset,
|
||||
e->next_offset);
|
||||
if (ret)
|
||||
return ret;
|
||||
--- a/net/ipv4/netfilter/ip_tables.c
|
||||
+++ b/net/ipv4/netfilter/ip_tables.c
|
||||
@@ -754,7 +754,8 @@ check_entry_size_and_hooks(struct ipt_en
|
||||
if (!ip_checkentry(&e->ip))
|
||||
return -EINVAL;
|
||||
|
||||
- err = xt_check_entry_offsets(e, e->target_offset, e->next_offset);
|
||||
+ err = xt_check_entry_offsets(e, e->elems, e->target_offset,
|
||||
+ e->next_offset);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -1513,7 +1514,7 @@ check_compat_entry_size_and_hooks(struct
|
||||
if (!ip_checkentry(&e->ip))
|
||||
return -EINVAL;
|
||||
|
||||
- ret = xt_compat_check_entry_offsets(e,
|
||||
+ ret = xt_compat_check_entry_offsets(e, e->elems,
|
||||
e->target_offset, e->next_offset);
|
||||
if (ret)
|
||||
return ret;
|
||||
--- a/net/ipv6/netfilter/ip6_tables.c
|
||||
+++ b/net/ipv6/netfilter/ip6_tables.c
|
||||
@@ -766,7 +766,8 @@ check_entry_size_and_hooks(struct ip6t_e
|
||||
if (!ip6_checkentry(&e->ipv6))
|
||||
return -EINVAL;
|
||||
|
||||
- err = xt_check_entry_offsets(e, e->target_offset, e->next_offset);
|
||||
+ err = xt_check_entry_offsets(e, e->elems, e->target_offset,
|
||||
+ e->next_offset);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -1525,7 +1526,7 @@ check_compat_entry_size_and_hooks(struct
|
||||
if (!ip6_checkentry(&e->ipv6))
|
||||
return -EINVAL;
|
||||
|
||||
- ret = xt_compat_check_entry_offsets(e,
|
||||
+ ret = xt_compat_check_entry_offsets(e, e->elems,
|
||||
e->target_offset, e->next_offset);
|
||||
if (ret)
|
||||
return ret;
|
||||
--- a/net/netfilter/x_tables.c
|
||||
+++ b/net/netfilter/x_tables.c
|
||||
@@ -546,14 +546,17 @@ struct compat_xt_standard_target {
|
||||
compat_uint_t verdict;
|
||||
};
|
||||
|
||||
-/* see xt_check_entry_offsets */
|
||||
-int xt_compat_check_entry_offsets(const void *base,
|
||||
+int xt_compat_check_entry_offsets(const void *base, const char *elems,
|
||||
unsigned int target_offset,
|
||||
unsigned int next_offset)
|
||||
{
|
||||
+ long size_of_base_struct = elems - (const char *)base;
|
||||
const struct compat_xt_entry_target *t;
|
||||
const char *e = base;
|
||||
|
||||
+ if (target_offset < size_of_base_struct)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
if (target_offset + sizeof(*t) > next_offset)
|
||||
return -EINVAL;
|
||||
|
||||
@@ -577,12 +580,16 @@ EXPORT_SYMBOL(xt_compat_check_entry_offs
|
||||
* xt_check_entry_offsets - validate arp/ip/ip6t_entry
|
||||
*
|
||||
* @base: pointer to arp/ip/ip6t_entry
|
||||
+ * @elems: pointer to first xt_entry_match, i.e. ip(6)t_entry->elems
|
||||
* @target_offset: the arp/ip/ip6_t->target_offset
|
||||
* @next_offset: the arp/ip/ip6_t->next_offset
|
||||
*
|
||||
* validates that target_offset and next_offset are sane.
|
||||
* Also see xt_compat_check_entry_offsets for CONFIG_COMPAT version.
|
||||
*
|
||||
+ * This function does not validate the targets or matches themselves, it
|
||||
+ * only tests that all the offsets and sizes are correct.
|
||||
+ *
|
||||
* The arp/ip/ip6t_entry structure @base must have passed following tests:
|
||||
* - it must point to a valid memory location
|
||||
* - base to base + next_offset must be accessible, i.e. not exceed allocated
|
||||
@@ -591,12 +598,18 @@ EXPORT_SYMBOL(xt_compat_check_entry_offs
|
||||
* Return: 0 on success, negative errno on failure.
|
||||
*/
|
||||
int xt_check_entry_offsets(const void *base,
|
||||
+ const char *elems,
|
||||
unsigned int target_offset,
|
||||
unsigned int next_offset)
|
||||
{
|
||||
+ long size_of_base_struct = elems - (const char *)base;
|
||||
const struct xt_entry_target *t;
|
||||
const char *e = base;
|
||||
|
||||
+ /* target start is within the ip/ip6/arpt_entry struct */
|
||||
+ if (target_offset < size_of_base_struct)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
if (target_offset + sizeof(*t) > next_offset)
|
||||
return -EINVAL;
|
||||
|
60
debian/patches/bugfix/all/netfilter-x_tables-check-standard-target-size-too.patch
vendored
Normal file
60
debian/patches/bugfix/all/netfilter-x_tables-check-standard-target-size-too.patch
vendored
Normal file
|
@ -0,0 +1,60 @@
|
|||
From: Florian Westphal <fw@strlen.de>
|
||||
Date: Fri, 1 Apr 2016 14:17:27 +0200
|
||||
Subject: netfilter: x_tables: check standard target size too
|
||||
Origin: https://git.kernel.org/linus/7ed2abddd20cf8f6bd27f65bd218f26fa5bf7f44
|
||||
|
||||
We have targets and standard targets -- the latter carries a verdict.
|
||||
|
||||
The ip/ip6tables validation functions will access t->verdict for the
|
||||
standard targets to fetch the jump offset or verdict for chainloop
|
||||
detection, but this happens before the targets get checked/validated.
|
||||
|
||||
Thus we also need to check for verdict presence here, else t->verdict
|
||||
can point right after a blob.
|
||||
|
||||
Spotted with UBSAN while testing malformed blobs.
|
||||
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
---
|
||||
net/netfilter/x_tables.c | 15 +++++++++++++++
|
||||
1 file changed, 15 insertions(+)
|
||||
|
||||
--- a/net/netfilter/x_tables.c
|
||||
+++ b/net/netfilter/x_tables.c
|
||||
@@ -540,6 +540,13 @@ int xt_compat_match_to_user(const struct
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xt_compat_match_to_user);
|
||||
|
||||
+/* non-compat version may have padding after verdict */
|
||||
+struct compat_xt_standard_target {
|
||||
+ struct compat_xt_entry_target t;
|
||||
+ compat_uint_t verdict;
|
||||
+};
|
||||
+
|
||||
+/* see xt_check_entry_offsets */
|
||||
int xt_compat_check_entry_offsets(const void *base,
|
||||
unsigned int target_offset,
|
||||
unsigned int next_offset)
|
||||
@@ -557,6 +564,10 @@ int xt_compat_check_entry_offsets(const
|
||||
if (target_offset + t->u.target_size > next_offset)
|
||||
return -EINVAL;
|
||||
|
||||
+ if (strcmp(t->u.user.name, XT_STANDARD_TARGET) == 0 &&
|
||||
+ target_offset + sizeof(struct compat_xt_standard_target) != next_offset)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(xt_compat_check_entry_offsets);
|
||||
@@ -596,6 +607,10 @@ int xt_check_entry_offsets(const void *b
|
||||
if (target_offset + t->u.target_size > next_offset)
|
||||
return -EINVAL;
|
||||
|
||||
+ if (strcmp(t->u.user.name, XT_STANDARD_TARGET) == 0 &&
|
||||
+ target_offset + sizeof(struct xt_standard_target) != next_offset)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(xt_check_entry_offsets);
|
798
debian/patches/bugfix/all/netfilter-x_tables-do-compat-validation-via-translat.patch
vendored
Normal file
798
debian/patches/bugfix/all/netfilter-x_tables-do-compat-validation-via-translat.patch
vendored
Normal file
|
@ -0,0 +1,798 @@
|
|||
From: Florian Westphal <fw@strlen.de>
|
||||
Date: Fri, 1 Apr 2016 14:17:34 +0200
|
||||
Subject: netfilter: x_tables: do compat validation via translate_table
|
||||
Origin: https://git.kernel.org/linus/09d9686047dbbe1cf4faa558d3ecc4aae2046054
|
||||
|
||||
This looks like refactoring, but its also a bug fix.
|
||||
|
||||
Problem is that the compat path (32bit iptables, 64bit kernel) lacks a few
|
||||
sanity tests that are done in the normal path.
|
||||
|
||||
For example, we do not check for underflows and the base chain policies.
|
||||
|
||||
While its possible to also add such checks to the compat path, its more
|
||||
copy&pastry, for instance we cannot reuse check_underflow() helper as
|
||||
e->target_offset differs in the compat case.
|
||||
|
||||
Other problem is that it makes auditing for validation errors harder; two
|
||||
places need to be checked and kept in sync.
|
||||
|
||||
At a high level 32 bit compat works like this:
|
||||
1- initial pass over blob:
|
||||
validate match/entry offsets, bounds checking
|
||||
lookup all matches and targets
|
||||
do bookkeeping wrt. size delta of 32/64bit structures
|
||||
assign match/target.u.kernel pointer (points at kernel
|
||||
implementation, needed to access ->compatsize etc.)
|
||||
|
||||
2- allocate memory according to the total bookkeeping size to
|
||||
contain the translated ruleset
|
||||
|
||||
3- second pass over original blob:
|
||||
for each entry, copy the 32bit representation to the newly allocated
|
||||
memory. This also does any special match translations (e.g.
|
||||
adjust 32bit to 64bit longs, etc).
|
||||
|
||||
4- check if ruleset is free of loops (chase all jumps)
|
||||
|
||||
5-first pass over translated blob:
|
||||
call the checkentry function of all matches and targets.
|
||||
|
||||
The alternative implemented by this patch is to drop steps 3&4 from the
|
||||
compat process, the translation is changed into an intermediate step
|
||||
rather than a full 1:1 translate_table replacement.
|
||||
|
||||
In the 2nd pass (step #3), change the 64bit ruleset back to a kernel
|
||||
representation, i.e. put() the kernel pointer and restore ->u.user.name .
|
||||
|
||||
This gets us a 64bit ruleset that is in the format generated by a 64bit
|
||||
iptables userspace -- we can then use translate_table() to get the
|
||||
'native' sanity checks.
|
||||
|
||||
This has two drawbacks:
|
||||
|
||||
1. we re-validate all the match and target entry structure sizes even
|
||||
though compat translation is supposed to never generate bogus offsets.
|
||||
2. we put and then re-lookup each match and target.
|
||||
|
||||
THe upside is that we get all sanity tests and ruleset validations
|
||||
provided by the normal path and can remove some duplicated compat code.
|
||||
|
||||
iptables-restore time of autogenerated ruleset with 300k chains of form
|
||||
-A CHAIN0001 -m limit --limit 1/s -j CHAIN0002
|
||||
-A CHAIN0002 -m limit --limit 1/s -j CHAIN0003
|
||||
|
||||
shows no noticeable differences in restore times:
|
||||
old: 0m30.796s
|
||||
new: 0m31.521s
|
||||
64bit: 0m25.674s
|
||||
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
---
|
||||
net/ipv4/netfilter/arp_tables.c | 114 ++++++-----------------------
|
||||
net/ipv4/netfilter/ip_tables.c | 155 ++++++++--------------------------------
|
||||
net/ipv6/netfilter/ip6_tables.c | 148 ++++++--------------------------------
|
||||
net/netfilter/x_tables.c | 8 +++
|
||||
4 files changed, 83 insertions(+), 342 deletions(-)
|
||||
|
||||
--- a/net/ipv4/netfilter/arp_tables.c
|
||||
+++ b/net/ipv4/netfilter/arp_tables.c
|
||||
@@ -1234,19 +1234,17 @@ static inline void compat_release_entry(
|
||||
module_put(t->u.kernel.target->me);
|
||||
}
|
||||
|
||||
-static inline int
|
||||
+static int
|
||||
check_compat_entry_size_and_hooks(struct compat_arpt_entry *e,
|
||||
struct xt_table_info *newinfo,
|
||||
unsigned int *size,
|
||||
const unsigned char *base,
|
||||
- const unsigned char *limit,
|
||||
- const unsigned int *hook_entries,
|
||||
- const unsigned int *underflows)
|
||||
+ const unsigned char *limit)
|
||||
{
|
||||
struct xt_entry_target *t;
|
||||
struct xt_target *target;
|
||||
unsigned int entry_offset;
|
||||
- int ret, off, h;
|
||||
+ int ret, off;
|
||||
|
||||
duprintf("check_compat_entry_size_and_hooks %p\n", e);
|
||||
if ((unsigned long)e % __alignof__(struct compat_arpt_entry) != 0 ||
|
||||
@@ -1291,17 +1289,6 @@ check_compat_entry_size_and_hooks(struct
|
||||
if (ret)
|
||||
goto release_target;
|
||||
|
||||
- /* Check hooks & underflows */
|
||||
- for (h = 0; h < NF_ARP_NUMHOOKS; h++) {
|
||||
- if ((unsigned char *)e - base == hook_entries[h])
|
||||
- newinfo->hook_entry[h] = hook_entries[h];
|
||||
- if ((unsigned char *)e - base == underflows[h])
|
||||
- newinfo->underflow[h] = underflows[h];
|
||||
- }
|
||||
-
|
||||
- /* Clear counters and comefrom */
|
||||
- memset(&e->counters, 0, sizeof(e->counters));
|
||||
- e->comefrom = 0;
|
||||
return 0;
|
||||
|
||||
release_target:
|
||||
@@ -1351,7 +1338,7 @@ static int translate_compat_table(struct
|
||||
struct xt_table_info *newinfo, *info;
|
||||
void *pos, *entry0, *entry1;
|
||||
struct compat_arpt_entry *iter0;
|
||||
- struct arpt_entry *iter1;
|
||||
+ struct arpt_replace repl;
|
||||
unsigned int size;
|
||||
int ret = 0;
|
||||
|
||||
@@ -1360,12 +1347,6 @@ static int translate_compat_table(struct
|
||||
size = compatr->size;
|
||||
info->number = compatr->num_entries;
|
||||
|
||||
- /* Init all hooks to impossible value. */
|
||||
- for (i = 0; i < NF_ARP_NUMHOOKS; i++) {
|
||||
- info->hook_entry[i] = 0xFFFFFFFF;
|
||||
- info->underflow[i] = 0xFFFFFFFF;
|
||||
- }
|
||||
-
|
||||
duprintf("translate_compat_table: size %u\n", info->size);
|
||||
j = 0;
|
||||
xt_compat_lock(NFPROTO_ARP);
|
||||
@@ -1374,9 +1355,7 @@ static int translate_compat_table(struct
|
||||
xt_entry_foreach(iter0, entry0, compatr->size) {
|
||||
ret = check_compat_entry_size_and_hooks(iter0, info, &size,
|
||||
entry0,
|
||||
- entry0 + compatr->size,
|
||||
- compatr->hook_entry,
|
||||
- compatr->underflow);
|
||||
+ entry0 + compatr->size);
|
||||
if (ret != 0)
|
||||
goto out_unlock;
|
||||
++j;
|
||||
@@ -1389,23 +1368,6 @@ static int translate_compat_table(struct
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
- /* Check hooks all assigned */
|
||||
- for (i = 0; i < NF_ARP_NUMHOOKS; i++) {
|
||||
- /* Only hooks which are valid */
|
||||
- if (!(compatr->valid_hooks & (1 << i)))
|
||||
- continue;
|
||||
- if (info->hook_entry[i] == 0xFFFFFFFF) {
|
||||
- duprintf("Invalid hook entry %u %u\n",
|
||||
- i, info->hook_entry[i]);
|
||||
- goto out_unlock;
|
||||
- }
|
||||
- if (info->underflow[i] == 0xFFFFFFFF) {
|
||||
- duprintf("Invalid underflow %u %u\n",
|
||||
- i, info->underflow[i]);
|
||||
- goto out_unlock;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
ret = -ENOMEM;
|
||||
newinfo = xt_alloc_table_info(size);
|
||||
if (!newinfo)
|
||||
@@ -1422,55 +1384,26 @@ static int translate_compat_table(struct
|
||||
xt_entry_foreach(iter0, entry0, compatr->size)
|
||||
compat_copy_entry_from_user(iter0, &pos, &size,
|
||||
newinfo, entry1);
|
||||
+
|
||||
+ /* all module references in entry0 are now gone */
|
||||
+
|
||||
xt_compat_flush_offsets(NFPROTO_ARP);
|
||||
xt_compat_unlock(NFPROTO_ARP);
|
||||
|
||||
- ret = -ELOOP;
|
||||
- if (!mark_source_chains(newinfo, compatr->valid_hooks, entry1))
|
||||
- goto free_newinfo;
|
||||
-
|
||||
- i = 0;
|
||||
- xt_entry_foreach(iter1, entry1, newinfo->size) {
|
||||
- iter1->counters.pcnt = xt_percpu_counter_alloc();
|
||||
- if (IS_ERR_VALUE(iter1->counters.pcnt)) {
|
||||
- ret = -ENOMEM;
|
||||
- break;
|
||||
- }
|
||||
+ memcpy(&repl, compatr, sizeof(*compatr));
|
||||
|
||||
- ret = check_target(iter1, compatr->name);
|
||||
- if (ret != 0) {
|
||||
- xt_percpu_counter_free(iter1->counters.pcnt);
|
||||
- break;
|
||||
- }
|
||||
- ++i;
|
||||
- if (strcmp(arpt_get_target(iter1)->u.user.name,
|
||||
- XT_ERROR_TARGET) == 0)
|
||||
- ++newinfo->stacksize;
|
||||
- }
|
||||
- if (ret) {
|
||||
- /*
|
||||
- * The first i matches need cleanup_entry (calls ->destroy)
|
||||
- * because they had called ->check already. The other j-i
|
||||
- * entries need only release.
|
||||
- */
|
||||
- int skip = i;
|
||||
- j -= i;
|
||||
- xt_entry_foreach(iter0, entry0, newinfo->size) {
|
||||
- if (skip-- > 0)
|
||||
- continue;
|
||||
- if (j-- == 0)
|
||||
- break;
|
||||
- compat_release_entry(iter0);
|
||||
- }
|
||||
- xt_entry_foreach(iter1, entry1, newinfo->size) {
|
||||
- if (i-- == 0)
|
||||
- break;
|
||||
- cleanup_entry(iter1);
|
||||
- }
|
||||
- xt_free_table_info(newinfo);
|
||||
- return ret;
|
||||
+ for (i = 0; i < NF_ARP_NUMHOOKS; i++) {
|
||||
+ repl.hook_entry[i] = newinfo->hook_entry[i];
|
||||
+ repl.underflow[i] = newinfo->underflow[i];
|
||||
}
|
||||
|
||||
+ repl.num_counters = 0;
|
||||
+ repl.counters = NULL;
|
||||
+ repl.size = newinfo->size;
|
||||
+ ret = translate_table(newinfo, entry1, &repl);
|
||||
+ if (ret)
|
||||
+ goto free_newinfo;
|
||||
+
|
||||
*pinfo = newinfo;
|
||||
*pentry0 = entry1;
|
||||
xt_free_table_info(info);
|
||||
@@ -1478,17 +1411,16 @@ static int translate_compat_table(struct
|
||||
|
||||
free_newinfo:
|
||||
xt_free_table_info(newinfo);
|
||||
-out:
|
||||
+ return ret;
|
||||
+out_unlock:
|
||||
+ xt_compat_flush_offsets(NFPROTO_ARP);
|
||||
+ xt_compat_unlock(NFPROTO_ARP);
|
||||
xt_entry_foreach(iter0, entry0, compatr->size) {
|
||||
if (j-- == 0)
|
||||
break;
|
||||
compat_release_entry(iter0);
|
||||
}
|
||||
return ret;
|
||||
-out_unlock:
|
||||
- xt_compat_flush_offsets(NFPROTO_ARP);
|
||||
- xt_compat_unlock(NFPROTO_ARP);
|
||||
- goto out;
|
||||
}
|
||||
|
||||
static int compat_do_replace(struct net *net, void __user *user,
|
||||
--- a/net/ipv4/netfilter/ip_tables.c
|
||||
+++ b/net/ipv4/netfilter/ip_tables.c
|
||||
@@ -1483,16 +1483,14 @@ check_compat_entry_size_and_hooks(struct
|
||||
struct xt_table_info *newinfo,
|
||||
unsigned int *size,
|
||||
const unsigned char *base,
|
||||
- const unsigned char *limit,
|
||||
- const unsigned int *hook_entries,
|
||||
- const unsigned int *underflows)
|
||||
+ const unsigned char *limit)
|
||||
{
|
||||
struct xt_entry_match *ematch;
|
||||
struct xt_entry_target *t;
|
||||
struct xt_target *target;
|
||||
unsigned int entry_offset;
|
||||
unsigned int j;
|
||||
- int ret, off, h;
|
||||
+ int ret, off;
|
||||
|
||||
duprintf("check_compat_entry_size_and_hooks %p\n", e);
|
||||
if ((unsigned long)e % __alignof__(struct compat_ipt_entry) != 0 ||
|
||||
@@ -1544,17 +1542,6 @@ check_compat_entry_size_and_hooks(struct
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
- /* Check hooks & underflows */
|
||||
- for (h = 0; h < NF_INET_NUMHOOKS; h++) {
|
||||
- if ((unsigned char *)e - base == hook_entries[h])
|
||||
- newinfo->hook_entry[h] = hook_entries[h];
|
||||
- if ((unsigned char *)e - base == underflows[h])
|
||||
- newinfo->underflow[h] = underflows[h];
|
||||
- }
|
||||
-
|
||||
- /* Clear counters and comefrom */
|
||||
- memset(&e->counters, 0, sizeof(e->counters));
|
||||
- e->comefrom = 0;
|
||||
return 0;
|
||||
|
||||
out:
|
||||
@@ -1597,6 +1584,7 @@ compat_copy_entry_from_user(struct compa
|
||||
xt_compat_target_from_user(t, dstptr, size);
|
||||
|
||||
de->next_offset = e->next_offset - (origsize - *size);
|
||||
+
|
||||
for (h = 0; h < NF_INET_NUMHOOKS; h++) {
|
||||
if ((unsigned char *)de - base < newinfo->hook_entry[h])
|
||||
newinfo->hook_entry[h] -= origsize - *size;
|
||||
@@ -1606,48 +1594,6 @@ compat_copy_entry_from_user(struct compa
|
||||
}
|
||||
|
||||
static int
|
||||
-compat_check_entry(struct ipt_entry *e, struct net *net, const char *name)
|
||||
-{
|
||||
- struct xt_entry_match *ematch;
|
||||
- struct xt_mtchk_param mtpar;
|
||||
- unsigned int j;
|
||||
- int ret = 0;
|
||||
-
|
||||
- e->counters.pcnt = xt_percpu_counter_alloc();
|
||||
- if (IS_ERR_VALUE(e->counters.pcnt))
|
||||
- return -ENOMEM;
|
||||
-
|
||||
- j = 0;
|
||||
- mtpar.net = net;
|
||||
- mtpar.table = name;
|
||||
- mtpar.entryinfo = &e->ip;
|
||||
- mtpar.hook_mask = e->comefrom;
|
||||
- mtpar.family = NFPROTO_IPV4;
|
||||
- xt_ematch_foreach(ematch, e) {
|
||||
- ret = check_match(ematch, &mtpar);
|
||||
- if (ret != 0)
|
||||
- goto cleanup_matches;
|
||||
- ++j;
|
||||
- }
|
||||
-
|
||||
- ret = check_target(e, net, name);
|
||||
- if (ret)
|
||||
- goto cleanup_matches;
|
||||
- return 0;
|
||||
-
|
||||
- cleanup_matches:
|
||||
- xt_ematch_foreach(ematch, e) {
|
||||
- if (j-- == 0)
|
||||
- break;
|
||||
- cleanup_match(ematch, net);
|
||||
- }
|
||||
-
|
||||
- xt_percpu_counter_free(e->counters.pcnt);
|
||||
-
|
||||
- return ret;
|
||||
-}
|
||||
-
|
||||
-static int
|
||||
translate_compat_table(struct net *net,
|
||||
struct xt_table_info **pinfo,
|
||||
void **pentry0,
|
||||
@@ -1657,7 +1603,7 @@ translate_compat_table(struct net *net,
|
||||
struct xt_table_info *newinfo, *info;
|
||||
void *pos, *entry0, *entry1;
|
||||
struct compat_ipt_entry *iter0;
|
||||
- struct ipt_entry *iter1;
|
||||
+ struct ipt_replace repl;
|
||||
unsigned int size;
|
||||
int ret;
|
||||
|
||||
@@ -1666,12 +1612,6 @@ translate_compat_table(struct net *net,
|
||||
size = compatr->size;
|
||||
info->number = compatr->num_entries;
|
||||
|
||||
- /* Init all hooks to impossible value. */
|
||||
- for (i = 0; i < NF_INET_NUMHOOKS; i++) {
|
||||
- info->hook_entry[i] = 0xFFFFFFFF;
|
||||
- info->underflow[i] = 0xFFFFFFFF;
|
||||
- }
|
||||
-
|
||||
duprintf("translate_compat_table: size %u\n", info->size);
|
||||
j = 0;
|
||||
xt_compat_lock(AF_INET);
|
||||
@@ -1680,9 +1620,7 @@ translate_compat_table(struct net *net,
|
||||
xt_entry_foreach(iter0, entry0, compatr->size) {
|
||||
ret = check_compat_entry_size_and_hooks(iter0, info, &size,
|
||||
entry0,
|
||||
- entry0 + compatr->size,
|
||||
- compatr->hook_entry,
|
||||
- compatr->underflow);
|
||||
+ entry0 + compatr->size);
|
||||
if (ret != 0)
|
||||
goto out_unlock;
|
||||
++j;
|
||||
@@ -1695,23 +1633,6 @@ translate_compat_table(struct net *net,
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
- /* Check hooks all assigned */
|
||||
- for (i = 0; i < NF_INET_NUMHOOKS; i++) {
|
||||
- /* Only hooks which are valid */
|
||||
- if (!(compatr->valid_hooks & (1 << i)))
|
||||
- continue;
|
||||
- if (info->hook_entry[i] == 0xFFFFFFFF) {
|
||||
- duprintf("Invalid hook entry %u %u\n",
|
||||
- i, info->hook_entry[i]);
|
||||
- goto out_unlock;
|
||||
- }
|
||||
- if (info->underflow[i] == 0xFFFFFFFF) {
|
||||
- duprintf("Invalid underflow %u %u\n",
|
||||
- i, info->underflow[i]);
|
||||
- goto out_unlock;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
ret = -ENOMEM;
|
||||
newinfo = xt_alloc_table_info(size);
|
||||
if (!newinfo)
|
||||
@@ -1719,8 +1640,8 @@ translate_compat_table(struct net *net,
|
||||
|
||||
newinfo->number = compatr->num_entries;
|
||||
for (i = 0; i < NF_INET_NUMHOOKS; i++) {
|
||||
- newinfo->hook_entry[i] = info->hook_entry[i];
|
||||
- newinfo->underflow[i] = info->underflow[i];
|
||||
+ newinfo->hook_entry[i] = compatr->hook_entry[i];
|
||||
+ newinfo->underflow[i] = compatr->underflow[i];
|
||||
}
|
||||
entry1 = newinfo->entries;
|
||||
pos = entry1;
|
||||
@@ -1729,47 +1650,30 @@ translate_compat_table(struct net *net,
|
||||
compat_copy_entry_from_user(iter0, &pos, &size,
|
||||
newinfo, entry1);
|
||||
|
||||
+ /* all module references in entry0 are now gone.
|
||||
+ * entry1/newinfo contains a 64bit ruleset that looks exactly as
|
||||
+ * generated by 64bit userspace.
|
||||
+ *
|
||||
+ * Call standard translate_table() to validate all hook_entrys,
|
||||
+ * underflows, check for loops, etc.
|
||||
+ */
|
||||
xt_compat_flush_offsets(AF_INET);
|
||||
xt_compat_unlock(AF_INET);
|
||||
|
||||
- ret = -ELOOP;
|
||||
- if (!mark_source_chains(newinfo, compatr->valid_hooks, entry1))
|
||||
- goto free_newinfo;
|
||||
+ memcpy(&repl, compatr, sizeof(*compatr));
|
||||
|
||||
- i = 0;
|
||||
- xt_entry_foreach(iter1, entry1, newinfo->size) {
|
||||
- ret = compat_check_entry(iter1, net, compatr->name);
|
||||
- if (ret != 0)
|
||||
- break;
|
||||
- ++i;
|
||||
- if (strcmp(ipt_get_target(iter1)->u.user.name,
|
||||
- XT_ERROR_TARGET) == 0)
|
||||
- ++newinfo->stacksize;
|
||||
- }
|
||||
- if (ret) {
|
||||
- /*
|
||||
- * The first i matches need cleanup_entry (calls ->destroy)
|
||||
- * because they had called ->check already. The other j-i
|
||||
- * entries need only release.
|
||||
- */
|
||||
- int skip = i;
|
||||
- j -= i;
|
||||
- xt_entry_foreach(iter0, entry0, newinfo->size) {
|
||||
- if (skip-- > 0)
|
||||
- continue;
|
||||
- if (j-- == 0)
|
||||
- break;
|
||||
- compat_release_entry(iter0);
|
||||
- }
|
||||
- xt_entry_foreach(iter1, entry1, newinfo->size) {
|
||||
- if (i-- == 0)
|
||||
- break;
|
||||
- cleanup_entry(iter1, net);
|
||||
- }
|
||||
- xt_free_table_info(newinfo);
|
||||
- return ret;
|
||||
+ for (i = 0; i < NF_INET_NUMHOOKS; i++) {
|
||||
+ repl.hook_entry[i] = newinfo->hook_entry[i];
|
||||
+ repl.underflow[i] = newinfo->underflow[i];
|
||||
}
|
||||
|
||||
+ repl.num_counters = 0;
|
||||
+ repl.counters = NULL;
|
||||
+ repl.size = newinfo->size;
|
||||
+ ret = translate_table(net, newinfo, entry1, &repl);
|
||||
+ if (ret)
|
||||
+ goto free_newinfo;
|
||||
+
|
||||
*pinfo = newinfo;
|
||||
*pentry0 = entry1;
|
||||
xt_free_table_info(info);
|
||||
@@ -1777,17 +1681,16 @@ translate_compat_table(struct net *net,
|
||||
|
||||
free_newinfo:
|
||||
xt_free_table_info(newinfo);
|
||||
-out:
|
||||
+ return ret;
|
||||
+out_unlock:
|
||||
+ xt_compat_flush_offsets(AF_INET);
|
||||
+ xt_compat_unlock(AF_INET);
|
||||
xt_entry_foreach(iter0, entry0, compatr->size) {
|
||||
if (j-- == 0)
|
||||
break;
|
||||
compat_release_entry(iter0);
|
||||
}
|
||||
return ret;
|
||||
-out_unlock:
|
||||
- xt_compat_flush_offsets(AF_INET);
|
||||
- xt_compat_unlock(AF_INET);
|
||||
- goto out;
|
||||
}
|
||||
|
||||
static int
|
||||
--- a/net/ipv6/netfilter/ip6_tables.c
|
||||
+++ b/net/ipv6/netfilter/ip6_tables.c
|
||||
@@ -1495,16 +1495,14 @@ check_compat_entry_size_and_hooks(struct
|
||||
struct xt_table_info *newinfo,
|
||||
unsigned int *size,
|
||||
const unsigned char *base,
|
||||
- const unsigned char *limit,
|
||||
- const unsigned int *hook_entries,
|
||||
- const unsigned int *underflows)
|
||||
+ const unsigned char *limit)
|
||||
{
|
||||
struct xt_entry_match *ematch;
|
||||
struct xt_entry_target *t;
|
||||
struct xt_target *target;
|
||||
unsigned int entry_offset;
|
||||
unsigned int j;
|
||||
- int ret, off, h;
|
||||
+ int ret, off;
|
||||
|
||||
duprintf("check_compat_entry_size_and_hooks %p\n", e);
|
||||
if ((unsigned long)e % __alignof__(struct compat_ip6t_entry) != 0 ||
|
||||
@@ -1556,17 +1554,6 @@ check_compat_entry_size_and_hooks(struct
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
- /* Check hooks & underflows */
|
||||
- for (h = 0; h < NF_INET_NUMHOOKS; h++) {
|
||||
- if ((unsigned char *)e - base == hook_entries[h])
|
||||
- newinfo->hook_entry[h] = hook_entries[h];
|
||||
- if ((unsigned char *)e - base == underflows[h])
|
||||
- newinfo->underflow[h] = underflows[h];
|
||||
- }
|
||||
-
|
||||
- /* Clear counters and comefrom */
|
||||
- memset(&e->counters, 0, sizeof(e->counters));
|
||||
- e->comefrom = 0;
|
||||
return 0;
|
||||
|
||||
out:
|
||||
@@ -1615,47 +1602,6 @@ compat_copy_entry_from_user(struct compa
|
||||
}
|
||||
}
|
||||
|
||||
-static int compat_check_entry(struct ip6t_entry *e, struct net *net,
|
||||
- const char *name)
|
||||
-{
|
||||
- unsigned int j;
|
||||
- int ret = 0;
|
||||
- struct xt_mtchk_param mtpar;
|
||||
- struct xt_entry_match *ematch;
|
||||
-
|
||||
- e->counters.pcnt = xt_percpu_counter_alloc();
|
||||
- if (IS_ERR_VALUE(e->counters.pcnt))
|
||||
- return -ENOMEM;
|
||||
- j = 0;
|
||||
- mtpar.net = net;
|
||||
- mtpar.table = name;
|
||||
- mtpar.entryinfo = &e->ipv6;
|
||||
- mtpar.hook_mask = e->comefrom;
|
||||
- mtpar.family = NFPROTO_IPV6;
|
||||
- xt_ematch_foreach(ematch, e) {
|
||||
- ret = check_match(ematch, &mtpar);
|
||||
- if (ret != 0)
|
||||
- goto cleanup_matches;
|
||||
- ++j;
|
||||
- }
|
||||
-
|
||||
- ret = check_target(e, net, name);
|
||||
- if (ret)
|
||||
- goto cleanup_matches;
|
||||
- return 0;
|
||||
-
|
||||
- cleanup_matches:
|
||||
- xt_ematch_foreach(ematch, e) {
|
||||
- if (j-- == 0)
|
||||
- break;
|
||||
- cleanup_match(ematch, net);
|
||||
- }
|
||||
-
|
||||
- xt_percpu_counter_free(e->counters.pcnt);
|
||||
-
|
||||
- return ret;
|
||||
-}
|
||||
-
|
||||
static int
|
||||
translate_compat_table(struct net *net,
|
||||
struct xt_table_info **pinfo,
|
||||
@@ -1666,7 +1612,7 @@ translate_compat_table(struct net *net,
|
||||
struct xt_table_info *newinfo, *info;
|
||||
void *pos, *entry0, *entry1;
|
||||
struct compat_ip6t_entry *iter0;
|
||||
- struct ip6t_entry *iter1;
|
||||
+ struct ip6t_replace repl;
|
||||
unsigned int size;
|
||||
int ret = 0;
|
||||
|
||||
@@ -1675,12 +1621,6 @@ translate_compat_table(struct net *net,
|
||||
size = compatr->size;
|
||||
info->number = compatr->num_entries;
|
||||
|
||||
- /* Init all hooks to impossible value. */
|
||||
- for (i = 0; i < NF_INET_NUMHOOKS; i++) {
|
||||
- info->hook_entry[i] = 0xFFFFFFFF;
|
||||
- info->underflow[i] = 0xFFFFFFFF;
|
||||
- }
|
||||
-
|
||||
duprintf("translate_compat_table: size %u\n", info->size);
|
||||
j = 0;
|
||||
xt_compat_lock(AF_INET6);
|
||||
@@ -1689,9 +1629,7 @@ translate_compat_table(struct net *net,
|
||||
xt_entry_foreach(iter0, entry0, compatr->size) {
|
||||
ret = check_compat_entry_size_and_hooks(iter0, info, &size,
|
||||
entry0,
|
||||
- entry0 + compatr->size,
|
||||
- compatr->hook_entry,
|
||||
- compatr->underflow);
|
||||
+ entry0 + compatr->size);
|
||||
if (ret != 0)
|
||||
goto out_unlock;
|
||||
++j;
|
||||
@@ -1704,23 +1642,6 @@ translate_compat_table(struct net *net,
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
- /* Check hooks all assigned */
|
||||
- for (i = 0; i < NF_INET_NUMHOOKS; i++) {
|
||||
- /* Only hooks which are valid */
|
||||
- if (!(compatr->valid_hooks & (1 << i)))
|
||||
- continue;
|
||||
- if (info->hook_entry[i] == 0xFFFFFFFF) {
|
||||
- duprintf("Invalid hook entry %u %u\n",
|
||||
- i, info->hook_entry[i]);
|
||||
- goto out_unlock;
|
||||
- }
|
||||
- if (info->underflow[i] == 0xFFFFFFFF) {
|
||||
- duprintf("Invalid underflow %u %u\n",
|
||||
- i, info->underflow[i]);
|
||||
- goto out_unlock;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
ret = -ENOMEM;
|
||||
newinfo = xt_alloc_table_info(size);
|
||||
if (!newinfo)
|
||||
@@ -1728,56 +1649,34 @@ translate_compat_table(struct net *net,
|
||||
|
||||
newinfo->number = compatr->num_entries;
|
||||
for (i = 0; i < NF_INET_NUMHOOKS; i++) {
|
||||
- newinfo->hook_entry[i] = info->hook_entry[i];
|
||||
- newinfo->underflow[i] = info->underflow[i];
|
||||
+ newinfo->hook_entry[i] = compatr->hook_entry[i];
|
||||
+ newinfo->underflow[i] = compatr->underflow[i];
|
||||
}
|
||||
entry1 = newinfo->entries;
|
||||
pos = entry1;
|
||||
+ size = compatr->size;
|
||||
xt_entry_foreach(iter0, entry0, compatr->size)
|
||||
compat_copy_entry_from_user(iter0, &pos, &size,
|
||||
newinfo, entry1);
|
||||
|
||||
+ /* all module references in entry0 are now gone. */
|
||||
xt_compat_flush_offsets(AF_INET6);
|
||||
xt_compat_unlock(AF_INET6);
|
||||
|
||||
- ret = -ELOOP;
|
||||
- if (!mark_source_chains(newinfo, compatr->valid_hooks, entry1))
|
||||
- goto free_newinfo;
|
||||
+ memcpy(&repl, compatr, sizeof(*compatr));
|
||||
|
||||
- i = 0;
|
||||
- xt_entry_foreach(iter1, entry1, newinfo->size) {
|
||||
- ret = compat_check_entry(iter1, net, compatr->name);
|
||||
- if (ret != 0)
|
||||
- break;
|
||||
- ++i;
|
||||
- if (strcmp(ip6t_get_target(iter1)->u.user.name,
|
||||
- XT_ERROR_TARGET) == 0)
|
||||
- ++newinfo->stacksize;
|
||||
- }
|
||||
- if (ret) {
|
||||
- /*
|
||||
- * The first i matches need cleanup_entry (calls ->destroy)
|
||||
- * because they had called ->check already. The other j-i
|
||||
- * entries need only release.
|
||||
- */
|
||||
- int skip = i;
|
||||
- j -= i;
|
||||
- xt_entry_foreach(iter0, entry0, newinfo->size) {
|
||||
- if (skip-- > 0)
|
||||
- continue;
|
||||
- if (j-- == 0)
|
||||
- break;
|
||||
- compat_release_entry(iter0);
|
||||
- }
|
||||
- xt_entry_foreach(iter1, entry1, newinfo->size) {
|
||||
- if (i-- == 0)
|
||||
- break;
|
||||
- cleanup_entry(iter1, net);
|
||||
- }
|
||||
- xt_free_table_info(newinfo);
|
||||
- return ret;
|
||||
+ for (i = 0; i < NF_INET_NUMHOOKS; i++) {
|
||||
+ repl.hook_entry[i] = newinfo->hook_entry[i];
|
||||
+ repl.underflow[i] = newinfo->underflow[i];
|
||||
}
|
||||
|
||||
+ repl.num_counters = 0;
|
||||
+ repl.counters = NULL;
|
||||
+ repl.size = newinfo->size;
|
||||
+ ret = translate_table(net, newinfo, entry1, &repl);
|
||||
+ if (ret)
|
||||
+ goto free_newinfo;
|
||||
+
|
||||
*pinfo = newinfo;
|
||||
*pentry0 = entry1;
|
||||
xt_free_table_info(info);
|
||||
@@ -1785,17 +1684,16 @@ translate_compat_table(struct net *net,
|
||||
|
||||
free_newinfo:
|
||||
xt_free_table_info(newinfo);
|
||||
-out:
|
||||
+ return ret;
|
||||
+out_unlock:
|
||||
+ xt_compat_flush_offsets(AF_INET6);
|
||||
+ xt_compat_unlock(AF_INET6);
|
||||
xt_entry_foreach(iter0, entry0, compatr->size) {
|
||||
if (j-- == 0)
|
||||
break;
|
||||
compat_release_entry(iter0);
|
||||
}
|
||||
return ret;
|
||||
-out_unlock:
|
||||
- xt_compat_flush_offsets(AF_INET6);
|
||||
- xt_compat_unlock(AF_INET6);
|
||||
- goto out;
|
||||
}
|
||||
|
||||
static int
|
||||
--- a/net/netfilter/x_tables.c
|
||||
+++ b/net/netfilter/x_tables.c
|
||||
@@ -533,6 +533,7 @@ void xt_compat_match_from_user(struct xt
|
||||
struct compat_xt_entry_match *cm = (struct compat_xt_entry_match *)m;
|
||||
int pad, off = xt_compat_match_offset(match);
|
||||
u_int16_t msize = cm->u.user.match_size;
|
||||
+ char name[sizeof(m->u.user.name)];
|
||||
|
||||
m = *dstptr;
|
||||
memcpy(m, cm, sizeof(*cm));
|
||||
@@ -546,6 +547,9 @@ void xt_compat_match_from_user(struct xt
|
||||
|
||||
msize += off;
|
||||
m->u.user.match_size = msize;
|
||||
+ strlcpy(name, match->name, sizeof(name));
|
||||
+ module_put(match->me);
|
||||
+ strncpy(m->u.user.name, name, sizeof(m->u.user.name));
|
||||
|
||||
*size += off;
|
||||
*dstptr += msize;
|
||||
@@ -763,6 +767,7 @@ void xt_compat_target_from_user(struct x
|
||||
struct compat_xt_entry_target *ct = (struct compat_xt_entry_target *)t;
|
||||
int pad, off = xt_compat_target_offset(target);
|
||||
u_int16_t tsize = ct->u.user.target_size;
|
||||
+ char name[sizeof(t->u.user.name)];
|
||||
|
||||
t = *dstptr;
|
||||
memcpy(t, ct, sizeof(*ct));
|
||||
@@ -776,6 +781,9 @@ void xt_compat_target_from_user(struct x
|
||||
|
||||
tsize += off;
|
||||
t->u.user.target_size = tsize;
|
||||
+ strlcpy(name, target->name, sizeof(name));
|
||||
+ module_put(target->me);
|
||||
+ strncpy(t->u.user.name, name, sizeof(t->u.user.name));
|
||||
|
||||
*size += off;
|
||||
*dstptr += tsize;
|
100
debian/patches/bugfix/all/netfilter-x_tables-don-t-move-to-non-existent-next-r.patch
vendored
Normal file
100
debian/patches/bugfix/all/netfilter-x_tables-don-t-move-to-non-existent-next-r.patch
vendored
Normal file
|
@ -0,0 +1,100 @@
|
|||
From: Florian Westphal <fw@strlen.de>
|
||||
Date: Fri, 1 Apr 2016 14:17:21 +0200
|
||||
Subject: netfilter: x_tables: don't move to non-existent next rule
|
||||
Origin: https://git.kernel.org/linus/f24e230d257af1ad7476c6e81a8dc3127a74204e
|
||||
|
||||
Ben Hawkes says:
|
||||
|
||||
In the mark_source_chains function (net/ipv4/netfilter/ip_tables.c) it
|
||||
is possible for a user-supplied ipt_entry structure to have a large
|
||||
next_offset field. This field is not bounds checked prior to writing a
|
||||
counter value at the supplied offset.
|
||||
|
||||
Base chains enforce absolute verdict.
|
||||
|
||||
User defined chains are supposed to end with an unconditional return,
|
||||
xtables userspace adds them automatically.
|
||||
|
||||
But if such return is missing we will move to non-existent next rule.
|
||||
|
||||
Reported-by: Ben Hawkes <hawkes@google.com>
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
---
|
||||
net/ipv4/netfilter/arp_tables.c | 8 +++++---
|
||||
net/ipv4/netfilter/ip_tables.c | 4 ++++
|
||||
net/ipv6/netfilter/ip6_tables.c | 4 ++++
|
||||
3 files changed, 13 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/net/ipv4/netfilter/arp_tables.c
|
||||
+++ b/net/ipv4/netfilter/arp_tables.c
|
||||
@@ -439,6 +439,8 @@ static int mark_source_chains(const stru
|
||||
size = e->next_offset;
|
||||
e = (struct arpt_entry *)
|
||||
(entry0 + pos + size);
|
||||
+ if (pos + size >= newinfo->size)
|
||||
+ return 0;
|
||||
e->counters.pcnt = pos;
|
||||
pos += size;
|
||||
} else {
|
||||
@@ -461,6 +463,8 @@ static int mark_source_chains(const stru
|
||||
} else {
|
||||
/* ... this is a fallthru */
|
||||
newpos = pos + e->next_offset;
|
||||
+ if (newpos >= newinfo->size)
|
||||
+ return 0;
|
||||
}
|
||||
e = (struct arpt_entry *)
|
||||
(entry0 + newpos);
|
||||
@@ -691,10 +695,8 @@ static int translate_table(struct xt_tab
|
||||
}
|
||||
}
|
||||
|
||||
- if (!mark_source_chains(newinfo, repl->valid_hooks, entry0)) {
|
||||
- duprintf("Looping hook\n");
|
||||
+ if (!mark_source_chains(newinfo, repl->valid_hooks, entry0))
|
||||
return -ELOOP;
|
||||
- }
|
||||
|
||||
/* Finally, each sanity check must pass */
|
||||
i = 0;
|
||||
--- a/net/ipv4/netfilter/ip_tables.c
|
||||
+++ b/net/ipv4/netfilter/ip_tables.c
|
||||
@@ -520,6 +520,8 @@ mark_source_chains(const struct xt_table
|
||||
size = e->next_offset;
|
||||
e = (struct ipt_entry *)
|
||||
(entry0 + pos + size);
|
||||
+ if (pos + size >= newinfo->size)
|
||||
+ return 0;
|
||||
e->counters.pcnt = pos;
|
||||
pos += size;
|
||||
} else {
|
||||
@@ -541,6 +543,8 @@ mark_source_chains(const struct xt_table
|
||||
} else {
|
||||
/* ... this is a fallthru */
|
||||
newpos = pos + e->next_offset;
|
||||
+ if (newpos >= newinfo->size)
|
||||
+ return 0;
|
||||
}
|
||||
e = (struct ipt_entry *)
|
||||
(entry0 + newpos);
|
||||
--- a/net/ipv6/netfilter/ip6_tables.c
|
||||
+++ b/net/ipv6/netfilter/ip6_tables.c
|
||||
@@ -532,6 +532,8 @@ mark_source_chains(const struct xt_table
|
||||
size = e->next_offset;
|
||||
e = (struct ip6t_entry *)
|
||||
(entry0 + pos + size);
|
||||
+ if (pos + size >= newinfo->size)
|
||||
+ return 0;
|
||||
e->counters.pcnt = pos;
|
||||
pos += size;
|
||||
} else {
|
||||
@@ -553,6 +555,8 @@ mark_source_chains(const struct xt_table
|
||||
} else {
|
||||
/* ... this is a fallthru */
|
||||
newpos = pos + e->next_offset;
|
||||
+ if (newpos >= newinfo->size)
|
||||
+ return 0;
|
||||
}
|
||||
e = (struct ip6t_entry *)
|
||||
(entry0 + newpos);
|
54
debian/patches/bugfix/all/netfilter-x_tables-don-t-reject-valid-target-size-on.patch
vendored
Normal file
54
debian/patches/bugfix/all/netfilter-x_tables-don-t-reject-valid-target-size-on.patch
vendored
Normal file
|
@ -0,0 +1,54 @@
|
|||
From: Florian Westphal <fw@strlen.de>
|
||||
Date: Wed, 1 Jun 2016 02:04:44 +0200
|
||||
Subject: netfilter: x_tables: don't reject valid target size on some
|
||||
architectures
|
||||
Origin: https://git.kernel.org/linus/7b7eba0f3515fca3296b8881d583f7c1042f5226
|
||||
|
||||
Quoting John Stultz:
|
||||
In updating a 32bit arm device from 4.6 to Linus' current HEAD, I
|
||||
noticed I was having some trouble with networking, and realized that
|
||||
/proc/net/ip_tables_names was suddenly empty.
|
||||
Digging through the registration process, it seems we're catching on the:
|
||||
|
||||
if (strcmp(t->u.user.name, XT_STANDARD_TARGET) == 0 &&
|
||||
target_offset + sizeof(struct xt_standard_target) != next_offset)
|
||||
return -EINVAL;
|
||||
|
||||
Where next_offset seems to be 4 bytes larger then the
|
||||
offset + standard_target struct size.
|
||||
|
||||
next_offset needs to be aligned via XT_ALIGN (so we can access all members
|
||||
of ip(6)t_entry struct).
|
||||
|
||||
This problem didn't show up on i686 as it only needs 4-byte alignment for
|
||||
u64, but iptables userspace on other 32bit arches does insert extra padding.
|
||||
|
||||
Reported-by: John Stultz <john.stultz@linaro.org>
|
||||
Tested-by: John Stultz <john.stultz@linaro.org>
|
||||
Fixes: 7ed2abddd20cf ("netfilter: x_tables: check standard target size too")
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
---
|
||||
net/netfilter/x_tables.c | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/net/netfilter/x_tables.c
|
||||
+++ b/net/netfilter/x_tables.c
|
||||
@@ -609,7 +609,7 @@ int xt_compat_check_entry_offsets(const
|
||||
return -EINVAL;
|
||||
|
||||
if (strcmp(t->u.user.name, XT_STANDARD_TARGET) == 0 &&
|
||||
- target_offset + sizeof(struct compat_xt_standard_target) != next_offset)
|
||||
+ COMPAT_XT_ALIGN(target_offset + sizeof(struct compat_xt_standard_target)) != next_offset)
|
||||
return -EINVAL;
|
||||
|
||||
/* compat_xt_entry match has less strict aligment requirements,
|
||||
@@ -691,7 +691,7 @@ int xt_check_entry_offsets(const void *b
|
||||
return -EINVAL;
|
||||
|
||||
if (strcmp(t->u.user.name, XT_STANDARD_TARGET) == 0 &&
|
||||
- target_offset + sizeof(struct xt_standard_target) != next_offset)
|
||||
+ XT_ALIGN(target_offset + sizeof(struct xt_standard_target)) != next_offset)
|
||||
return -EINVAL;
|
||||
|
||||
return xt_check_entry_match(elems, base + target_offset,
|
328
debian/patches/bugfix/all/netfilter-x_tables-introduce-and-use-xt_copy_counter.patch
vendored
Normal file
328
debian/patches/bugfix/all/netfilter-x_tables-introduce-and-use-xt_copy_counter.patch
vendored
Normal file
|
@ -0,0 +1,328 @@
|
|||
From: Florian Westphal <fw@strlen.de>
|
||||
Date: Fri, 1 Apr 2016 15:37:59 +0200
|
||||
Subject: netfilter: x_tables: introduce and use xt_copy_counters_from_user
|
||||
Origin: https://git.kernel.org/linus/d7591f0c41ce3e67600a982bab6989ef0f07b3ce
|
||||
|
||||
The three variants use same copy&pasted code, condense this into a
|
||||
helper and use that.
|
||||
|
||||
Make sure info.name is 0-terminated.
|
||||
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
---
|
||||
include/linux/netfilter/x_tables.h | 3 ++
|
||||
net/ipv4/netfilter/arp_tables.c | 48 +++----------------------
|
||||
net/ipv4/netfilter/ip_tables.c | 48 +++----------------------
|
||||
net/ipv6/netfilter/ip6_tables.c | 49 +++----------------------
|
||||
net/netfilter/x_tables.c | 74 ++++++++++++++++++++++++++++++++++++++
|
||||
5 files changed, 92 insertions(+), 130 deletions(-)
|
||||
|
||||
--- a/include/linux/netfilter/x_tables.h
|
||||
+++ b/include/linux/netfilter/x_tables.h
|
||||
@@ -251,6 +251,9 @@ int xt_check_match(struct xt_mtchk_param
|
||||
int xt_check_target(struct xt_tgchk_param *, unsigned int size, u_int8_t proto,
|
||||
bool inv_proto);
|
||||
|
||||
+void *xt_copy_counters_from_user(const void __user *user, unsigned int len,
|
||||
+ struct xt_counters_info *info, bool compat);
|
||||
+
|
||||
struct xt_table *xt_register_table(struct net *net,
|
||||
const struct xt_table *table,
|
||||
struct xt_table_info *bootstrap,
|
||||
--- a/net/ipv4/netfilter/arp_tables.c
|
||||
+++ b/net/ipv4/netfilter/arp_tables.c
|
||||
@@ -1131,55 +1131,17 @@ static int do_add_counters(struct net *n
|
||||
unsigned int i;
|
||||
struct xt_counters_info tmp;
|
||||
struct xt_counters *paddc;
|
||||
- unsigned int num_counters;
|
||||
- const char *name;
|
||||
- int size;
|
||||
- void *ptmp;
|
||||
struct xt_table *t;
|
||||
const struct xt_table_info *private;
|
||||
int ret = 0;
|
||||
struct arpt_entry *iter;
|
||||
unsigned int addend;
|
||||
-#ifdef CONFIG_COMPAT
|
||||
- struct compat_xt_counters_info compat_tmp;
|
||||
|
||||
- if (compat) {
|
||||
- ptmp = &compat_tmp;
|
||||
- size = sizeof(struct compat_xt_counters_info);
|
||||
- } else
|
||||
-#endif
|
||||
- {
|
||||
- ptmp = &tmp;
|
||||
- size = sizeof(struct xt_counters_info);
|
||||
- }
|
||||
-
|
||||
- if (copy_from_user(ptmp, user, size) != 0)
|
||||
- return -EFAULT;
|
||||
-
|
||||
-#ifdef CONFIG_COMPAT
|
||||
- if (compat) {
|
||||
- num_counters = compat_tmp.num_counters;
|
||||
- name = compat_tmp.name;
|
||||
- } else
|
||||
-#endif
|
||||
- {
|
||||
- num_counters = tmp.num_counters;
|
||||
- name = tmp.name;
|
||||
- }
|
||||
-
|
||||
- if (len != size + num_counters * sizeof(struct xt_counters))
|
||||
- return -EINVAL;
|
||||
-
|
||||
- paddc = vmalloc(len - size);
|
||||
- if (!paddc)
|
||||
- return -ENOMEM;
|
||||
-
|
||||
- if (copy_from_user(paddc, user + size, len - size) != 0) {
|
||||
- ret = -EFAULT;
|
||||
- goto free;
|
||||
- }
|
||||
+ paddc = xt_copy_counters_from_user(user, len, &tmp, compat);
|
||||
+ if (IS_ERR(paddc))
|
||||
+ return PTR_ERR(paddc);
|
||||
|
||||
- t = xt_find_table_lock(net, NFPROTO_ARP, name);
|
||||
+ t = xt_find_table_lock(net, NFPROTO_ARP, tmp.name);
|
||||
if (IS_ERR_OR_NULL(t)) {
|
||||
ret = t ? PTR_ERR(t) : -ENOENT;
|
||||
goto free;
|
||||
@@ -1187,7 +1149,7 @@ static int do_add_counters(struct net *n
|
||||
|
||||
local_bh_disable();
|
||||
private = t->private;
|
||||
- if (private->number != num_counters) {
|
||||
+ if (private->number != tmp.num_counters) {
|
||||
ret = -EINVAL;
|
||||
goto unlock_up_free;
|
||||
}
|
||||
--- a/net/ipv4/netfilter/ip_tables.c
|
||||
+++ b/net/ipv4/netfilter/ip_tables.c
|
||||
@@ -1314,55 +1314,17 @@ do_add_counters(struct net *net, const v
|
||||
unsigned int i;
|
||||
struct xt_counters_info tmp;
|
||||
struct xt_counters *paddc;
|
||||
- unsigned int num_counters;
|
||||
- const char *name;
|
||||
- int size;
|
||||
- void *ptmp;
|
||||
struct xt_table *t;
|
||||
const struct xt_table_info *private;
|
||||
int ret = 0;
|
||||
struct ipt_entry *iter;
|
||||
unsigned int addend;
|
||||
-#ifdef CONFIG_COMPAT
|
||||
- struct compat_xt_counters_info compat_tmp;
|
||||
|
||||
- if (compat) {
|
||||
- ptmp = &compat_tmp;
|
||||
- size = sizeof(struct compat_xt_counters_info);
|
||||
- } else
|
||||
-#endif
|
||||
- {
|
||||
- ptmp = &tmp;
|
||||
- size = sizeof(struct xt_counters_info);
|
||||
- }
|
||||
-
|
||||
- if (copy_from_user(ptmp, user, size) != 0)
|
||||
- return -EFAULT;
|
||||
-
|
||||
-#ifdef CONFIG_COMPAT
|
||||
- if (compat) {
|
||||
- num_counters = compat_tmp.num_counters;
|
||||
- name = compat_tmp.name;
|
||||
- } else
|
||||
-#endif
|
||||
- {
|
||||
- num_counters = tmp.num_counters;
|
||||
- name = tmp.name;
|
||||
- }
|
||||
-
|
||||
- if (len != size + num_counters * sizeof(struct xt_counters))
|
||||
- return -EINVAL;
|
||||
-
|
||||
- paddc = vmalloc(len - size);
|
||||
- if (!paddc)
|
||||
- return -ENOMEM;
|
||||
-
|
||||
- if (copy_from_user(paddc, user + size, len - size) != 0) {
|
||||
- ret = -EFAULT;
|
||||
- goto free;
|
||||
- }
|
||||
+ paddc = xt_copy_counters_from_user(user, len, &tmp, compat);
|
||||
+ if (IS_ERR(paddc))
|
||||
+ return PTR_ERR(paddc);
|
||||
|
||||
- t = xt_find_table_lock(net, AF_INET, name);
|
||||
+ t = xt_find_table_lock(net, AF_INET, tmp.name);
|
||||
if (IS_ERR_OR_NULL(t)) {
|
||||
ret = t ? PTR_ERR(t) : -ENOENT;
|
||||
goto free;
|
||||
@@ -1370,7 +1332,7 @@ do_add_counters(struct net *net, const v
|
||||
|
||||
local_bh_disable();
|
||||
private = t->private;
|
||||
- if (private->number != num_counters) {
|
||||
+ if (private->number != tmp.num_counters) {
|
||||
ret = -EINVAL;
|
||||
goto unlock_up_free;
|
||||
}
|
||||
--- a/net/ipv6/netfilter/ip6_tables.c
|
||||
+++ b/net/ipv6/netfilter/ip6_tables.c
|
||||
@@ -1326,55 +1326,16 @@ do_add_counters(struct net *net, const v
|
||||
unsigned int i;
|
||||
struct xt_counters_info tmp;
|
||||
struct xt_counters *paddc;
|
||||
- unsigned int num_counters;
|
||||
- char *name;
|
||||
- int size;
|
||||
- void *ptmp;
|
||||
struct xt_table *t;
|
||||
const struct xt_table_info *private;
|
||||
int ret = 0;
|
||||
struct ip6t_entry *iter;
|
||||
unsigned int addend;
|
||||
-#ifdef CONFIG_COMPAT
|
||||
- struct compat_xt_counters_info compat_tmp;
|
||||
|
||||
- if (compat) {
|
||||
- ptmp = &compat_tmp;
|
||||
- size = sizeof(struct compat_xt_counters_info);
|
||||
- } else
|
||||
-#endif
|
||||
- {
|
||||
- ptmp = &tmp;
|
||||
- size = sizeof(struct xt_counters_info);
|
||||
- }
|
||||
-
|
||||
- if (copy_from_user(ptmp, user, size) != 0)
|
||||
- return -EFAULT;
|
||||
-
|
||||
-#ifdef CONFIG_COMPAT
|
||||
- if (compat) {
|
||||
- num_counters = compat_tmp.num_counters;
|
||||
- name = compat_tmp.name;
|
||||
- } else
|
||||
-#endif
|
||||
- {
|
||||
- num_counters = tmp.num_counters;
|
||||
- name = tmp.name;
|
||||
- }
|
||||
-
|
||||
- if (len != size + num_counters * sizeof(struct xt_counters))
|
||||
- return -EINVAL;
|
||||
-
|
||||
- paddc = vmalloc(len - size);
|
||||
- if (!paddc)
|
||||
- return -ENOMEM;
|
||||
-
|
||||
- if (copy_from_user(paddc, user + size, len - size) != 0) {
|
||||
- ret = -EFAULT;
|
||||
- goto free;
|
||||
- }
|
||||
-
|
||||
- t = xt_find_table_lock(net, AF_INET6, name);
|
||||
+ paddc = xt_copy_counters_from_user(user, len, &tmp, compat);
|
||||
+ if (IS_ERR(paddc))
|
||||
+ return PTR_ERR(paddc);
|
||||
+ t = xt_find_table_lock(net, AF_INET6, tmp.name);
|
||||
if (IS_ERR_OR_NULL(t)) {
|
||||
ret = t ? PTR_ERR(t) : -ENOENT;
|
||||
goto free;
|
||||
@@ -1382,7 +1343,7 @@ do_add_counters(struct net *net, const v
|
||||
|
||||
local_bh_disable();
|
||||
private = t->private;
|
||||
- if (private->number != num_counters) {
|
||||
+ if (private->number != tmp.num_counters) {
|
||||
ret = -EINVAL;
|
||||
goto unlock_up_free;
|
||||
}
|
||||
--- a/net/netfilter/x_tables.c
|
||||
+++ b/net/netfilter/x_tables.c
|
||||
@@ -752,6 +752,80 @@ int xt_check_target(struct xt_tgchk_para
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xt_check_target);
|
||||
|
||||
+/**
|
||||
+ * xt_copy_counters_from_user - copy counters and metadata from userspace
|
||||
+ *
|
||||
+ * @user: src pointer to userspace memory
|
||||
+ * @len: alleged size of userspace memory
|
||||
+ * @info: where to store the xt_counters_info metadata
|
||||
+ * @compat: true if we setsockopt call is done by 32bit task on 64bit kernel
|
||||
+ *
|
||||
+ * Copies counter meta data from @user and stores it in @info.
|
||||
+ *
|
||||
+ * vmallocs memory to hold the counters, then copies the counter data
|
||||
+ * from @user to the new memory and returns a pointer to it.
|
||||
+ *
|
||||
+ * If @compat is true, @info gets converted automatically to the 64bit
|
||||
+ * representation.
|
||||
+ *
|
||||
+ * The metadata associated with the counters is stored in @info.
|
||||
+ *
|
||||
+ * Return: returns pointer that caller has to test via IS_ERR().
|
||||
+ * If IS_ERR is false, caller has to vfree the pointer.
|
||||
+ */
|
||||
+void *xt_copy_counters_from_user(const void __user *user, unsigned int len,
|
||||
+ struct xt_counters_info *info, bool compat)
|
||||
+{
|
||||
+ void *mem;
|
||||
+ u64 size;
|
||||
+
|
||||
+#ifdef CONFIG_COMPAT
|
||||
+ if (compat) {
|
||||
+ /* structures only differ in size due to alignment */
|
||||
+ struct compat_xt_counters_info compat_tmp;
|
||||
+
|
||||
+ if (len <= sizeof(compat_tmp))
|
||||
+ return ERR_PTR(-EINVAL);
|
||||
+
|
||||
+ len -= sizeof(compat_tmp);
|
||||
+ if (copy_from_user(&compat_tmp, user, sizeof(compat_tmp)) != 0)
|
||||
+ return ERR_PTR(-EFAULT);
|
||||
+
|
||||
+ strlcpy(info->name, compat_tmp.name, sizeof(info->name));
|
||||
+ info->num_counters = compat_tmp.num_counters;
|
||||
+ user += sizeof(compat_tmp);
|
||||
+ } else
|
||||
+#endif
|
||||
+ {
|
||||
+ if (len <= sizeof(*info))
|
||||
+ return ERR_PTR(-EINVAL);
|
||||
+
|
||||
+ len -= sizeof(*info);
|
||||
+ if (copy_from_user(info, user, sizeof(*info)) != 0)
|
||||
+ return ERR_PTR(-EFAULT);
|
||||
+
|
||||
+ info->name[sizeof(info->name) - 1] = '\0';
|
||||
+ user += sizeof(*info);
|
||||
+ }
|
||||
+
|
||||
+ size = sizeof(struct xt_counters);
|
||||
+ size *= info->num_counters;
|
||||
+
|
||||
+ if (size != (u64)len)
|
||||
+ return ERR_PTR(-EINVAL);
|
||||
+
|
||||
+ mem = vmalloc(len);
|
||||
+ if (!mem)
|
||||
+ return ERR_PTR(-ENOMEM);
|
||||
+
|
||||
+ if (copy_from_user(mem, user, len) == 0)
|
||||
+ return mem;
|
||||
+
|
||||
+ vfree(mem);
|
||||
+ return ERR_PTR(-EFAULT);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(xt_copy_counters_from_user);
|
||||
+
|
||||
#ifdef CONFIG_COMPAT
|
||||
int xt_compat_target_offset(const struct xt_target *target)
|
||||
{
|
|
@ -0,0 +1,149 @@
|
|||
From: Florian Westphal <fw@strlen.de>
|
||||
Date: Fri, 1 Apr 2016 14:17:24 +0200
|
||||
Subject: netfilter: x_tables: kill check_entry helper
|
||||
Origin: https://git.kernel.org/linus/aa412ba225dd3bc36d404c28cdc3d674850d80d0
|
||||
|
||||
Once we add more sanity testing to xt_check_entry_offsets it
|
||||
becomes relvant if we're expecting a 32bit 'config_compat' blob
|
||||
or a normal one.
|
||||
|
||||
Since we already have a lot of similar-named functions (check_entry,
|
||||
compat_check_entry, find_and_check_entry, etc.) and the current
|
||||
incarnation is short just fold its contents into the callers.
|
||||
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
---
|
||||
net/ipv4/netfilter/arp_tables.c | 19 ++++++++-----------
|
||||
net/ipv4/netfilter/ip_tables.c | 20 ++++++++------------
|
||||
net/ipv6/netfilter/ip6_tables.c | 20 ++++++++------------
|
||||
3 files changed, 24 insertions(+), 35 deletions(-)
|
||||
|
||||
--- a/net/ipv4/netfilter/arp_tables.c
|
||||
+++ b/net/ipv4/netfilter/arp_tables.c
|
||||
@@ -494,14 +494,6 @@ next:
|
||||
return 1;
|
||||
}
|
||||
|
||||
-static inline int check_entry(const struct arpt_entry *e)
|
||||
-{
|
||||
- if (!arp_checkentry(&e->arp))
|
||||
- return -EINVAL;
|
||||
-
|
||||
- return xt_check_entry_offsets(e, e->target_offset, e->next_offset);
|
||||
-}
|
||||
-
|
||||
static inline int check_target(struct arpt_entry *e, const char *name)
|
||||
{
|
||||
struct xt_entry_target *t = arpt_get_target(e);
|
||||
@@ -597,7 +589,10 @@ static inline int check_entry_size_and_h
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
- err = check_entry(e);
|
||||
+ if (!arp_checkentry(&e->arp))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ err = xt_check_entry_offsets(e, e->target_offset, e->next_offset);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -1256,8 +1251,10 @@ check_compat_entry_size_and_hooks(struct
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
- /* For purposes of check_entry casting the compat entry is fine */
|
||||
- ret = check_entry((struct arpt_entry *)e);
|
||||
+ if (!arp_checkentry(&e->arp))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ ret = xt_check_entry_offsets(e, e->target_offset, e->next_offset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
--- a/net/ipv4/netfilter/ip_tables.c
|
||||
+++ b/net/ipv4/netfilter/ip_tables.c
|
||||
@@ -588,15 +588,6 @@ static void cleanup_match(struct xt_entr
|
||||
}
|
||||
|
||||
static int
|
||||
-check_entry(const struct ipt_entry *e)
|
||||
-{
|
||||
- if (!ip_checkentry(&e->ip))
|
||||
- return -EINVAL;
|
||||
-
|
||||
- return xt_check_entry_offsets(e, e->target_offset, e->next_offset);
|
||||
-}
|
||||
-
|
||||
-static int
|
||||
check_match(struct xt_entry_match *m, struct xt_mtchk_param *par)
|
||||
{
|
||||
const struct ipt_ip *ip = par->entryinfo;
|
||||
@@ -760,7 +751,10 @@ check_entry_size_and_hooks(struct ipt_en
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
- err = check_entry(e);
|
||||
+ if (!ip_checkentry(&e->ip))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ err = xt_check_entry_offsets(e, e->target_offset, e->next_offset);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -1516,8 +1510,10 @@ check_compat_entry_size_and_hooks(struct
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
- /* For purposes of check_entry casting the compat entry is fine */
|
||||
- ret = check_entry((struct ipt_entry *)e);
|
||||
+ if (!ip_checkentry(&e->ip))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ ret = xt_check_entry_offsets(e, e->target_offset, e->next_offset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
--- a/net/ipv6/netfilter/ip6_tables.c
|
||||
+++ b/net/ipv6/netfilter/ip6_tables.c
|
||||
@@ -599,15 +599,6 @@ static void cleanup_match(struct xt_entr
|
||||
module_put(par.match->me);
|
||||
}
|
||||
|
||||
-static int
|
||||
-check_entry(const struct ip6t_entry *e)
|
||||
-{
|
||||
- if (!ip6_checkentry(&e->ipv6))
|
||||
- return -EINVAL;
|
||||
-
|
||||
- return xt_check_entry_offsets(e, e->target_offset, e->next_offset);
|
||||
-}
|
||||
-
|
||||
static int check_match(struct xt_entry_match *m, struct xt_mtchk_param *par)
|
||||
{
|
||||
const struct ip6t_ip6 *ipv6 = par->entryinfo;
|
||||
@@ -772,7 +763,10 @@ check_entry_size_and_hooks(struct ip6t_e
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
- err = check_entry(e);
|
||||
+ if (!ip6_checkentry(&e->ipv6))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ err = xt_check_entry_offsets(e, e->target_offset, e->next_offset);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -1528,8 +1522,10 @@ check_compat_entry_size_and_hooks(struct
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
- /* For purposes of check_entry casting the compat entry is fine */
|
||||
- ret = check_entry((struct ip6t_entry *)e);
|
||||
+ if (!ip6_checkentry(&e->ipv6))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ ret = xt_check_entry_offsets(e, e->target_offset, e->next_offset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
137
debian/patches/bugfix/all/netfilter-x_tables-validate-all-offsets-and-sizes-in.patch
vendored
Normal file
137
debian/patches/bugfix/all/netfilter-x_tables-validate-all-offsets-and-sizes-in.patch
vendored
Normal file
|
@ -0,0 +1,137 @@
|
|||
From: Florian Westphal <fw@strlen.de>
|
||||
Date: Fri, 1 Apr 2016 14:17:29 +0200
|
||||
Subject: netfilter: x_tables: validate all offsets and sizes in a rule
|
||||
Origin: https://git.kernel.org/linus/13631bfc604161a9d69cd68991dff8603edd66f9
|
||||
|
||||
Validate that all matches (if any) add up to the beginning of
|
||||
the target and that each match covers at least the base structure size.
|
||||
|
||||
The compat path should be able to safely re-use the function
|
||||
as the structures only differ in alignment; added a
|
||||
BUILD_BUG_ON just in case we have an arch that adds padding as well.
|
||||
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
---
|
||||
net/netfilter/x_tables.c | 81 +++++++++++++++++++++++++++++++++++++++++++++---
|
||||
1 file changed, 76 insertions(+), 5 deletions(-)
|
||||
|
||||
--- a/net/netfilter/x_tables.c
|
||||
+++ b/net/netfilter/x_tables.c
|
||||
@@ -416,6 +416,47 @@ int xt_check_match(struct xt_mtchk_param
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xt_check_match);
|
||||
|
||||
+/** xt_check_entry_match - check that matches end before start of target
|
||||
+ *
|
||||
+ * @match: beginning of xt_entry_match
|
||||
+ * @target: beginning of this rules target (alleged end of matches)
|
||||
+ * @alignment: alignment requirement of match structures
|
||||
+ *
|
||||
+ * Validates that all matches add up to the beginning of the target,
|
||||
+ * and that each match covers at least the base structure size.
|
||||
+ *
|
||||
+ * Return: 0 on success, negative errno on failure.
|
||||
+ */
|
||||
+static int xt_check_entry_match(const char *match, const char *target,
|
||||
+ const size_t alignment)
|
||||
+{
|
||||
+ const struct xt_entry_match *pos;
|
||||
+ int length = target - match;
|
||||
+
|
||||
+ if (length == 0) /* no matches */
|
||||
+ return 0;
|
||||
+
|
||||
+ pos = (struct xt_entry_match *)match;
|
||||
+ do {
|
||||
+ if ((unsigned long)pos % alignment)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (length < (int)sizeof(struct xt_entry_match))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (pos->u.match_size < sizeof(struct xt_entry_match))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (pos->u.match_size > length)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ length -= pos->u.match_size;
|
||||
+ pos = ((void *)((char *)(pos) + (pos)->u.match_size));
|
||||
+ } while (length > 0);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
#ifdef CONFIG_COMPAT
|
||||
int xt_compat_add_offset(u_int8_t af, unsigned int offset, int delta)
|
||||
{
|
||||
@@ -571,7 +612,14 @@ int xt_compat_check_entry_offsets(const
|
||||
target_offset + sizeof(struct compat_xt_standard_target) != next_offset)
|
||||
return -EINVAL;
|
||||
|
||||
- return 0;
|
||||
+ /* compat_xt_entry match has less strict aligment requirements,
|
||||
+ * otherwise they are identical. In case of padding differences
|
||||
+ * we need to add compat version of xt_check_entry_match.
|
||||
+ */
|
||||
+ BUILD_BUG_ON(sizeof(struct compat_xt_entry_match) != sizeof(struct xt_entry_match));
|
||||
+
|
||||
+ return xt_check_entry_match(elems, base + target_offset,
|
||||
+ __alignof__(struct compat_xt_entry_match));
|
||||
}
|
||||
EXPORT_SYMBOL(xt_compat_check_entry_offsets);
|
||||
#endif /* CONFIG_COMPAT */
|
||||
@@ -584,17 +632,39 @@ EXPORT_SYMBOL(xt_compat_check_entry_offs
|
||||
* @target_offset: the arp/ip/ip6_t->target_offset
|
||||
* @next_offset: the arp/ip/ip6_t->next_offset
|
||||
*
|
||||
- * validates that target_offset and next_offset are sane.
|
||||
- * Also see xt_compat_check_entry_offsets for CONFIG_COMPAT version.
|
||||
+ * validates that target_offset and next_offset are sane and that all
|
||||
+ * match sizes (if any) align with the target offset.
|
||||
*
|
||||
* This function does not validate the targets or matches themselves, it
|
||||
- * only tests that all the offsets and sizes are correct.
|
||||
+ * only tests that all the offsets and sizes are correct, that all
|
||||
+ * match structures are aligned, and that the last structure ends where
|
||||
+ * the target structure begins.
|
||||
+ *
|
||||
+ * Also see xt_compat_check_entry_offsets for CONFIG_COMPAT version.
|
||||
*
|
||||
* The arp/ip/ip6t_entry structure @base must have passed following tests:
|
||||
* - it must point to a valid memory location
|
||||
* - base to base + next_offset must be accessible, i.e. not exceed allocated
|
||||
* length.
|
||||
*
|
||||
+ * A well-formed entry looks like this:
|
||||
+ *
|
||||
+ * ip(6)t_entry match [mtdata] match [mtdata] target [tgdata] ip(6)t_entry
|
||||
+ * e->elems[]-----' | |
|
||||
+ * matchsize | |
|
||||
+ * matchsize | |
|
||||
+ * | |
|
||||
+ * target_offset---------------------------------' |
|
||||
+ * next_offset---------------------------------------------------'
|
||||
+ *
|
||||
+ * elems[]: flexible array member at end of ip(6)/arpt_entry struct.
|
||||
+ * This is where matches (if any) and the target reside.
|
||||
+ * target_offset: beginning of target.
|
||||
+ * next_offset: start of the next rule; also: size of this rule.
|
||||
+ * Since targets have a minimum size, target_offset + minlen <= next_offset.
|
||||
+ *
|
||||
+ * Every match stores its size, sum of sizes must not exceed target_offset.
|
||||
+ *
|
||||
* Return: 0 on success, negative errno on failure.
|
||||
*/
|
||||
int xt_check_entry_offsets(const void *base,
|
||||
@@ -624,7 +694,8 @@ int xt_check_entry_offsets(const void *b
|
||||
target_offset + sizeof(struct xt_standard_target) != next_offset)
|
||||
return -EINVAL;
|
||||
|
||||
- return 0;
|
||||
+ return xt_check_entry_match(elems, base + target_offset,
|
||||
+ __alignof__(struct xt_entry_match));
|
||||
}
|
||||
EXPORT_SYMBOL(xt_check_entry_offsets);
|
||||
|
127
debian/patches/bugfix/all/netfilter-x_tables-validate-targets-of-jumps.patch
vendored
Normal file
127
debian/patches/bugfix/all/netfilter-x_tables-validate-targets-of-jumps.patch
vendored
Normal file
|
@ -0,0 +1,127 @@
|
|||
From: Florian Westphal <fw@strlen.de>
|
||||
Date: Fri, 1 Apr 2016 14:17:22 +0200
|
||||
Subject: netfilter: x_tables: validate targets of jumps
|
||||
Origin: https://git.kernel.org/linus/36472341017529e2b12573093cc0f68719300997
|
||||
|
||||
When we see a jump also check that the offset gets us to beginning of
|
||||
a rule (an ipt_entry).
|
||||
|
||||
The extra overhead is negible, even with absurd cases.
|
||||
|
||||
300k custom rules, 300k jumps to 'next' user chain:
|
||||
[ plus one jump from INPUT to first userchain ]:
|
||||
|
||||
Before:
|
||||
real 0m24.874s
|
||||
user 0m7.532s
|
||||
sys 0m16.076s
|
||||
|
||||
After:
|
||||
real 0m27.464s
|
||||
user 0m7.436s
|
||||
sys 0m18.840s
|
||||
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
---
|
||||
net/ipv4/netfilter/arp_tables.c | 16 ++++++++++++++++
|
||||
net/ipv4/netfilter/ip_tables.c | 16 ++++++++++++++++
|
||||
net/ipv6/netfilter/ip6_tables.c | 16 ++++++++++++++++
|
||||
3 files changed, 48 insertions(+)
|
||||
|
||||
--- a/net/ipv4/netfilter/arp_tables.c
|
||||
+++ b/net/ipv4/netfilter/arp_tables.c
|
||||
@@ -367,6 +367,18 @@ static inline bool unconditional(const s
|
||||
memcmp(&e->arp, &uncond, sizeof(uncond)) == 0;
|
||||
}
|
||||
|
||||
+static bool find_jump_target(const struct xt_table_info *t,
|
||||
+ const struct arpt_entry *target)
|
||||
+{
|
||||
+ struct arpt_entry *iter;
|
||||
+
|
||||
+ xt_entry_foreach(iter, t->entries, t->size) {
|
||||
+ if (iter == target)
|
||||
+ return true;
|
||||
+ }
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
/* Figures out from what hook each rule can be called: returns 0 if
|
||||
* there are loops. Puts hook bitmask in comefrom.
|
||||
*/
|
||||
@@ -460,6 +472,10 @@ static int mark_source_chains(const stru
|
||||
/* This a jump; chase it. */
|
||||
duprintf("Jump rule %u -> %u\n",
|
||||
pos, newpos);
|
||||
+ e = (struct arpt_entry *)
|
||||
+ (entry0 + newpos);
|
||||
+ if (!find_jump_target(newinfo, e))
|
||||
+ return 0;
|
||||
} else {
|
||||
/* ... this is a fallthru */
|
||||
newpos = pos + e->next_offset;
|
||||
--- a/net/ipv4/netfilter/ip_tables.c
|
||||
+++ b/net/ipv4/netfilter/ip_tables.c
|
||||
@@ -443,6 +443,18 @@ ipt_do_table(struct sk_buff *skb,
|
||||
#endif
|
||||
}
|
||||
|
||||
+static bool find_jump_target(const struct xt_table_info *t,
|
||||
+ const struct ipt_entry *target)
|
||||
+{
|
||||
+ struct ipt_entry *iter;
|
||||
+
|
||||
+ xt_entry_foreach(iter, t->entries, t->size) {
|
||||
+ if (iter == target)
|
||||
+ return true;
|
||||
+ }
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
/* Figures out from what hook each rule can be called: returns 0 if
|
||||
there are loops. Puts hook bitmask in comefrom. */
|
||||
static int
|
||||
@@ -540,6 +552,10 @@ mark_source_chains(const struct xt_table
|
||||
/* This a jump; chase it. */
|
||||
duprintf("Jump rule %u -> %u\n",
|
||||
pos, newpos);
|
||||
+ e = (struct ipt_entry *)
|
||||
+ (entry0 + newpos);
|
||||
+ if (!find_jump_target(newinfo, e))
|
||||
+ return 0;
|
||||
} else {
|
||||
/* ... this is a fallthru */
|
||||
newpos = pos + e->next_offset;
|
||||
--- a/net/ipv6/netfilter/ip6_tables.c
|
||||
+++ b/net/ipv6/netfilter/ip6_tables.c
|
||||
@@ -455,6 +455,18 @@ ip6t_do_table(struct sk_buff *skb,
|
||||
#endif
|
||||
}
|
||||
|
||||
+static bool find_jump_target(const struct xt_table_info *t,
|
||||
+ const struct ip6t_entry *target)
|
||||
+{
|
||||
+ struct ip6t_entry *iter;
|
||||
+
|
||||
+ xt_entry_foreach(iter, t->entries, t->size) {
|
||||
+ if (iter == target)
|
||||
+ return true;
|
||||
+ }
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
/* Figures out from what hook each rule can be called: returns 0 if
|
||||
there are loops. Puts hook bitmask in comefrom. */
|
||||
static int
|
||||
@@ -552,6 +564,10 @@ mark_source_chains(const struct xt_table
|
||||
/* This a jump; chase it. */
|
||||
duprintf("Jump rule %u -> %u\n",
|
||||
pos, newpos);
|
||||
+ e = (struct ip6t_entry *)
|
||||
+ (entry0 + newpos);
|
||||
+ if (!find_jump_target(newinfo, e))
|
||||
+ return 0;
|
||||
} else {
|
||||
/* ... this is a fallthru */
|
||||
newpos = pos + e->next_offset;
|
234
debian/patches/bugfix/all/netfilter-x_tables-xt_compat_match_from_user-doesn-t.patch
vendored
Normal file
234
debian/patches/bugfix/all/netfilter-x_tables-xt_compat_match_from_user-doesn-t.patch
vendored
Normal file
|
@ -0,0 +1,234 @@
|
|||
From: Florian Westphal <fw@strlen.de>
|
||||
Date: Fri, 1 Apr 2016 14:17:33 +0200
|
||||
Subject: netfilter: x_tables: xt_compat_match_from_user doesn't need a retval
|
||||
Origin: https://git.kernel.org/linus/0188346f21e6546498c2a0f84888797ad4063fc5
|
||||
|
||||
Always returned 0.
|
||||
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
---
|
||||
include/linux/netfilter/x_tables.h | 2 +-
|
||||
net/ipv4/netfilter/arp_tables.c | 17 +++++------------
|
||||
net/ipv4/netfilter/ip_tables.c | 26 +++++++++-----------------
|
||||
net/ipv6/netfilter/ip6_tables.c | 27 +++++++++------------------
|
||||
net/netfilter/x_tables.c | 5 ++---
|
||||
5 files changed, 26 insertions(+), 51 deletions(-)
|
||||
|
||||
--- a/include/linux/netfilter/x_tables.h
|
||||
+++ b/include/linux/netfilter/x_tables.h
|
||||
@@ -484,7 +484,7 @@ void xt_compat_init_offsets(u_int8_t af,
|
||||
int xt_compat_calc_jump(u_int8_t af, unsigned int offset);
|
||||
|
||||
int xt_compat_match_offset(const struct xt_match *match);
|
||||
-int xt_compat_match_from_user(struct xt_entry_match *m, void **dstptr,
|
||||
+void xt_compat_match_from_user(struct xt_entry_match *m, void **dstptr,
|
||||
unsigned int *size);
|
||||
int xt_compat_match_to_user(const struct xt_entry_match *m,
|
||||
void __user **dstptr, unsigned int *size);
|
||||
--- a/net/ipv4/netfilter/arp_tables.c
|
||||
+++ b/net/ipv4/netfilter/arp_tables.c
|
||||
@@ -1310,7 +1310,7 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
-static int
|
||||
+static void
|
||||
compat_copy_entry_from_user(struct compat_arpt_entry *e, void **dstptr,
|
||||
unsigned int *size,
|
||||
struct xt_table_info *newinfo, unsigned char *base)
|
||||
@@ -1319,9 +1319,8 @@ compat_copy_entry_from_user(struct compa
|
||||
struct xt_target *target;
|
||||
struct arpt_entry *de;
|
||||
unsigned int origsize;
|
||||
- int ret, h;
|
||||
+ int h;
|
||||
|
||||
- ret = 0;
|
||||
origsize = *size;
|
||||
de = (struct arpt_entry *)*dstptr;
|
||||
memcpy(de, e, sizeof(struct arpt_entry));
|
||||
@@ -1342,7 +1341,6 @@ compat_copy_entry_from_user(struct compa
|
||||
if ((unsigned char *)de - base < newinfo->underflow[h])
|
||||
newinfo->underflow[h] -= origsize - *size;
|
||||
}
|
||||
- return ret;
|
||||
}
|
||||
|
||||
static int translate_compat_table(struct xt_table_info **pinfo,
|
||||
@@ -1421,16 +1419,11 @@ static int translate_compat_table(struct
|
||||
entry1 = newinfo->entries;
|
||||
pos = entry1;
|
||||
size = compatr->size;
|
||||
- xt_entry_foreach(iter0, entry0, compatr->size) {
|
||||
- ret = compat_copy_entry_from_user(iter0, &pos, &size,
|
||||
- newinfo, entry1);
|
||||
- if (ret != 0)
|
||||
- break;
|
||||
- }
|
||||
+ xt_entry_foreach(iter0, entry0, compatr->size)
|
||||
+ compat_copy_entry_from_user(iter0, &pos, &size,
|
||||
+ newinfo, entry1);
|
||||
xt_compat_flush_offsets(NFPROTO_ARP);
|
||||
xt_compat_unlock(NFPROTO_ARP);
|
||||
- if (ret)
|
||||
- goto free_newinfo;
|
||||
|
||||
ret = -ELOOP;
|
||||
if (!mark_source_chains(newinfo, compatr->valid_hooks, entry1))
|
||||
--- a/net/ipv4/netfilter/ip_tables.c
|
||||
+++ b/net/ipv4/netfilter/ip_tables.c
|
||||
@@ -1568,7 +1568,7 @@ release_matches:
|
||||
return ret;
|
||||
}
|
||||
|
||||
-static int
|
||||
+static void
|
||||
compat_copy_entry_from_user(struct compat_ipt_entry *e, void **dstptr,
|
||||
unsigned int *size,
|
||||
struct xt_table_info *newinfo, unsigned char *base)
|
||||
@@ -1577,10 +1577,9 @@ compat_copy_entry_from_user(struct compa
|
||||
struct xt_target *target;
|
||||
struct ipt_entry *de;
|
||||
unsigned int origsize;
|
||||
- int ret, h;
|
||||
+ int h;
|
||||
struct xt_entry_match *ematch;
|
||||
|
||||
- ret = 0;
|
||||
origsize = *size;
|
||||
de = (struct ipt_entry *)*dstptr;
|
||||
memcpy(de, e, sizeof(struct ipt_entry));
|
||||
@@ -1589,11 +1588,9 @@ compat_copy_entry_from_user(struct compa
|
||||
*dstptr += sizeof(struct ipt_entry);
|
||||
*size += sizeof(struct ipt_entry) - sizeof(struct compat_ipt_entry);
|
||||
|
||||
- xt_ematch_foreach(ematch, e) {
|
||||
- ret = xt_compat_match_from_user(ematch, dstptr, size);
|
||||
- if (ret != 0)
|
||||
- return ret;
|
||||
- }
|
||||
+ xt_ematch_foreach(ematch, e)
|
||||
+ xt_compat_match_from_user(ematch, dstptr, size);
|
||||
+
|
||||
de->target_offset = e->target_offset - (origsize - *size);
|
||||
t = compat_ipt_get_target(e);
|
||||
target = t->u.kernel.target;
|
||||
@@ -1606,7 +1603,6 @@ compat_copy_entry_from_user(struct compa
|
||||
if ((unsigned char *)de - base < newinfo->underflow[h])
|
||||
newinfo->underflow[h] -= origsize - *size;
|
||||
}
|
||||
- return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -1729,16 +1725,12 @@ translate_compat_table(struct net *net,
|
||||
entry1 = newinfo->entries;
|
||||
pos = entry1;
|
||||
size = compatr->size;
|
||||
- xt_entry_foreach(iter0, entry0, compatr->size) {
|
||||
- ret = compat_copy_entry_from_user(iter0, &pos, &size,
|
||||
- newinfo, entry1);
|
||||
- if (ret != 0)
|
||||
- break;
|
||||
- }
|
||||
+ xt_entry_foreach(iter0, entry0, compatr->size)
|
||||
+ compat_copy_entry_from_user(iter0, &pos, &size,
|
||||
+ newinfo, entry1);
|
||||
+
|
||||
xt_compat_flush_offsets(AF_INET);
|
||||
xt_compat_unlock(AF_INET);
|
||||
- if (ret)
|
||||
- goto free_newinfo;
|
||||
|
||||
ret = -ELOOP;
|
||||
if (!mark_source_chains(newinfo, compatr->valid_hooks, entry1))
|
||||
--- a/net/ipv6/netfilter/ip6_tables.c
|
||||
+++ b/net/ipv6/netfilter/ip6_tables.c
|
||||
@@ -1580,7 +1580,7 @@ release_matches:
|
||||
return ret;
|
||||
}
|
||||
|
||||
-static int
|
||||
+static void
|
||||
compat_copy_entry_from_user(struct compat_ip6t_entry *e, void **dstptr,
|
||||
unsigned int *size,
|
||||
struct xt_table_info *newinfo, unsigned char *base)
|
||||
@@ -1588,10 +1588,9 @@ compat_copy_entry_from_user(struct compa
|
||||
struct xt_entry_target *t;
|
||||
struct ip6t_entry *de;
|
||||
unsigned int origsize;
|
||||
- int ret, h;
|
||||
+ int h;
|
||||
struct xt_entry_match *ematch;
|
||||
|
||||
- ret = 0;
|
||||
origsize = *size;
|
||||
de = (struct ip6t_entry *)*dstptr;
|
||||
memcpy(de, e, sizeof(struct ip6t_entry));
|
||||
@@ -1600,11 +1599,9 @@ compat_copy_entry_from_user(struct compa
|
||||
*dstptr += sizeof(struct ip6t_entry);
|
||||
*size += sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
|
||||
|
||||
- xt_ematch_foreach(ematch, e) {
|
||||
- ret = xt_compat_match_from_user(ematch, dstptr, size);
|
||||
- if (ret != 0)
|
||||
- return ret;
|
||||
- }
|
||||
+ xt_ematch_foreach(ematch, e)
|
||||
+ xt_compat_match_from_user(ematch, dstptr, size);
|
||||
+
|
||||
de->target_offset = e->target_offset - (origsize - *size);
|
||||
t = compat_ip6t_get_target(e);
|
||||
xt_compat_target_from_user(t, dstptr, size);
|
||||
@@ -1616,7 +1613,6 @@ compat_copy_entry_from_user(struct compa
|
||||
if ((unsigned char *)de - base < newinfo->underflow[h])
|
||||
newinfo->underflow[h] -= origsize - *size;
|
||||
}
|
||||
- return ret;
|
||||
}
|
||||
|
||||
static int compat_check_entry(struct ip6t_entry *e, struct net *net,
|
||||
@@ -1737,17 +1733,12 @@ translate_compat_table(struct net *net,
|
||||
}
|
||||
entry1 = newinfo->entries;
|
||||
pos = entry1;
|
||||
- size = compatr->size;
|
||||
- xt_entry_foreach(iter0, entry0, compatr->size) {
|
||||
- ret = compat_copy_entry_from_user(iter0, &pos, &size,
|
||||
- newinfo, entry1);
|
||||
- if (ret != 0)
|
||||
- break;
|
||||
- }
|
||||
+ xt_entry_foreach(iter0, entry0, compatr->size)
|
||||
+ compat_copy_entry_from_user(iter0, &pos, &size,
|
||||
+ newinfo, entry1);
|
||||
+
|
||||
xt_compat_flush_offsets(AF_INET6);
|
||||
xt_compat_unlock(AF_INET6);
|
||||
- if (ret)
|
||||
- goto free_newinfo;
|
||||
|
||||
ret = -ELOOP;
|
||||
if (!mark_source_chains(newinfo, compatr->valid_hooks, entry1))
|
||||
--- a/net/netfilter/x_tables.c
|
||||
+++ b/net/netfilter/x_tables.c
|
||||
@@ -526,8 +526,8 @@ int xt_compat_match_offset(const struct
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xt_compat_match_offset);
|
||||
|
||||
-int xt_compat_match_from_user(struct xt_entry_match *m, void **dstptr,
|
||||
- unsigned int *size)
|
||||
+void xt_compat_match_from_user(struct xt_entry_match *m, void **dstptr,
|
||||
+ unsigned int *size)
|
||||
{
|
||||
const struct xt_match *match = m->u.kernel.match;
|
||||
struct compat_xt_entry_match *cm = (struct compat_xt_entry_match *)m;
|
||||
@@ -549,7 +549,6 @@ int xt_compat_match_from_user(struct xt_
|
||||
|
||||
*size += off;
|
||||
*dstptr += msize;
|
||||
- return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xt_compat_match_from_user);
|
||||
|
|
@ -115,6 +115,22 @@ bugfix/all/rds-fix-an-infoleak-in-rds_inc_info_copy.patch
|
|||
bugfix/all/keys-potential-uninitialized-variable.patch
|
||||
bugfix/all/percpu-fix-synchronization-between-chunk-map_extend_.patch
|
||||
bugfix/all/percpu-fix-synchronization-between-synchronous-map-e.patch
|
||||
bugfix/all/netfilter-x_tables-don-t-move-to-non-existent-next-r.patch
|
||||
bugfix/all/netfilter-x_tables-validate-targets-of-jumps.patch
|
||||
bugfix/all/netfilter-x_tables-add-and-use-xt_check_entry_offset.patch
|
||||
bugfix/all/netfilter-x_tables-kill-check_entry-helper.patch
|
||||
bugfix/all/netfilter-x_tables-assert-minimum-target-size.patch
|
||||
bugfix/all/netfilter-x_tables-add-compat-version-of-xt_check_en.patch
|
||||
bugfix/all/netfilter-x_tables-check-standard-target-size-too.patch
|
||||
bugfix/all/netfilter-x_tables-check-for-bogus-target-offset.patch
|
||||
bugfix/all/netfilter-x_tables-validate-all-offsets-and-sizes-in.patch
|
||||
bugfix/all/netfilter-x_tables-don-t-reject-valid-target-size-on.patch
|
||||
bugfix/all/netfilter-arp_tables-simplify-translate_compat_table.patch
|
||||
bugfix/all/netfilter-ip_tables-simplify-translate_compat_table-.patch
|
||||
bugfix/all/netfilter-ip6_tables-simplify-translate_compat_table.patch
|
||||
bugfix/all/netfilter-x_tables-xt_compat_match_from_user-doesn-t.patch
|
||||
bugfix/all/netfilter-x_tables-do-compat-validation-via-translat.patch
|
||||
bugfix/all/netfilter-x_tables-introduce-and-use-xt_copy_counter.patch
|
||||
|
||||
# ABI maintenance
|
||||
debian/mips-siginfo-fix-abi-change-in-4.6.2.patch
|
||||
|
|
Loading…
Reference in New Issue