From: Greg Kroah-Hartman Date: Thu, 23 Jun 2016 04:47:04 +0000 (-0700) Subject: 3.14-stable patches X-Git-Tag: v3.14.73~6 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=958c1f659fcd442a17d94ce5d84a04213b216493;p=thirdparty%2Fkernel%2Fstable-queue.git 3.14-stable patches added patches: netfilter-arp_tables-simplify-translate_compat_table-args.patch netfilter-ensure-number-of-counters-is-0-in-do_replace.patch netfilter-ip6_tables-simplify-translate_compat_table-args.patch netfilter-ip_tables-simplify-translate_compat_table-args.patch netfilter-x_tables-do-compat-validation-via-translate_table.patch netfilter-x_tables-xt_compat_match_from_user-doesn-t-need-a-retval.patch --- diff --git a/queue-3.14/netfilter-arp_tables-simplify-translate_compat_table-args.patch b/queue-3.14/netfilter-arp_tables-simplify-translate_compat_table-args.patch new file mode 100644 index 00000000000..c441dfe2f80 --- /dev/null +++ b/queue-3.14/netfilter-arp_tables-simplify-translate_compat_table-args.patch @@ -0,0 +1,213 @@ +From 8dddd32756f6fe8e4e82a63361119b7e2384e02f Mon Sep 17 00:00:00 2001 +From: Florian Westphal +Date: Fri, 1 Apr 2016 14:17:32 +0200 +Subject: netfilter: arp_tables: simplify translate_compat_table args + +From: Florian Westphal + +commit 8dddd32756f6fe8e4e82a63361119b7e2384e02f upstream. + +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman + +--- + 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 +@@ -1204,6 +1204,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; +@@ -1219,8 +1231,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; +@@ -1291,7 +1302,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; +@@ -1324,14 +1335,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; +@@ -1343,8 +1349,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++) { +@@ -1355,40 +1361,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; + } + } +@@ -1398,17 +1403,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[raw_smp_processor_id()]; + 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; + } +@@ -1418,12 +1423,12 @@ 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; + xt_entry_foreach(iter1, entry1, newinfo->size) { +- ret = check_target(iter1, name); ++ ret = check_target(iter1, compatr->name); + if (ret != 0) + break; + ++i; +@@ -1468,7 +1473,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); +@@ -1480,18 +1485,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) + { +@@ -1522,10 +1515,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; + diff --git a/queue-3.14/netfilter-ensure-number-of-counters-is-0-in-do_replace.patch b/queue-3.14/netfilter-ensure-number-of-counters-is-0-in-do_replace.patch new file mode 100644 index 00000000000..96e93a517dc --- /dev/null +++ b/queue-3.14/netfilter-ensure-number-of-counters-is-0-in-do_replace.patch @@ -0,0 +1,126 @@ +From 1086bbe97a074844188c6c988fa0b1a98c3ccbb9 Mon Sep 17 00:00:00 2001 +From: Dave Jones +Date: Tue, 19 May 2015 20:55:17 -0400 +Subject: netfilter: ensure number of counters is >0 in do_replace() + +From: Dave Jones + +commit 1086bbe97a074844188c6c988fa0b1a98c3ccbb9 upstream. + +After improving setsockopt() coverage in trinity, I started triggering +vmalloc failures pretty reliably from this code path: + +warn_alloc_failed+0xe9/0x140 +__vmalloc_node_range+0x1be/0x270 +vzalloc+0x4b/0x50 +__do_replace+0x52/0x260 [ip_tables] +do_ipt_set_ctl+0x15d/0x1d0 [ip_tables] +nf_setsockopt+0x65/0x90 +ip_setsockopt+0x61/0xa0 +raw_setsockopt+0x16/0x60 +sock_common_setsockopt+0x14/0x20 +SyS_setsockopt+0x71/0xd0 + +It turns out we don't validate that the num_counters field in the +struct we pass in from userspace is initialized. + +The same problem also exists in ebtables, arptables, ipv6, and the +compat variants. + +Signed-off-by: Dave Jones +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman + +--- + net/bridge/netfilter/ebtables.c | 4 ++++ + net/ipv4/netfilter/arp_tables.c | 6 ++++++ + net/ipv4/netfilter/ip_tables.c | 6 ++++++ + net/ipv6/netfilter/ip6_tables.c | 6 ++++++ + 4 files changed, 22 insertions(+) + +--- a/net/bridge/netfilter/ebtables.c ++++ b/net/bridge/netfilter/ebtables.c +@@ -1105,6 +1105,8 @@ static int do_replace(struct net *net, c + return -ENOMEM; + if (tmp.num_counters >= INT_MAX / sizeof(struct ebt_counter)) + return -ENOMEM; ++ if (tmp.num_counters == 0) ++ return -EINVAL; + + tmp.name[sizeof(tmp.name) - 1] = 0; + +@@ -2150,6 +2152,8 @@ static int compat_copy_ebt_replace_from_ + return -ENOMEM; + if (tmp.num_counters >= INT_MAX / sizeof(struct ebt_counter)) + return -ENOMEM; ++ if (tmp.num_counters == 0) ++ return -EINVAL; + + memcpy(repl, &tmp, offsetof(struct ebt_replace, hook_entry)); + +--- a/net/ipv4/netfilter/arp_tables.c ++++ b/net/ipv4/netfilter/arp_tables.c +@@ -1081,6 +1081,9 @@ static int do_replace(struct net *net, c + /* overflow check */ + if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) + return -ENOMEM; ++ if (tmp.num_counters == 0) ++ return -EINVAL; ++ + tmp.name[sizeof(tmp.name)-1] = 0; + + newinfo = xt_alloc_table_info(tmp.size); +@@ -1495,6 +1498,9 @@ static int compat_do_replace(struct net + return -ENOMEM; + if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) + return -ENOMEM; ++ if (tmp.num_counters == 0) ++ return -EINVAL; ++ + tmp.name[sizeof(tmp.name)-1] = 0; + + newinfo = xt_alloc_table_info(tmp.size); +--- a/net/ipv4/netfilter/ip_tables.c ++++ b/net/ipv4/netfilter/ip_tables.c +@@ -1267,6 +1267,9 @@ do_replace(struct net *net, const void _ + /* overflow check */ + if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) + return -ENOMEM; ++ if (tmp.num_counters == 0) ++ return -EINVAL; ++ + tmp.name[sizeof(tmp.name)-1] = 0; + + newinfo = xt_alloc_table_info(tmp.size); +@@ -1802,6 +1805,9 @@ compat_do_replace(struct net *net, void + return -ENOMEM; + if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) + return -ENOMEM; ++ if (tmp.num_counters == 0) ++ return -EINVAL; ++ + tmp.name[sizeof(tmp.name)-1] = 0; + + newinfo = xt_alloc_table_info(tmp.size); +--- a/net/ipv6/netfilter/ip6_tables.c ++++ b/net/ipv6/netfilter/ip6_tables.c +@@ -1277,6 +1277,9 @@ do_replace(struct net *net, const void _ + /* overflow check */ + if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) + return -ENOMEM; ++ if (tmp.num_counters == 0) ++ return -EINVAL; ++ + tmp.name[sizeof(tmp.name)-1] = 0; + + newinfo = xt_alloc_table_info(tmp.size); +@@ -1811,6 +1814,9 @@ compat_do_replace(struct net *net, void + return -ENOMEM; + if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) + return -ENOMEM; ++ if (tmp.num_counters == 0) ++ return -EINVAL; ++ + tmp.name[sizeof(tmp.name)-1] = 0; + + newinfo = xt_alloc_table_info(tmp.size); diff --git a/queue-3.14/netfilter-ip6_tables-simplify-translate_compat_table-args.patch b/queue-3.14/netfilter-ip6_tables-simplify-translate_compat_table-args.patch new file mode 100644 index 00000000000..b82babd83d9 --- /dev/null +++ b/queue-3.14/netfilter-ip6_tables-simplify-translate_compat_table-args.patch @@ -0,0 +1,193 @@ +From 329a0807124f12fe1c8032f95d8a8eb47047fb0e Mon Sep 17 00:00:00 2001 +From: Florian Westphal +Date: Fri, 1 Apr 2016 14:17:31 +0200 +Subject: netfilter: ip6_tables: simplify translate_compat_table args + +From: Florian Westphal + +commit 329a0807124f12fe1c8032f95d8a8eb47047fb0e upstream. + +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman + +--- + net/ipv6/netfilter/ip6_tables.c | 61 ++++++++++++++++------------------------ + 1 file changed, 25 insertions(+), 36 deletions(-) + +--- a/net/ipv6/netfilter/ip6_tables.c ++++ b/net/ipv6/netfilter/ip6_tables.c +@@ -1456,7 +1456,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, + unsigned int hookmask, + int *size) +@@ -1494,8 +1493,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; +@@ -1531,8 +1529,8 @@ 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, e->comefrom, &off); ++ ret = compat_find_calc_match(ematch, &e->ipv6, e->comefrom, ++ &off); + if (ret != 0) + goto release_matches; + ++j; +@@ -1581,7 +1579,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; +@@ -1655,14 +1653,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; +@@ -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_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; + } + } +@@ -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[raw_smp_processor_id()]; + 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; +@@ -1799,7 +1791,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); +@@ -1842,10 +1834,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; + diff --git a/queue-3.14/netfilter-ip_tables-simplify-translate_compat_table-args.patch b/queue-3.14/netfilter-ip_tables-simplify-translate_compat_table-args.patch new file mode 100644 index 00000000000..89cf6c10448 --- /dev/null +++ b/queue-3.14/netfilter-ip_tables-simplify-translate_compat_table-args.patch @@ -0,0 +1,193 @@ +From 7d3f843eed29222254c9feab481f55175a1afcc9 Mon Sep 17 00:00:00 2001 +From: Florian Westphal +Date: Fri, 1 Apr 2016 14:17:30 +0200 +Subject: netfilter: ip_tables: simplify translate_compat_table args + +From: Florian Westphal + +commit 7d3f843eed29222254c9feab481f55175a1afcc9 upstream. + +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman + +--- + net/ipv4/netfilter/ip_tables.c | 61 ++++++++++++++++------------------------- + 1 file changed, 25 insertions(+), 36 deletions(-) + +--- a/net/ipv4/netfilter/ip_tables.c ++++ b/net/ipv4/netfilter/ip_tables.c +@@ -1444,7 +1444,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, + unsigned int hookmask, + int *size) +@@ -1482,8 +1481,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; +@@ -1519,8 +1517,8 @@ 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, e->comefrom, &off); ++ ret = compat_find_calc_match(ematch, &e->ip, e->comefrom, ++ &off); + if (ret != 0) + goto release_matches; + ++j; +@@ -1569,7 +1567,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; +@@ -1645,14 +1643,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; +@@ -1664,8 +1657,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++) { +@@ -1676,40 +1669,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; + } + } +@@ -1719,17 +1711,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[raw_smp_processor_id()]; + 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; + } +@@ -1739,12 +1731,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; +@@ -1789,7 +1781,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); +@@ -1832,10 +1824,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; + diff --git a/queue-3.14/netfilter-x_tables-do-compat-validation-via-translate_table.patch b/queue-3.14/netfilter-x_tables-do-compat-validation-via-translate_table.patch new file mode 100644 index 00000000000..aa7d97cf11e --- /dev/null +++ b/queue-3.14/netfilter-x_tables-do-compat-validation-via-translate_table.patch @@ -0,0 +1,792 @@ +From 09d9686047dbbe1cf4faa558d3ecc4aae2046054 Mon Sep 17 00:00:00 2001 +From: Florian Westphal +Date: Fri, 1 Apr 2016 14:17:34 +0200 +Subject: netfilter: x_tables: do compat validation via translate_table + +From: Florian Westphal + +commit 09d9686047dbbe1cf4faa558d3ecc4aae2046054 upstream. + +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 +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman + +--- + net/ipv4/netfilter/arp_tables.c | 109 +++++----------------------- + net/ipv4/netfilter/ip_tables.c | 151 +++++++--------------------------------- + net/ipv6/netfilter/ip6_tables.c | 145 +++++--------------------------------- + net/netfilter/x_tables.c | 8 ++ + 4 files changed, 80 insertions(+), 333 deletions(-) + +--- a/net/ipv4/netfilter/arp_tables.c ++++ b/net/ipv4/netfilter/arp_tables.c +@@ -1227,19 +1227,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 || +@@ -1284,17 +1282,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: +@@ -1344,7 +1331,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; + +@@ -1353,12 +1340,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); +@@ -1367,9 +1348,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; +@@ -1382,23 +1361,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) +@@ -1415,51 +1377,25 @@ 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; ++ memcpy(&repl, compatr, sizeof(*compatr)); + +- i = 0; +- xt_entry_foreach(iter1, entry1, newinfo->size) { +- ret = check_target(iter1, compatr->name); +- if (ret != 0) +- 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]; + } + +- /* And one copy for every other CPU */ +- for_each_possible_cpu(i) +- if (newinfo->entries[i] && newinfo->entries[i] != entry1) +- memcpy(newinfo->entries[i], entry1, newinfo->size); ++ 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; +@@ -1468,17 +1404,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 +@@ -1482,16 +1482,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,41 +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; +- +- 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); +- } +- return ret; +-} +- +-static int + translate_compat_table(struct net *net, + struct xt_table_info **pinfo, + void **pentry0, +@@ -1650,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; + +@@ -1659,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); +@@ -1673,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; +@@ -1688,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) +@@ -1712,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[raw_smp_processor_id()]; + pos = entry1; +@@ -1722,51 +1650,29 @@ 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]; + } + +- /* And one copy for every other CPU */ +- for_each_possible_cpu(i) +- if (newinfo->entries[i] && newinfo->entries[i] != entry1) +- memcpy(newinfo->entries[i], entry1, newinfo->size); ++ 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; +@@ -1775,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 +@@ -1494,16 +1494,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,41 +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; +- +- 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); +- } +- return ret; +-} +- + static int + translate_compat_table(struct net *net, + struct xt_table_info **pinfo, +@@ -1660,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; + +@@ -1669,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); +@@ -1683,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; +@@ -1698,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) +@@ -1722,60 +1649,33 @@ 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[raw_smp_processor_id()]; + 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]; + } + +- /* And one copy for every other CPU */ +- for_each_possible_cpu(i) +- if (newinfo->entries[i] && newinfo->entries[i] != entry1) +- memcpy(newinfo->entries[i], entry1, newinfo->size); ++ 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; +@@ -1784,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 +@@ -552,6 +552,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)); +@@ -565,6 +566,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; +@@ -782,6 +786,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)); +@@ -795,6 +800,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; diff --git a/queue-3.14/netfilter-x_tables-xt_compat_match_from_user-doesn-t-need-a-retval.patch b/queue-3.14/netfilter-x_tables-xt_compat_match_from_user-doesn-t-need-a-retval.patch new file mode 100644 index 00000000000..e3cd54195b9 --- /dev/null +++ b/queue-3.14/netfilter-x_tables-xt_compat_match_from_user-doesn-t-need-a-retval.patch @@ -0,0 +1,240 @@ +From 0188346f21e6546498c2a0f84888797ad4063fc5 Mon Sep 17 00:00:00 2001 +From: Florian Westphal +Date: Fri, 1 Apr 2016 14:17:33 +0200 +Subject: netfilter: x_tables: xt_compat_match_from_user doesn't need a retval + +From: Florian Westphal + +commit 0188346f21e6546498c2a0f84888797ad4063fc5 upstream. + +Always returned 0. + +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman + +--- + 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 +@@ -425,7 +425,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 +@@ -1300,7 +1300,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) +@@ -1309,9 +1309,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)); +@@ -1332,7 +1331,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, +@@ -1411,16 +1409,11 @@ static int translate_compat_table(struct + entry1 = newinfo->entries[raw_smp_processor_id()]; + 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 +@@ -1565,7 +1565,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) +@@ -1574,10 +1574,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)); +@@ -1586,11 +1585,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; +@@ -1603,7 +1600,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 +@@ -1719,16 +1715,12 @@ translate_compat_table(struct net *net, + entry1 = newinfo->entries[raw_smp_processor_id()]; + 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 +@@ -1577,7 +1577,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) +@@ -1585,10 +1585,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)); +@@ -1597,11 +1596,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); +@@ -1613,7 +1610,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, +@@ -1728,17 +1724,12 @@ translate_compat_table(struct net *net, + } + entry1 = newinfo->entries[raw_smp_processor_id()]; + 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 +@@ -545,8 +545,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; +@@ -568,7 +568,6 @@ int xt_compat_match_from_user(struct xt_ + + *size += off; + *dstptr += msize; +- return 0; + } + EXPORT_SYMBOL_GPL(xt_compat_match_from_user); + diff --git a/queue-3.14/series b/queue-3.14/series index 49eb2602688..dd944229739 100644 --- a/queue-3.14/series +++ b/queue-3.14/series @@ -27,3 +27,9 @@ netfilter-x_tables-check-standard-target-size-too.patch netfilter-x_tables-check-for-bogus-target-offset.patch netfilter-x_tables-validate-all-offsets-and-sizes-in-a-rule.patch netfilter-x_tables-don-t-reject-valid-target-size-on-some-architectures.patch +netfilter-arp_tables-simplify-translate_compat_table-args.patch +netfilter-ip_tables-simplify-translate_compat_table-args.patch +netfilter-ip6_tables-simplify-translate_compat_table-args.patch +netfilter-x_tables-xt_compat_match_from_user-doesn-t-need-a-retval.patch +netfilter-ensure-number-of-counters-is-0-in-do_replace.patch +netfilter-x_tables-do-compat-validation-via-translate_table.patch