summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Pagano <mpagano@gentoo.org>2016-06-24 16:37:40 -0400
committerMike Pagano <mpagano@gentoo.org>2016-06-24 16:37:40 -0400
commitf9a15264873604ef2ac27a04eec02ad8698b25f4 (patch)
tree2f588df1bfb918f02005fed8650a8c7db6f58bfc
parentLinux patch 3.14.72 (diff)
downloadlinux-patches-f9a15264873604ef2ac27a04eec02ad8698b25f4.tar.gz
linux-patches-f9a15264873604ef2ac27a04eec02ad8698b25f4.tar.bz2
linux-patches-f9a15264873604ef2ac27a04eec02ad8698b25f4.zip
Linux patch 3.14.733.14-80
-rw-r--r--0000_README4
-rw-r--r--1072_linux-3.14.73.patch2837
2 files changed, 2841 insertions, 0 deletions
diff --git a/0000_README b/0000_README
index d2e93d29..7e32ae1f 100644
--- a/0000_README
+++ b/0000_README
@@ -330,6 +330,10 @@ Patch: 1071_linux-3.14.72.patch
From: http://www.kernel.org
Desc: Linux 3.14.72
+Patch: 1072_linux-3.14.73.patch
+From: http://www.kernel.org
+Desc: Linux 3.14.73
+
Patch: 1500_XATTR_USER_PREFIX.patch
From: https://bugs.gentoo.org/show_bug.cgi?id=470644
Desc: Support for namespace user.pax.* on tmpfs.
diff --git a/1072_linux-3.14.73.patch b/1072_linux-3.14.73.patch
new file mode 100644
index 00000000..c43abbc6
--- /dev/null
+++ b/1072_linux-3.14.73.patch
@@ -0,0 +1,2837 @@
+diff --git a/Documentation/sysctl/fs.txt b/Documentation/sysctl/fs.txt
+index 88152f214f48..302b5ed616a6 100644
+--- a/Documentation/sysctl/fs.txt
++++ b/Documentation/sysctl/fs.txt
+@@ -32,6 +32,8 @@ Currently, these files are in /proc/sys/fs:
+ - nr_open
+ - overflowuid
+ - overflowgid
++- pipe-user-pages-hard
++- pipe-user-pages-soft
+ - protected_hardlinks
+ - protected_symlinks
+ - suid_dumpable
+@@ -159,6 +161,27 @@ The default is 65534.
+
+ ==============================================================
+
++pipe-user-pages-hard:
++
++Maximum total number of pages a non-privileged user may allocate for pipes.
++Once this limit is reached, no new pipes may be allocated until usage goes
++below the limit again. When set to 0, no limit is applied, which is the default
++setting.
++
++==============================================================
++
++pipe-user-pages-soft:
++
++Maximum total number of pages a non-privileged user may allocate for pipes
++before the pipe size gets limited to a single page. Once this limit is reached,
++new pipes will be limited to a single page in size for this user in order to
++limit total memory usage, and trying to increase them using fcntl() will be
++denied until usage goes below the limit again. The default value allows to
++allocate up to 1024 pipes at their default size. When set to 0, no limit is
++applied.
++
++==============================================================
++
+ protected_hardlinks:
+
+ A long-standing class of security issues is the hardlink-based
+diff --git a/Makefile b/Makefile
+index 6155aaf6342a..939dfae7bb5f 100644
+--- a/Makefile
++++ b/Makefile
+@@ -1,6 +1,6 @@
+ VERSION = 3
+ PATCHLEVEL = 14
+-SUBLEVEL = 72
++SUBLEVEL = 73
+ EXTRAVERSION =
+ NAME = Remembering Coco
+
+diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c
+index 0dd3b79b15c3..ec33df500f86 100644
+--- a/arch/arm/kernel/ptrace.c
++++ b/arch/arm/kernel/ptrace.c
+@@ -733,8 +733,8 @@ static int vfp_set(struct task_struct *target,
+ if (ret)
+ return ret;
+
+- vfp_flush_hwstate(thread);
+ thread->vfpstate.hard = new_vfp;
++ vfp_flush_hwstate(thread);
+
+ return 0;
+ }
+diff --git a/arch/mips/include/asm/processor.h b/arch/mips/include/asm/processor.h
+index 3605b844ad87..efe9964ea9b4 100644
+--- a/arch/mips/include/asm/processor.h
++++ b/arch/mips/include/asm/processor.h
+@@ -51,7 +51,7 @@ extern unsigned int vced_count, vcei_count;
+ * User space process size: 2GB. This is hardcoded into a few places,
+ * so don't change it unless you know what you are doing.
+ */
+-#define TASK_SIZE 0x7fff8000UL
++#define TASK_SIZE 0x80000000UL
+ #endif
+
+ #ifdef __KERNEL__
+diff --git a/arch/parisc/kernel/unaligned.c b/arch/parisc/kernel/unaligned.c
+index d7c0acb35ec2..8d49614d600d 100644
+--- a/arch/parisc/kernel/unaligned.c
++++ b/arch/parisc/kernel/unaligned.c
+@@ -666,7 +666,7 @@ void handle_unaligned(struct pt_regs *regs)
+ break;
+ }
+
+- if (modify && R1(regs->iir))
++ if (ret == 0 && modify && R1(regs->iir))
+ regs->gr[R1(regs->iir)] = newbase;
+
+
+@@ -677,6 +677,14 @@ void handle_unaligned(struct pt_regs *regs)
+
+ if (ret)
+ {
++ /*
++ * The unaligned handler failed.
++ * If we were called by __get_user() or __put_user() jump
++ * to it's exception fixup handler instead of crashing.
++ */
++ if (!user_mode(regs) && fixup_exception(regs))
++ return;
++
+ printk(KERN_CRIT "Unaligned handler failed, ret = %d\n", ret);
+ die_if_kernel("Unaligned data reference", regs, 28);
+
+diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h
+index 92c538d5ed19..940b15703885 100644
+--- a/arch/powerpc/include/asm/reg.h
++++ b/arch/powerpc/include/asm/reg.h
+@@ -680,7 +680,7 @@
+ #define MMCR0_FCWAIT 0x00000002UL /* freeze counter in WAIT state */
+ #define MMCR0_FCHV 0x00000001UL /* freeze conditions in hypervisor mode */
+ #define SPRN_MMCR1 798
+-#define SPRN_MMCR2 769
++#define SPRN_MMCR2 785
+ #define SPRN_MMCRA 0x312
+ #define MMCRA_SDSYNC 0x80000000UL /* SDAR synced with SIAR */
+ #define MMCRA_SDAR_DCACHE_MISS 0x40000000UL
+@@ -715,13 +715,13 @@
+ #define SPRN_PMC6 792
+ #define SPRN_PMC7 793
+ #define SPRN_PMC8 794
+-#define SPRN_SIAR 780
+-#define SPRN_SDAR 781
+ #define SPRN_SIER 784
+ #define SIER_SIPR 0x2000000 /* Sampled MSR_PR */
+ #define SIER_SIHV 0x1000000 /* Sampled MSR_HV */
+ #define SIER_SIAR_VALID 0x0400000 /* SIAR contents valid */
+ #define SIER_SDAR_VALID 0x0200000 /* SDAR contents valid */
++#define SPRN_SIAR 796
++#define SPRN_SDAR 797
+ #define SPRN_TACR 888
+ #define SPRN_TCSCR 889
+ #define SPRN_CSIGR 890
+diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c
+index 83da53fde6b5..6700b83b7259 100644
+--- a/arch/powerpc/platforms/pseries/eeh_pseries.c
++++ b/arch/powerpc/platforms/pseries/eeh_pseries.c
+@@ -615,29 +615,50 @@ static int pseries_eeh_configure_bridge(struct eeh_pe *pe)
+ {
+ int config_addr;
+ int ret;
++ /* Waiting 0.2s maximum before skipping configuration */
++ int max_wait = 200;
+
+ /* Figure out the PE address */
+ config_addr = pe->config_addr;
+ if (pe->addr)
+ config_addr = pe->addr;
+
+- /* Use new configure-pe function, if supported */
+- if (ibm_configure_pe != RTAS_UNKNOWN_SERVICE) {
+- ret = rtas_call(ibm_configure_pe, 3, 1, NULL,
+- config_addr, BUID_HI(pe->phb->buid),
+- BUID_LO(pe->phb->buid));
+- } else if (ibm_configure_bridge != RTAS_UNKNOWN_SERVICE) {
+- ret = rtas_call(ibm_configure_bridge, 3, 1, NULL,
+- config_addr, BUID_HI(pe->phb->buid),
+- BUID_LO(pe->phb->buid));
+- } else {
+- return -EFAULT;
+- }
++ while (max_wait > 0) {
++ /* Use new configure-pe function, if supported */
++ if (ibm_configure_pe != RTAS_UNKNOWN_SERVICE) {
++ ret = rtas_call(ibm_configure_pe, 3, 1, NULL,
++ config_addr, BUID_HI(pe->phb->buid),
++ BUID_LO(pe->phb->buid));
++ } else if (ibm_configure_bridge != RTAS_UNKNOWN_SERVICE) {
++ ret = rtas_call(ibm_configure_bridge, 3, 1, NULL,
++ config_addr, BUID_HI(pe->phb->buid),
++ BUID_LO(pe->phb->buid));
++ } else {
++ return -EFAULT;
++ }
+
+- if (ret)
+- pr_warning("%s: Unable to configure bridge PHB#%d-PE#%x (%d)\n",
+- __func__, pe->phb->global_number, pe->addr, ret);
++ if (!ret)
++ return ret;
++
++ /*
++ * If RTAS returns a delay value that's above 100ms, cut it
++ * down to 100ms in case firmware made a mistake. For more
++ * on how these delay values work see rtas_busy_delay_time
++ */
++ if (ret > RTAS_EXTENDED_DELAY_MIN+2 &&
++ ret <= RTAS_EXTENDED_DELAY_MAX)
++ ret = RTAS_EXTENDED_DELAY_MIN+2;
++
++ max_wait -= rtas_busy_delay_time(ret);
++
++ if (max_wait < 0)
++ break;
++
++ rtas_busy_delay(ret);
++ }
+
++ pr_warn("%s: Unable to configure bridge PHB#%d-PE#%x (%d)\n",
++ __func__, pe->phb->global_number, pe->addr, ret);
+ return ret;
+ }
+
+diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
+index 8db66424be97..6cb4ce77df19 100644
+--- a/arch/x86/kvm/x86.c
++++ b/arch/x86/kvm/x86.c
+@@ -3029,6 +3029,11 @@ static int kvm_vcpu_ioctl_x86_set_debugregs(struct kvm_vcpu *vcpu,
+ if (dbgregs->flags)
+ return -EINVAL;
+
++ if (dbgregs->dr6 & ~0xffffffffull)
++ return -EINVAL;
++ if (dbgregs->dr7 & ~0xffffffffull)
++ return -EINVAL;
++
+ memcpy(vcpu->arch.db, dbgregs->db, sizeof(vcpu->arch.db));
+ vcpu->arch.dr6 = dbgregs->dr6;
+ kvm_update_dr6(vcpu);
+diff --git a/drivers/crypto/ccp/ccp-crypto-aes-xts.c b/drivers/crypto/ccp/ccp-crypto-aes-xts.c
+index 0237ab58f242..a39ee43d1a79 100644
+--- a/drivers/crypto/ccp/ccp-crypto-aes-xts.c
++++ b/drivers/crypto/ccp/ccp-crypto-aes-xts.c
+@@ -123,6 +123,7 @@ static int ccp_aes_xts_crypt(struct ablkcipher_request *req,
+ struct ccp_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+ struct ccp_aes_req_ctx *rctx = ablkcipher_request_ctx(req);
+ unsigned int unit;
++ u32 unit_size;
+ int ret;
+
+ if (!ctx->u.aes.key_len)
+@@ -134,11 +135,17 @@ static int ccp_aes_xts_crypt(struct ablkcipher_request *req,
+ if (!req->info)
+ return -EINVAL;
+
+- for (unit = 0; unit < ARRAY_SIZE(unit_size_map); unit++)
+- if (!(req->nbytes & (unit_size_map[unit].size - 1)))
+- break;
++ unit_size = CCP_XTS_AES_UNIT_SIZE__LAST;
++ if (req->nbytes <= unit_size_map[0].size) {
++ for (unit = 0; unit < ARRAY_SIZE(unit_size_map); unit++) {
++ if (!(req->nbytes & (unit_size_map[unit].size - 1))) {
++ unit_size = unit_size_map[unit].value;
++ break;
++ }
++ }
++ }
+
+- if ((unit_size_map[unit].value == CCP_XTS_AES_UNIT_SIZE__LAST) ||
++ if ((unit_size == CCP_XTS_AES_UNIT_SIZE__LAST) ||
+ (ctx->u.aes.key_len != AES_KEYSIZE_128)) {
+ /* Use the fallback to process the request for any
+ * unsupported unit sizes or key sizes
+@@ -159,7 +166,7 @@ static int ccp_aes_xts_crypt(struct ablkcipher_request *req,
+ rctx->cmd.engine = CCP_ENGINE_XTS_AES_128;
+ rctx->cmd.u.xts.action = (encrypt) ? CCP_AES_ACTION_ENCRYPT
+ : CCP_AES_ACTION_DECRYPT;
+- rctx->cmd.u.xts.unit_size = unit_size_map[unit].value;
++ rctx->cmd.u.xts.unit_size = unit_size;
+ rctx->cmd.u.xts.key = &ctx->u.aes.key_sg;
+ rctx->cmd.u.xts.key_len = ctx->u.aes.key_len;
+ rctx->cmd.u.xts.iv = &rctx->iv_sg;
+diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c
+index 7645a3ce3854..b2af06444c2d 100644
+--- a/drivers/net/ethernet/sfc/ef10.c
++++ b/drivers/net/ethernet/sfc/ef10.c
+@@ -451,6 +451,17 @@ fail:
+ return rc;
+ }
+
++static void efx_ef10_forget_old_piobufs(struct efx_nic *efx)
++{
++ struct efx_channel *channel;
++ struct efx_tx_queue *tx_queue;
++
++ /* All our existing PIO buffers went away */
++ efx_for_each_channel(channel, efx)
++ efx_for_each_channel_tx_queue(tx_queue, channel)
++ tx_queue->piobuf = NULL;
++}
++
+ #else /* !EFX_USE_PIO */
+
+ static int efx_ef10_alloc_piobufs(struct efx_nic *efx, unsigned int n)
+@@ -467,6 +478,10 @@ static void efx_ef10_free_piobufs(struct efx_nic *efx)
+ {
+ }
+
++static void efx_ef10_forget_old_piobufs(struct efx_nic *efx)
++{
++}
++
+ #endif /* EFX_USE_PIO */
+
+ static void efx_ef10_remove(struct efx_nic *efx)
+@@ -698,6 +713,7 @@ static void efx_ef10_reset_mc_allocations(struct efx_nic *efx)
+ nic_data->must_realloc_vis = true;
+ nic_data->must_restore_filters = true;
+ nic_data->must_restore_piobufs = true;
++ efx_ef10_forget_old_piobufs(efx);
+ nic_data->rx_rss_context = EFX_EF10_RSS_CONTEXT_INVALID;
+ }
+
+diff --git a/fs/dcache.c b/fs/dcache.c
+index 9b235362efcd..47c06888dc05 100644
+--- a/fs/dcache.c
++++ b/fs/dcache.c
+@@ -1500,7 +1500,7 @@ struct dentry *d_alloc(struct dentry * parent, const struct qstr *name)
+ struct dentry *dentry = __d_alloc(parent->d_sb, name);
+ if (!dentry)
+ return NULL;
+-
++ dentry->d_flags |= DCACHE_RCUACCESS;
+ spin_lock(&parent->d_lock);
+ /*
+ * don't need child lock because it is not subject
+@@ -2352,7 +2352,6 @@ static void __d_rehash(struct dentry * entry, struct hlist_bl_head *b)
+ {
+ BUG_ON(!d_unhashed(entry));
+ hlist_bl_lock(b);
+- entry->d_flags |= DCACHE_RCUACCESS;
+ hlist_bl_add_head_rcu(&entry->d_hash, b);
+ hlist_bl_unlock(b);
+ }
+@@ -2536,6 +2535,7 @@ static void __d_move(struct dentry * dentry, struct dentry * target)
+
+ /* ... and switch the parents */
+ if (IS_ROOT(dentry)) {
++ dentry->d_flags |= DCACHE_RCUACCESS;
+ dentry->d_parent = target->d_parent;
+ target->d_parent = target;
+ INIT_LIST_HEAD(&target->d_child);
+diff --git a/fs/ecryptfs/kthread.c b/fs/ecryptfs/kthread.c
+index f1ea610362c6..9b661a4ccee7 100644
+--- a/fs/ecryptfs/kthread.c
++++ b/fs/ecryptfs/kthread.c
+@@ -25,6 +25,7 @@
+ #include <linux/slab.h>
+ #include <linux/wait.h>
+ #include <linux/mount.h>
++#include <linux/file.h>
+ #include "ecryptfs_kernel.h"
+
+ struct ecryptfs_open_req {
+@@ -147,7 +148,7 @@ int ecryptfs_privileged_open(struct file **lower_file,
+ flags |= IS_RDONLY(lower_dentry->d_inode) ? O_RDONLY : O_RDWR;
+ (*lower_file) = dentry_open(&req.path, flags, cred);
+ if (!IS_ERR(*lower_file))
+- goto out;
++ goto have_file;
+ if ((flags & O_ACCMODE) == O_RDONLY) {
+ rc = PTR_ERR((*lower_file));
+ goto out;
+@@ -165,8 +166,16 @@ int ecryptfs_privileged_open(struct file **lower_file,
+ mutex_unlock(&ecryptfs_kthread_ctl.mux);
+ wake_up(&ecryptfs_kthread_ctl.wait);
+ wait_for_completion(&req.done);
+- if (IS_ERR(*lower_file))
++ if (IS_ERR(*lower_file)) {
+ rc = PTR_ERR(*lower_file);
++ goto out;
++ }
++have_file:
++ if ((*lower_file)->f_op->mmap == NULL) {
++ fput(*lower_file);
++ *lower_file = NULL;
++ rc = -EMEDIUMTYPE;
++ }
+ out:
+ return rc;
+ }
+diff --git a/fs/pipe.c b/fs/pipe.c
+index a03801186366..fff8057a0809 100644
+--- a/fs/pipe.c
++++ b/fs/pipe.c
+@@ -39,6 +39,12 @@ unsigned int pipe_max_size = 1048576;
+ */
+ unsigned int pipe_min_size = PAGE_SIZE;
+
++/* Maximum allocatable pages per user. Hard limit is unset by default, soft
++ * matches default values.
++ */
++unsigned long pipe_user_pages_hard;
++unsigned long pipe_user_pages_soft = PIPE_DEF_BUFFERS * INR_OPEN_CUR;
++
+ /*
+ * We use a start+len construction, which provides full use of the
+ * allocated memory.
+@@ -795,20 +801,49 @@ pipe_fasync(int fd, struct file *filp, int on)
+ return retval;
+ }
+
++static void account_pipe_buffers(struct pipe_inode_info *pipe,
++ unsigned long old, unsigned long new)
++{
++ atomic_long_add(new - old, &pipe->user->pipe_bufs);
++}
++
++static bool too_many_pipe_buffers_soft(struct user_struct *user)
++{
++ return pipe_user_pages_soft &&
++ atomic_long_read(&user->pipe_bufs) >= pipe_user_pages_soft;
++}
++
++static bool too_many_pipe_buffers_hard(struct user_struct *user)
++{
++ return pipe_user_pages_hard &&
++ atomic_long_read(&user->pipe_bufs) >= pipe_user_pages_hard;
++}
++
+ struct pipe_inode_info *alloc_pipe_info(void)
+ {
+ struct pipe_inode_info *pipe;
+
+ pipe = kzalloc(sizeof(struct pipe_inode_info), GFP_KERNEL);
+ if (pipe) {
+- pipe->bufs = kzalloc(sizeof(struct pipe_buffer) * PIPE_DEF_BUFFERS, GFP_KERNEL);
++ unsigned long pipe_bufs = PIPE_DEF_BUFFERS;
++ struct user_struct *user = get_current_user();
++
++ if (!too_many_pipe_buffers_hard(user)) {
++ if (too_many_pipe_buffers_soft(user))
++ pipe_bufs = 1;
++ pipe->bufs = kzalloc(sizeof(struct pipe_buffer) * pipe_bufs, GFP_KERNEL);
++ }
++
+ if (pipe->bufs) {
+ init_waitqueue_head(&pipe->wait);
+ pipe->r_counter = pipe->w_counter = 1;
+- pipe->buffers = PIPE_DEF_BUFFERS;
++ pipe->buffers = pipe_bufs;
++ pipe->user = user;
++ account_pipe_buffers(pipe, 0, pipe_bufs);
+ mutex_init(&pipe->mutex);
+ return pipe;
+ }
++ free_uid(user);
+ kfree(pipe);
+ }
+
+@@ -819,6 +854,8 @@ void free_pipe_info(struct pipe_inode_info *pipe)
+ {
+ int i;
+
++ account_pipe_buffers(pipe, pipe->buffers, 0);
++ free_uid(pipe->user);
+ for (i = 0; i < pipe->buffers; i++) {
+ struct pipe_buffer *buf = pipe->bufs + i;
+ if (buf->ops)
+@@ -1209,6 +1246,7 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long nr_pages)
+ memcpy(bufs + head, pipe->bufs, tail * sizeof(struct pipe_buffer));
+ }
+
++ account_pipe_buffers(pipe, pipe->buffers, nr_pages);
+ pipe->curbuf = 0;
+ kfree(pipe->bufs);
+ pipe->bufs = bufs;
+@@ -1280,6 +1318,11 @@ long pipe_fcntl(struct file *file, unsigned int cmd, unsigned long arg)
+ if (!capable(CAP_SYS_RESOURCE) && size > pipe_max_size) {
+ ret = -EPERM;
+ goto out;
++ } else if ((too_many_pipe_buffers_hard(pipe->user) ||
++ too_many_pipe_buffers_soft(pipe->user)) &&
++ !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN)) {
++ ret = -EPERM;
++ goto out;
+ }
+ ret = pipe_set_size(pipe, nr_pages);
+ break;
+diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
+index fb8579d35cd4..47533013a772 100644
+--- a/fs/xfs/xfs_inode.c
++++ b/fs/xfs/xfs_inode.c
+@@ -3098,7 +3098,7 @@ xfs_iflush(
+ */
+ error = xfs_imap_to_bp(mp, NULL, &ip->i_imap, &dip, &bp, XBF_TRYLOCK,
+ 0);
+- if (error == -EAGAIN) {
++ if (error == EAGAIN) {
+ xfs_ifunlock(ip);
+ return error;
+ }
+diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h
+index a3e215bb0241..7741efa43b35 100644
+--- a/include/linux/netfilter/x_tables.h
++++ b/include/linux/netfilter/x_tables.h
+@@ -239,11 +239,18 @@ void xt_unregister_match(struct xt_match *target);
+ 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, const char *elems,
++ 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,
+ 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,
+@@ -421,7 +428,7 @@ void xt_compat_init_offsets(u_int8_t af, unsigned int number);
+ 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);
+@@ -431,6 +438,9 @@ void xt_compat_target_from_user(struct xt_entry_target *t, void **dstptr,
+ 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, const char *elems,
++ unsigned int target_offset,
++ unsigned int next_offset);
+
+ #endif /* CONFIG_COMPAT */
+ #endif /* _X_TABLES_H */
+diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h
+index ab5752692113..b3374f63bc36 100644
+--- a/include/linux/pipe_fs_i.h
++++ b/include/linux/pipe_fs_i.h
+@@ -42,6 +42,7 @@ struct pipe_buffer {
+ * @fasync_readers: reader side fasync
+ * @fasync_writers: writer side fasync
+ * @bufs: the circular array of pipe buffers
++ * @user: the user who created this pipe
+ **/
+ struct pipe_inode_info {
+ struct mutex mutex;
+@@ -57,6 +58,7 @@ struct pipe_inode_info {
+ struct fasync_struct *fasync_readers;
+ struct fasync_struct *fasync_writers;
+ struct pipe_buffer *bufs;
++ struct user_struct *user;
+ };
+
+ /*
+@@ -140,6 +142,8 @@ void pipe_unlock(struct pipe_inode_info *);
+ void pipe_double_lock(struct pipe_inode_info *, struct pipe_inode_info *);
+
+ extern unsigned int pipe_max_size, pipe_min_size;
++extern unsigned long pipe_user_pages_hard;
++extern unsigned long pipe_user_pages_soft;
+ int pipe_proc_fn(struct ctl_table *, int, void __user *, size_t *, loff_t *);
+
+
+diff --git a/include/linux/sched.h b/include/linux/sched.h
+index 7d6152a6700c..77ac8b6a5b68 100644
+--- a/include/linux/sched.h
++++ b/include/linux/sched.h
+@@ -756,6 +756,7 @@ struct user_struct {
+ #endif
+ unsigned long locked_shm; /* How many pages of mlocked shm ? */
+ unsigned long unix_inflight; /* How many files in flight in unix sockets */
++ atomic_long_t pipe_bufs; /* how many pages are allocated in pipe buffers */
+
+ #ifdef CONFIG_KEYS
+ struct key *uid_keyring; /* UID specific keyring */
+diff --git a/kernel/sysctl.c b/kernel/sysctl.c
+index c1b26e176aa6..df9eab6928d9 100644
+--- a/kernel/sysctl.c
++++ b/kernel/sysctl.c
+@@ -1668,6 +1668,20 @@ static struct ctl_table fs_table[] = {
+ .proc_handler = &pipe_proc_fn,
+ .extra1 = &pipe_min_size,
+ },
++ {
++ .procname = "pipe-user-pages-hard",
++ .data = &pipe_user_pages_hard,
++ .maxlen = sizeof(pipe_user_pages_hard),
++ .mode = 0644,
++ .proc_handler = proc_doulongvec_minmax,
++ },
++ {
++ .procname = "pipe-user-pages-soft",
++ .data = &pipe_user_pages_soft,
++ .maxlen = sizeof(pipe_user_pages_soft),
++ .mode = 0644,
++ .proc_handler = proc_doulongvec_minmax,
++ },
+ { }
+ };
+
+diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c
+index f95b6f93814b..42155b1af373 100644
+--- a/net/ipv4/netfilter/arp_tables.c
++++ b/net/ipv4/netfilter/arp_tables.c
+@@ -355,11 +355,25 @@ unsigned int arpt_do_table(struct sk_buff *skb,
+ }
+
+ /* All zeroes == unconditional rule. */
+-static inline bool unconditional(const struct arpt_arp *arp)
++static inline bool unconditional(const struct arpt_entry *e)
+ {
+ static const struct arpt_arp uncond;
+
+- return memcmp(arp, &uncond, sizeof(uncond)) == 0;
++ return e->target_offset == sizeof(struct arpt_entry) &&
++ memcmp(&e->arp, &uncond, sizeof(uncond)) == 0;
++}
++
++static bool find_jump_target(const struct xt_table_info *t,
++ const void *entry0,
++ const struct arpt_entry *target)
++{
++ struct arpt_entry *iter;
++
++ xt_entry_foreach(iter, entry0, t->size) {
++ if (iter == target)
++ return true;
++ }
++ return false;
+ }
+
+ /* Figures out from what hook each rule can be called: returns 0 if
+@@ -398,11 +412,10 @@ static int mark_source_chains(const struct xt_table_info *newinfo,
+ |= ((1 << hook) | (1 << NF_ARP_NUMHOOKS));
+
+ /* Unconditional return/END. */
+- if ((e->target_offset == sizeof(struct arpt_entry) &&
++ if ((unconditional(e) &&
+ (strcmp(t->target.u.user.name,
+ XT_STANDARD_TARGET) == 0) &&
+- t->verdict < 0 && unconditional(&e->arp)) ||
+- visited) {
++ t->verdict < 0) || visited) {
+ unsigned int oldpos, size;
+
+ if ((strcmp(t->target.u.user.name,
+@@ -435,6 +448,8 @@ static int mark_source_chains(const struct xt_table_info *newinfo,
+ 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 {
+@@ -454,9 +469,15 @@ static int mark_source_chains(const struct xt_table_info *newinfo,
+ /* This a jump; chase it. */
+ duprintf("Jump rule %u -> %u\n",
+ pos, newpos);
++ e = (struct arpt_entry *)
++ (entry0 + newpos);
++ if (!find_jump_target(newinfo, entry0, e))
++ return 0;
+ } else {
+ /* ... this is a fallthru */
+ newpos = pos + e->next_offset;
++ if (newpos >= newinfo->size)
++ return 0;
+ }
+ e = (struct arpt_entry *)
+ (entry0 + newpos);
+@@ -470,25 +491,6 @@ static int mark_source_chains(const struct xt_table_info *newinfo,
+ return 1;
+ }
+
+-static inline int check_entry(const struct arpt_entry *e, const char *name)
+-{
+- const struct xt_entry_target *t;
+-
+- if (!arp_checkentry(&e->arp)) {
+- duprintf("arp_tables: arp check failed %p %s.\n", e, name);
+- 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;
+-}
+-
+ static inline int check_target(struct arpt_entry *e, const char *name)
+ {
+ struct xt_entry_target *t = arpt_get_target(e);
+@@ -518,10 +520,6 @@ find_check_entry(struct arpt_entry *e, const char *name, unsigned int size)
+ struct xt_target *target;
+ int ret;
+
+- ret = check_entry(e, name);
+- if (ret)
+- return ret;
+-
+ t = arpt_get_target(e);
+ target = xt_request_find_target(NFPROTO_ARP, t->u.user.name,
+ t->u.user.revision);
+@@ -547,7 +545,7 @@ static bool check_underflow(const struct arpt_entry *e)
+ const struct xt_entry_target *t;
+ unsigned int verdict;
+
+- if (!unconditional(&e->arp))
++ if (!unconditional(e))
+ return false;
+ t = arpt_get_target_c(e);
+ if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0)
+@@ -566,9 +564,11 @@ static inline int check_entry_size_and_hooks(struct arpt_entry *e,
+ unsigned int valid_hooks)
+ {
+ unsigned int h;
++ int err;
+
+ if ((unsigned long)e % __alignof__(struct arpt_entry) != 0 ||
+- (unsigned char *)e + sizeof(struct arpt_entry) >= limit) {
++ (unsigned char *)e + sizeof(struct arpt_entry) >= limit ||
++ (unsigned char *)e + e->next_offset > limit) {
+ duprintf("Bad offset %p\n", e);
+ return -EINVAL;
+ }
+@@ -580,6 +580,14 @@ static inline int check_entry_size_and_hooks(struct arpt_entry *e,
+ return -EINVAL;
+ }
+
++ if (!arp_checkentry(&e->arp))
++ return -EINVAL;
++
++ err = xt_check_entry_offsets(e, e->elems, e->target_offset,
++ e->next_offset);
++ if (err)
++ return err;
++
+ /* Check hooks & underflows */
+ for (h = 0; h < NF_ARP_NUMHOOKS; h++) {
+ if (!(valid_hooks & (1 << h)))
+@@ -588,9 +596,9 @@ static inline int check_entry_size_and_hooks(struct arpt_entry *e,
+ newinfo->hook_entry[h] = hook_entries[h];
+ if ((unsigned char *)e - base == underflows[h]) {
+ if (!check_underflow(e)) {
+- pr_err("Underflows must be unconditional and "
+- "use the STANDARD target with "
+- "ACCEPT/DROP\n");
++ pr_debug("Underflows must be unconditional and "
++ "use the STANDARD target with "
++ "ACCEPT/DROP\n");
+ return -EINVAL;
+ }
+ newinfo->underflow[h] = underflows[h];
+@@ -680,10 +688,8 @@ static int translate_table(struct xt_table_info *newinfo, void *entry0,
+ }
+ }
+
+- 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;
+@@ -1076,6 +1082,9 @@ static int do_replace(struct net *net, const void __user *user,
+ /* 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);
+@@ -1116,56 +1125,18 @@ static int do_add_counters(struct net *net, const void __user *user,
+ unsigned int i, curcpu;
+ 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;
+ void *loc_cpu_entry;
+ 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);
+- }
++ paddc = xt_copy_counters_from_user(user, len, &tmp, compat);
++ if (IS_ERR(paddc))
++ return PTR_ERR(paddc);
+
+- 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, 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;
+@@ -1173,7 +1144,7 @@ static int do_add_counters(struct net *net, const void __user *user,
+
+ local_bh_disable();
+ private = t->private;
+- if (private->number != num_counters) {
++ if (private->number != tmp.num_counters) {
+ ret = -EINVAL;
+ goto unlock_up_free;
+ }
+@@ -1199,6 +1170,18 @@ static int do_add_counters(struct net *net, const void __user *user,
+ }
+
+ #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;
+@@ -1207,24 +1190,22 @@ static inline void compat_release_entry(struct compat_arpt_entry *e)
+ 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 char *name)
++ 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 ||
+- (unsigned char *)e + sizeof(struct compat_arpt_entry) >= limit) {
++ (unsigned char *)e + sizeof(struct compat_arpt_entry) >= limit ||
++ (unsigned char *)e + e->next_offset > limit) {
+ duprintf("Bad offset %p, limit = %p\n", e, limit);
+ return -EINVAL;
+ }
+@@ -1236,8 +1217,11 @@ check_compat_entry_size_and_hooks(struct compat_arpt_entry *e,
+ return -EINVAL;
+ }
+
+- /* For purposes of check_entry casting the compat entry is fine */
+- ret = check_entry((struct arpt_entry *)e, name);
++ if (!arp_checkentry(&e->arp))
++ return -EINVAL;
++
++ ret = xt_compat_check_entry_offsets(e, e->elems, e->target_offset,
++ e->next_offset);
+ if (ret)
+ return ret;
+
+@@ -1261,17 +1245,6 @@ check_compat_entry_size_and_hooks(struct compat_arpt_entry *e,
+ 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:
+@@ -1280,18 +1253,17 @@ out:
+ return ret;
+ }
+
+-static int
++static void
+ 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;
+ 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));
+@@ -1312,144 +1284,81 @@ compat_copy_entry_from_user(struct compat_arpt_entry *e, void **dstptr,
+ if ((unsigned char *)de - base < newinfo->underflow[h])
+ newinfo->underflow[h] -= origsize - *size;
+ }
+- 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;
+ void *pos, *entry0, *entry1;
+ struct compat_arpt_entry *iter0;
+- struct arpt_entry *iter1;
++ struct arpt_replace repl;
+ unsigned int size;
+ int ret = 0;
+
+ info = *pinfo;
+ entry0 = *pentry0;
+- size = total_size;
+- info->number = number;
+-
+- /* Init all hooks to impossible value. */
+- for (i = 0; i < NF_ARP_NUMHOOKS; i++) {
+- info->hook_entry[i] = 0xFFFFFFFF;
+- info->underflow[i] = 0xFFFFFFFF;
+- }
++ size = compatr->size;
++ info->number = compatr->num_entries;
+
+ 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);
+ 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)))
+- continue;
+- if (info->hook_entry[i] == 0xFFFFFFFF) {
+- duprintf("Invalid hook entry %u %u\n",
+- i, hook_entries[i]);
+- goto out_unlock;
+- }
+- if (info->underflow[i] == 0xFFFFFFFF) {
+- duprintf("Invalid underflow %u %u\n",
+- i, underflows[i]);
+- goto out_unlock;
+- }
+- }
+-
+ ret = -ENOMEM;
+ newinfo = xt_alloc_table_info(size);
+ 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) {
+- ret = compat_copy_entry_from_user(iter0, &pos, &size,
+- name, newinfo, entry1);
+- if (ret != 0)
+- break;
+- }
++ 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(NFPROTO_ARP);
+ xt_compat_unlock(NFPROTO_ARP);
+- if (ret)
+- goto free_newinfo;
+
+- ret = -ELOOP;
+- if (!mark_source_chains(newinfo, valid_hooks, entry1))
+- goto free_newinfo;
++ memcpy(&repl, compatr, sizeof(*compatr));
+
+- i = 0;
+- xt_entry_foreach(iter1, entry1, newinfo->size) {
+- ret = check_target(iter1, 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;
+@@ -1458,31 +1367,18 @@ static int translate_compat_table(const char *name,
+
+ free_newinfo:
+ xt_free_table_info(newinfo);
+-out:
+- xt_entry_foreach(iter0, entry0, total_size) {
++ 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;
+ }
+
+-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)
+ {
+@@ -1500,6 +1396,9 @@ static int compat_do_replace(struct net *net, void __user *user,
+ 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);
+@@ -1513,10 +1412,7 @@ static int compat_do_replace(struct net *net, void __user *user,
+ 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/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c
+index 99e810f84671..82b6bc4f9167 100644
+--- a/net/ipv4/netfilter/ip_tables.c
++++ b/net/ipv4/netfilter/ip_tables.c
+@@ -168,11 +168,12 @@ get_entry(const void *base, unsigned int offset)
+
+ /* All zeroes == unconditional rule. */
+ /* Mildly perf critical (only if packet tracing is on) */
+-static inline bool unconditional(const struct ipt_ip *ip)
++static inline bool unconditional(const struct ipt_entry *e)
+ {
+ static const struct ipt_ip uncond;
+
+- return memcmp(ip, &uncond, sizeof(uncond)) == 0;
++ return e->target_offset == sizeof(struct ipt_entry) &&
++ memcmp(&e->ip, &uncond, sizeof(uncond)) == 0;
+ #undef FWINV
+ }
+
+@@ -229,11 +230,10 @@ get_chainname_rulenum(const struct ipt_entry *s, const struct ipt_entry *e,
+ } else if (s == e) {
+ (*rulenum)++;
+
+- if (s->target_offset == sizeof(struct ipt_entry) &&
++ if (unconditional(s) &&
+ strcmp(t->target.u.kernel.target->name,
+ XT_STANDARD_TARGET) == 0 &&
+- t->verdict < 0 &&
+- unconditional(&s->ip)) {
++ t->verdict < 0) {
+ /* Tail of chains: STANDARD target (return/policy) */
+ *comment = *chainname == hookname
+ ? comments[NF_IP_TRACE_COMMENT_POLICY]
+@@ -439,6 +439,19 @@ ipt_do_table(struct sk_buff *skb,
+ #endif
+ }
+
++static bool find_jump_target(const struct xt_table_info *t,
++ const void *entry0,
++ const struct ipt_entry *target)
++{
++ struct ipt_entry *iter;
++
++ xt_entry_foreach(iter, entry0, 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
+@@ -472,11 +485,10 @@ mark_source_chains(const struct xt_table_info *newinfo,
+ e->comefrom |= ((1 << hook) | (1 << NF_INET_NUMHOOKS));
+
+ /* Unconditional return/END. */
+- if ((e->target_offset == sizeof(struct ipt_entry) &&
++ if ((unconditional(e) &&
+ (strcmp(t->target.u.user.name,
+ XT_STANDARD_TARGET) == 0) &&
+- t->verdict < 0 && unconditional(&e->ip)) ||
+- visited) {
++ t->verdict < 0) || visited) {
+ unsigned int oldpos, size;
+
+ if ((strcmp(t->target.u.user.name,
+@@ -517,6 +529,8 @@ mark_source_chains(const struct xt_table_info *newinfo,
+ 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 {
+@@ -535,9 +549,15 @@ mark_source_chains(const struct xt_table_info *newinfo,
+ /* This a jump; chase it. */
+ duprintf("Jump rule %u -> %u\n",
+ pos, newpos);
++ e = (struct ipt_entry *)
++ (entry0 + newpos);
++ if (!find_jump_target(newinfo, entry0, e))
++ return 0;
+ } else {
+ /* ... this is a fallthru */
+ newpos = pos + e->next_offset;
++ if (newpos >= newinfo->size)
++ return 0;
+ }
+ e = (struct ipt_entry *)
+ (entry0 + newpos);
+@@ -565,27 +585,6 @@ static void cleanup_match(struct xt_entry_match *m, struct net *net)
+ }
+
+ static int
+-check_entry(const struct ipt_entry *e, const char *name)
+-{
+- const struct xt_entry_target *t;
+-
+- if (!ip_checkentry(&e->ip)) {
+- duprintf("ip check failed %p %s.\n", e, name);
+- 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;
+-}
+-
+-static int
+ check_match(struct xt_entry_match *m, struct xt_mtchk_param *par)
+ {
+ const struct ipt_ip *ip = par->entryinfo;
+@@ -662,10 +661,6 @@ find_check_entry(struct ipt_entry *e, struct net *net, const char *name,
+ struct xt_mtchk_param mtpar;
+ struct xt_entry_match *ematch;
+
+- ret = check_entry(e, name);
+- if (ret)
+- return ret;
+-
+ j = 0;
+ mtpar.net = net;
+ mtpar.table = name;
+@@ -709,7 +704,7 @@ static bool check_underflow(const struct ipt_entry *e)
+ const struct xt_entry_target *t;
+ unsigned int verdict;
+
+- if (!unconditional(&e->ip))
++ if (!unconditional(e))
+ return false;
+ t = ipt_get_target_c(e);
+ if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0)
+@@ -729,9 +724,11 @@ check_entry_size_and_hooks(struct ipt_entry *e,
+ unsigned int valid_hooks)
+ {
+ unsigned int h;
++ int err;
+
+ if ((unsigned long)e % __alignof__(struct ipt_entry) != 0 ||
+- (unsigned char *)e + sizeof(struct ipt_entry) >= limit) {
++ (unsigned char *)e + sizeof(struct ipt_entry) >= limit ||
++ (unsigned char *)e + e->next_offset > limit) {
+ duprintf("Bad offset %p\n", e);
+ return -EINVAL;
+ }
+@@ -743,6 +740,14 @@ check_entry_size_and_hooks(struct ipt_entry *e,
+ return -EINVAL;
+ }
+
++ if (!ip_checkentry(&e->ip))
++ return -EINVAL;
++
++ err = xt_check_entry_offsets(e, e->elems, e->target_offset,
++ e->next_offset);
++ if (err)
++ return err;
++
+ /* Check hooks & underflows */
+ for (h = 0; h < NF_INET_NUMHOOKS; h++) {
+ if (!(valid_hooks & (1 << h)))
+@@ -751,9 +756,9 @@ check_entry_size_and_hooks(struct ipt_entry *e,
+ newinfo->hook_entry[h] = hook_entries[h];
+ if ((unsigned char *)e - base == underflows[h]) {
+ if (!check_underflow(e)) {
+- pr_err("Underflows must be unconditional and "
+- "use the STANDARD target with "
+- "ACCEPT/DROP\n");
++ pr_debug("Underflows must be unconditional and "
++ "use the STANDARD target with "
++ "ACCEPT/DROP\n");
+ return -EINVAL;
+ }
+ newinfo->underflow[h] = underflows[h];
+@@ -1263,6 +1268,9 @@ do_replace(struct net *net, const void __user *user, unsigned int len)
+ /* 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);
+@@ -1304,56 +1312,18 @@ do_add_counters(struct net *net, const void __user *user,
+ unsigned int i, curcpu;
+ 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;
+ void *loc_cpu_entry;
+ 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;
+- }
++ paddc = xt_copy_counters_from_user(user, len, &tmp, compat);
++ if (IS_ERR(paddc))
++ return PTR_ERR(paddc);
+
+- 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_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;
+@@ -1361,7 +1331,7 @@ do_add_counters(struct net *net, const void __user *user,
+
+ local_bh_disable();
+ private = t->private;
+- if (private->number != num_counters) {
++ if (private->number != tmp.num_counters) {
+ ret = -EINVAL;
+ goto unlock_up_free;
+ }
+@@ -1440,7 +1410,6 @@ compat_copy_entry_to_user(struct ipt_entry *e, void __user **dstptr,
+
+ static int
+ compat_find_calc_match(struct xt_entry_match *m,
+- const char *name,
+ const struct ipt_ip *ip,
+ unsigned int hookmask,
+ int *size)
+@@ -1476,21 +1445,19 @@ check_compat_entry_size_and_hooks(struct compat_ipt_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 char *name)
++ 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 ||
+- (unsigned char *)e + sizeof(struct compat_ipt_entry) >= limit) {
++ (unsigned char *)e + sizeof(struct compat_ipt_entry) >= limit ||
++ (unsigned char *)e + e->next_offset > limit) {
+ duprintf("Bad offset %p, limit = %p\n", e, limit);
+ return -EINVAL;
+ }
+@@ -1502,8 +1469,11 @@ check_compat_entry_size_and_hooks(struct compat_ipt_entry *e,
+ return -EINVAL;
+ }
+
+- /* For purposes of check_entry casting the compat entry is fine */
+- ret = check_entry((struct ipt_entry *)e, name);
++ if (!ip_checkentry(&e->ip))
++ return -EINVAL;
++
++ ret = xt_compat_check_entry_offsets(e, e->elems,
++ e->target_offset, e->next_offset);
+ if (ret)
+ return ret;
+
+@@ -1511,8 +1481,8 @@ check_compat_entry_size_and_hooks(struct compat_ipt_entry *e,
+ 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;
+@@ -1535,17 +1505,6 @@ check_compat_entry_size_and_hooks(struct compat_ipt_entry *e,
+ 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:
+@@ -1559,19 +1518,18 @@ release_matches:
+ return ret;
+ }
+
+-static int
++static void
+ 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;
+ 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));
+@@ -1580,198 +1538,104 @@ compat_copy_entry_from_user(struct compat_ipt_entry *e, void **dstptr,
+ *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;
+ 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;
+ if ((unsigned char *)de - base < newinfo->underflow[h])
+ newinfo->underflow[h] -= origsize - *size;
+ }
+- return ret;
+-}
+-
+-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,
+- 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;
+ void *pos, *entry0, *entry1;
+ struct compat_ipt_entry *iter0;
+- struct ipt_entry *iter1;
++ struct ipt_replace repl;
+ unsigned int size;
+ int ret;
+
+ info = *pinfo;
+ entry0 = *pentry0;
+- size = total_size;
+- info->number = number;
+-
+- /* Init all hooks to impossible value. */
+- for (i = 0; i < NF_INET_NUMHOOKS; i++) {
+- info->hook_entry[i] = 0xFFFFFFFF;
+- info->underflow[i] = 0xFFFFFFFF;
+- }
++ size = compatr->size;
++ info->number = compatr->num_entries;
+
+ 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);
+ 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)))
+- continue;
+- if (info->hook_entry[i] == 0xFFFFFFFF) {
+- duprintf("Invalid hook entry %u %u\n",
+- i, hook_entries[i]);
+- goto out_unlock;
+- }
+- if (info->underflow[i] == 0xFFFFFFFF) {
+- duprintf("Invalid underflow %u %u\n",
+- i, underflows[i]);
+- goto out_unlock;
+- }
+- }
+-
+ ret = -ENOMEM;
+ newinfo = xt_alloc_table_info(size);
+ 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];
++ newinfo->hook_entry[i] = compatr->hook_entry[i];
++ newinfo->underflow[i] = compatr->underflow[i];
+ }
+ entry1 = newinfo->entries[raw_smp_processor_id()];
+ pos = entry1;
+- size = total_size;
+- xt_entry_foreach(iter0, entry0, total_size) {
+- ret = compat_copy_entry_from_user(iter0, &pos, &size,
+- name, newinfo, entry1);
+- if (ret != 0)
+- break;
+- }
++ 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.
++ * 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);
+- if (ret)
+- goto free_newinfo;
+
+- ret = -ELOOP;
+- if (!mark_source_chains(newinfo, 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, 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;
+@@ -1780,17 +1644,16 @@ translate_compat_table(struct net *net,
+
+ free_newinfo:
+ xt_free_table_info(newinfo);
+-out:
+- xt_entry_foreach(iter0, entry0, total_size) {
++ 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
+@@ -1810,6 +1673,9 @@ compat_do_replace(struct net *net, void __user *user, unsigned int len)
+ 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);
+@@ -1824,10 +1690,7 @@ compat_do_replace(struct net *net, void __user *user, unsigned int len)
+ 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/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c
+index e080fbbbc0e5..67d5e86dd84c 100644
+--- a/net/ipv6/netfilter/ip6_tables.c
++++ b/net/ipv6/netfilter/ip6_tables.c
+@@ -195,11 +195,12 @@ get_entry(const void *base, unsigned int offset)
+
+ /* All zeroes == unconditional rule. */
+ /* Mildly perf critical (only if packet tracing is on) */
+-static inline bool unconditional(const struct ip6t_ip6 *ipv6)
++static inline bool unconditional(const struct ip6t_entry *e)
+ {
+ static const struct ip6t_ip6 uncond;
+
+- return memcmp(ipv6, &uncond, sizeof(uncond)) == 0;
++ return e->target_offset == sizeof(struct ip6t_entry) &&
++ memcmp(&e->ipv6, &uncond, sizeof(uncond)) == 0;
+ }
+
+ static inline const struct xt_entry_target *
+@@ -255,11 +256,10 @@ get_chainname_rulenum(const struct ip6t_entry *s, const struct ip6t_entry *e,
+ } else if (s == e) {
+ (*rulenum)++;
+
+- if (s->target_offset == sizeof(struct ip6t_entry) &&
++ if (unconditional(s) &&
+ strcmp(t->target.u.kernel.target->name,
+ XT_STANDARD_TARGET) == 0 &&
+- t->verdict < 0 &&
+- unconditional(&s->ipv6)) {
++ t->verdict < 0) {
+ /* Tail of chains: STANDARD target (return/policy) */
+ *comment = *chainname == hookname
+ ? comments[NF_IP6_TRACE_COMMENT_POLICY]
+@@ -449,6 +449,19 @@ ip6t_do_table(struct sk_buff *skb,
+ #endif
+ }
+
++static bool find_jump_target(const struct xt_table_info *t,
++ const void *entry0,
++ const struct ip6t_entry *target)
++{
++ struct ip6t_entry *iter;
++
++ xt_entry_foreach(iter, entry0, 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
+@@ -482,11 +495,10 @@ mark_source_chains(const struct xt_table_info *newinfo,
+ e->comefrom |= ((1 << hook) | (1 << NF_INET_NUMHOOKS));
+
+ /* Unconditional return/END. */
+- if ((e->target_offset == sizeof(struct ip6t_entry) &&
++ if ((unconditional(e) &&
+ (strcmp(t->target.u.user.name,
+ XT_STANDARD_TARGET) == 0) &&
+- t->verdict < 0 &&
+- unconditional(&e->ipv6)) || visited) {
++ t->verdict < 0) || visited) {
+ unsigned int oldpos, size;
+
+ if ((strcmp(t->target.u.user.name,
+@@ -527,6 +539,8 @@ mark_source_chains(const struct xt_table_info *newinfo,
+ 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 {
+@@ -545,9 +559,15 @@ mark_source_chains(const struct xt_table_info *newinfo,
+ /* This a jump; chase it. */
+ duprintf("Jump rule %u -> %u\n",
+ pos, newpos);
++ e = (struct ip6t_entry *)
++ (entry0 + newpos);
++ if (!find_jump_target(newinfo, entry0, e))
++ return 0;
+ } else {
+ /* ... this is a fallthru */
+ newpos = pos + e->next_offset;
++ if (newpos >= newinfo->size)
++ return 0;
+ }
+ e = (struct ip6t_entry *)
+ (entry0 + newpos);
+@@ -574,27 +594,6 @@ static void cleanup_match(struct xt_entry_match *m, struct net *net)
+ module_put(par.match->me);
+ }
+
+-static int
+-check_entry(const struct ip6t_entry *e, const char *name)
+-{
+- const struct xt_entry_target *t;
+-
+- if (!ip6_checkentry(&e->ipv6)) {
+- duprintf("ip_tables: ip check failed %p %s.\n", e, name);
+- 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;
+-}
+-
+ static int check_match(struct xt_entry_match *m, struct xt_mtchk_param *par)
+ {
+ const struct ip6t_ip6 *ipv6 = par->entryinfo;
+@@ -673,10 +672,6 @@ find_check_entry(struct ip6t_entry *e, struct net *net, const char *name,
+ struct xt_mtchk_param mtpar;
+ struct xt_entry_match *ematch;
+
+- ret = check_entry(e, name);
+- if (ret)
+- return ret;
+-
+ j = 0;
+ mtpar.net = net;
+ mtpar.table = name;
+@@ -720,7 +715,7 @@ static bool check_underflow(const struct ip6t_entry *e)
+ const struct xt_entry_target *t;
+ unsigned int verdict;
+
+- if (!unconditional(&e->ipv6))
++ if (!unconditional(e))
+ return false;
+ t = ip6t_get_target_c(e);
+ if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0)
+@@ -740,9 +735,11 @@ check_entry_size_and_hooks(struct ip6t_entry *e,
+ unsigned int valid_hooks)
+ {
+ unsigned int h;
++ int err;
+
+ if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0 ||
+- (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
++ (unsigned char *)e + sizeof(struct ip6t_entry) >= limit ||
++ (unsigned char *)e + e->next_offset > limit) {
+ duprintf("Bad offset %p\n", e);
+ return -EINVAL;
+ }
+@@ -754,6 +751,14 @@ check_entry_size_and_hooks(struct ip6t_entry *e,
+ return -EINVAL;
+ }
+
++ if (!ip6_checkentry(&e->ipv6))
++ return -EINVAL;
++
++ err = xt_check_entry_offsets(e, e->elems, e->target_offset,
++ e->next_offset);
++ if (err)
++ return err;
++
+ /* Check hooks & underflows */
+ for (h = 0; h < NF_INET_NUMHOOKS; h++) {
+ if (!(valid_hooks & (1 << h)))
+@@ -762,9 +767,9 @@ check_entry_size_and_hooks(struct ip6t_entry *e,
+ newinfo->hook_entry[h] = hook_entries[h];
+ if ((unsigned char *)e - base == underflows[h]) {
+ if (!check_underflow(e)) {
+- pr_err("Underflows must be unconditional and "
+- "use the STANDARD target with "
+- "ACCEPT/DROP\n");
++ pr_debug("Underflows must be unconditional and "
++ "use the STANDARD target with "
++ "ACCEPT/DROP\n");
+ return -EINVAL;
+ }
+ newinfo->underflow[h] = underflows[h];
+@@ -1273,6 +1278,9 @@ do_replace(struct net *net, const void __user *user, unsigned int len)
+ /* 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);
+@@ -1314,56 +1322,17 @@ do_add_counters(struct net *net, const void __user *user, unsigned int len,
+ unsigned int i, curcpu;
+ 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;
+ const void *loc_cpu_entry;
+ 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;
+@@ -1372,7 +1341,7 @@ do_add_counters(struct net *net, const void __user *user, unsigned int len,
+
+ local_bh_disable();
+ private = t->private;
+- if (private->number != num_counters) {
++ if (private->number != tmp.num_counters) {
+ ret = -EINVAL;
+ goto unlock_up_free;
+ }
+@@ -1452,7 +1421,6 @@ compat_copy_entry_to_user(struct ip6t_entry *e, void __user **dstptr,
+
+ static int
+ compat_find_calc_match(struct xt_entry_match *m,
+- const char *name,
+ const struct ip6t_ip6 *ipv6,
+ unsigned int hookmask,
+ int *size)
+@@ -1488,21 +1456,19 @@ check_compat_entry_size_and_hooks(struct compat_ip6t_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 char *name)
++ 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 ||
+- (unsigned char *)e + sizeof(struct compat_ip6t_entry) >= limit) {
++ (unsigned char *)e + sizeof(struct compat_ip6t_entry) >= limit ||
++ (unsigned char *)e + e->next_offset > limit) {
+ duprintf("Bad offset %p, limit = %p\n", e, limit);
+ return -EINVAL;
+ }
+@@ -1514,8 +1480,11 @@ check_compat_entry_size_and_hooks(struct compat_ip6t_entry *e,
+ return -EINVAL;
+ }
+
+- /* For purposes of check_entry casting the compat entry is fine */
+- ret = check_entry((struct ip6t_entry *)e, name);
++ if (!ip6_checkentry(&e->ipv6))
++ return -EINVAL;
++
++ ret = xt_compat_check_entry_offsets(e, e->elems,
++ e->target_offset, e->next_offset);
+ if (ret)
+ return ret;
+
+@@ -1523,8 +1492,8 @@ check_compat_entry_size_and_hooks(struct compat_ip6t_entry *e,
+ 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;
+@@ -1547,17 +1516,6 @@ check_compat_entry_size_and_hooks(struct compat_ip6t_entry *e,
+ 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:
+@@ -1571,18 +1529,17 @@ release_matches:
+ return ret;
+ }
+
+-static int
++static void
+ 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;
+ 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));
+@@ -1591,11 +1548,9 @@ compat_copy_entry_from_user(struct compat_ip6t_entry *e, void **dstptr,
+ *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);
+@@ -1607,181 +1562,82 @@ compat_copy_entry_from_user(struct compat_ip6t_entry *e, void **dstptr,
+ 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,
+- 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,
+- 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;
+ void *pos, *entry0, *entry1;
+ struct compat_ip6t_entry *iter0;
+- struct ip6t_entry *iter1;
++ struct ip6t_replace repl;
+ unsigned int size;
+ int ret = 0;
+
+ info = *pinfo;
+ entry0 = *pentry0;
+- size = total_size;
+- info->number = number;
+-
+- /* Init all hooks to impossible value. */
+- for (i = 0; i < NF_INET_NUMHOOKS; i++) {
+- info->hook_entry[i] = 0xFFFFFFFF;
+- info->underflow[i] = 0xFFFFFFFF;
+- }
++ size = compatr->size;
++ info->number = compatr->num_entries;
+
+ 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);
+ 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)))
+- continue;
+- if (info->hook_entry[i] == 0xFFFFFFFF) {
+- duprintf("Invalid hook entry %u %u\n",
+- i, hook_entries[i]);
+- goto out_unlock;
+- }
+- if (info->underflow[i] == 0xFFFFFFFF) {
+- duprintf("Invalid underflow %u %u\n",
+- i, underflows[i]);
+- goto out_unlock;
+- }
+- }
+-
+ ret = -ENOMEM;
+ newinfo = xt_alloc_table_info(size);
+ 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];
++ newinfo->hook_entry[i] = compatr->hook_entry[i];
++ newinfo->underflow[i] = compatr->underflow[i];
+ }
+ entry1 = newinfo->entries[raw_smp_processor_id()];
+ pos = entry1;
+- size = total_size;
+- xt_entry_foreach(iter0, entry0, total_size) {
+- ret = compat_copy_entry_from_user(iter0, &pos, &size,
+- name, newinfo, entry1);
+- if (ret != 0)
+- break;
+- }
++ 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);
+- if (ret)
+- goto free_newinfo;
+
+- ret = -ELOOP;
+- if (!mark_source_chains(newinfo, 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, 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;
+@@ -1790,17 +1646,16 @@ translate_compat_table(struct net *net,
+
+ free_newinfo:
+ xt_free_table_info(newinfo);
+-out:
+- xt_entry_foreach(iter0, entry0, total_size) {
++ 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
+@@ -1820,6 +1675,9 @@ compat_do_replace(struct net *net, void __user *user, unsigned int len)
+ 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);
+@@ -1834,10 +1692,7 @@ compat_do_replace(struct net *net, void __user *user, unsigned int len)
+ 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/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
+index b50ae296537d..038304f796d8 100644
+--- a/net/ipv6/tcp_ipv6.c
++++ b/net/ipv6/tcp_ipv6.c
+@@ -1783,7 +1783,9 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i)
+ destp = ntohs(inet->inet_dport);
+ srcp = ntohs(inet->inet_sport);
+
+- if (icsk->icsk_pending == ICSK_TIME_RETRANS) {
++ if (icsk->icsk_pending == ICSK_TIME_RETRANS ||
++ icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS ||
++ icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) {
+ timer_active = 1;
+ timer_expires = icsk->icsk_timeout;
+ } else if (icsk->icsk_pending == ICSK_TIME_PROBE0) {
+diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c
+index 227aa11e8409..1393af786e15 100644
+--- a/net/netfilter/x_tables.c
++++ b/net/netfilter/x_tables.c
+@@ -435,6 +435,47 @@ int xt_check_match(struct xt_mtchk_param *par,
+ }
+ 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)
+ {
+@@ -504,13 +545,14 @@ int xt_compat_match_offset(const struct xt_match *match)
+ }
+ 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;
+ 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));
+@@ -524,10 +566,12 @@ int xt_compat_match_from_user(struct xt_entry_match *m, void **dstptr,
+
+ 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;
+- return 0;
+ }
+ EXPORT_SYMBOL_GPL(xt_compat_match_from_user);
+
+@@ -558,8 +602,125 @@ int xt_compat_match_to_user(const struct xt_entry_match *m,
+ return 0;
+ }
+ 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;
++};
++
++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;
++
++ 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;
++
++ if (strcmp(t->u.user.name, XT_STANDARD_TARGET) == 0 &&
++ COMPAT_XT_ALIGN(target_offset + sizeof(struct compat_xt_standard_target)) != next_offset)
++ return -EINVAL;
++
++ /* 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 */
+
++/**
++ * 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 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, 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,
++ 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;
++
++ 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;
++
++ if (strcmp(t->u.user.name, XT_STANDARD_TARGET) == 0 &&
++ XT_ALIGN(target_offset + sizeof(struct xt_standard_target)) != next_offset)
++ return -EINVAL;
++
++ return xt_check_entry_match(elems, base + target_offset,
++ __alignof__(struct xt_entry_match));
++}
++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)
+ {
+@@ -610,6 +771,80 @@ int xt_check_target(struct xt_tgchk_param *par,
+ }
+ 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)
+ {
+@@ -625,6 +860,7 @@ void xt_compat_target_from_user(struct xt_entry_target *t, void **dstptr,
+ 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));
+@@ -638,6 +874,9 @@ void xt_compat_target_from_user(struct xt_entry_target *t, void **dstptr,
+
+ 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/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
+index fd9373c9f057..d6231f219edc 100644
+--- a/net/netlink/af_netlink.c
++++ b/net/netlink/af_netlink.c
+@@ -2651,6 +2651,7 @@ static int netlink_dump(struct sock *sk)
+ struct netlink_callback *cb;
+ struct sk_buff *skb = NULL;
+ struct nlmsghdr *nlh;
++ struct module *module;
+ int len, err = -ENOBUFS;
+ int alloc_size;
+
+@@ -2700,9 +2701,11 @@ static int netlink_dump(struct sock *sk)
+ cb->done(cb);
+
+ nlk->cb_running = false;
++ module = cb->module;
++ skb = cb->skb;
+ mutex_unlock(nlk->cb_mutex);
+- module_put(cb->module);
+- consume_skb(cb->skb);
++ module_put(module);
++ consume_skb(skb);
+ return 0;
+
+ errout_skb:
+diff --git a/net/wireless/wext-core.c b/net/wireless/wext-core.c
+index 87dd619fb2e9..1c9a505b7019 100644
+--- a/net/wireless/wext-core.c
++++ b/net/wireless/wext-core.c
+@@ -954,8 +954,29 @@ static int wireless_process_ioctl(struct net *net, struct ifreq *ifr,
+ return private(dev, iwr, cmd, info, handler);
+ }
+ /* Old driver API : call driver ioctl handler */
+- if (dev->netdev_ops->ndo_do_ioctl)
+- return dev->netdev_ops->ndo_do_ioctl(dev, ifr, cmd);
++ if (dev->netdev_ops->ndo_do_ioctl) {
++#ifdef CONFIG_COMPAT
++ if (info->flags & IW_REQUEST_FLAG_COMPAT) {
++ int ret = 0;
++ struct iwreq iwr_lcl;
++ struct compat_iw_point *iwp_compat = (void *) &iwr->u.data;
++
++ memcpy(&iwr_lcl, iwr, sizeof(struct iwreq));
++ iwr_lcl.u.data.pointer = compat_ptr(iwp_compat->pointer);
++ iwr_lcl.u.data.length = iwp_compat->length;
++ iwr_lcl.u.data.flags = iwp_compat->flags;
++
++ ret = dev->netdev_ops->ndo_do_ioctl(dev, (void *) &iwr_lcl, cmd);
++
++ iwp_compat->pointer = ptr_to_compat(iwr_lcl.u.data.pointer);
++ iwp_compat->length = iwr_lcl.u.data.length;
++ iwp_compat->flags = iwr_lcl.u.data.flags;
++
++ return ret;
++ } else
++#endif
++ return dev->netdev_ops->ndo_do_ioctl(dev, ifr, cmd);
++ }
+ return -EOPNOTSUPP;
+ }
+