diff options
| -rw-r--r-- | Documentation/sysctl/kernel.txt | 42 | ||||
| -rw-r--r-- | android/configs/android-base.cfg | 1 | ||||
| -rw-r--r-- | arch/arm64/configs/flounder_defconfig | 1 | ||||
| -rw-r--r-- | drivers/gpu/nvgpu/gk20a/pmu_gk20a.c | 27 | ||||
| -rw-r--r-- | drivers/media/platform/tegra/nvavp/nvavp_dev.c | 43 | ||||
| -rw-r--r-- | drivers/net/usb/usbnet.c | 7 | ||||
| -rw-r--r-- | drivers/usb/core/devio.c | 9 | ||||
| -rw-r--r-- | include/linux/ipv6.h | 2 | ||||
| -rw-r--r-- | include/linux/perf_event.h | 5 | ||||
| -rw-r--r-- | include/net/ipv6.h | 21 | ||||
| -rw-r--r-- | kernel/events/core.c | 8 | ||||
| -rw-r--r-- | net/core/rtnetlink.c | 18 | ||||
| -rw-r--r-- | net/dccp/ipv6.c | 33 | ||||
| -rw-r--r-- | net/ipv6/af_inet6.c | 13 | ||||
| -rw-r--r-- | net/ipv6/datagram.c | 4 | ||||
| -rw-r--r-- | net/ipv6/exthdrs.c | 3 | ||||
| -rw-r--r-- | net/ipv6/inet6_connection_sock.c | 11 | ||||
| -rw-r--r-- | net/ipv6/ipv6_sockglue.c | 36 | ||||
| -rw-r--r-- | net/ipv6/raw.c | 8 | ||||
| -rw-r--r-- | net/ipv6/syncookies.c | 2 | ||||
| -rw-r--r-- | net/ipv6/tcp_ipv6.c | 28 | ||||
| -rw-r--r-- | net/ipv6/udp.c | 8 | ||||
| -rw-r--r-- | net/l2tp/l2tp_ip6.c | 8 | ||||
| -rw-r--r-- | net/socket.c | 4 | ||||
| -rw-r--r-- | security/Kconfig | 9 | ||||
| -rw-r--r-- | sound/core/compress_offload.c | 2 | ||||
| -rw-r--r-- | sound/core/timer.c | 35 | ||||
| -rw-r--r-- | tools/perf/util/evsel.c | 17 |
28 files changed, 292 insertions, 113 deletions
diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt index 9b34b168507..e2cb8a18c46 100644 --- a/Documentation/sysctl/kernel.txt +++ b/Documentation/sysctl/kernel.txt @@ -49,8 +49,9 @@ show up in /proc/sys/kernel: - overflowuid - panic - panic_on_oops -- panic_on_unrecovered_nmi - panic_on_stackoverflow +- panic_on_unrecovered_nmi +- perf_event_paranoid - pid_max - powersave-nap [ PPC only ] - printk @@ -402,19 +403,6 @@ the recommended setting is 60. ============================================================== -panic_on_unrecovered_nmi: - -The default Linux behaviour on an NMI of either memory or unknown is -to continue operation. For many environments such as scientific -computing it is preferable that the box is taken out and the error -dealt with than an uncorrected parity/ECC error get propagated. - -A small number of systems do generate NMI's for bizarre random reasons -such as power management so the default is off. That sysctl works like -the existing panic controls already in that directory. - -============================================================== - panic_on_oops: Controls the kernel's behaviour when an oops or BUG is encountered. @@ -438,6 +426,32 @@ This file shows up if CONFIG_DEBUG_STACKOVERFLOW is enabled. ============================================================== +panic_on_unrecovered_nmi: + +The default Linux behaviour on an NMI of either memory or unknown is +to continue operation. For many environments such as scientific +computing it is preferable that the box is taken out and the error +dealt with than an uncorrected parity/ECC error get propagated. + +A small number of systems do generate NMI's for bizarre random reasons +such as power management so the default is off. That sysctl works like +the existing panic controls already in that directory. + +============================================================== + +perf_event_paranoid: + +Controls use of the performance events system by unprivileged +users (without CAP_SYS_ADMIN). The default value is 3 if +CONFIG_SECURITY_PERF_EVENTS_RESTRICT is set, or 1 otherwise. + + -1: Allow use of (almost) all events by all users +>=0: Disallow raw tracepoint access by users without CAP_IOC_LOCK +>=1: Disallow CPU event access by users without CAP_SYS_ADMIN +>=2: Disallow kernel profiling by users without CAP_SYS_ADMIN +>=3: Disallow all event access by users without CAP_SYS_ADMIN + +============================================================== pid_max: diff --git a/android/configs/android-base.cfg b/android/configs/android-base.cfg index f288006650e..9ef67e0c3b5 100644 --- a/android/configs/android-base.cfg +++ b/android/configs/android-base.cfg @@ -138,6 +138,7 @@ CONFIG_RTC_CLASS=y CONFIG_RT_GROUP_SCHED=y CONFIG_SECURITY=y CONFIG_SECURITY_NETWORK=y +CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y CONFIG_SECURITY_SELINUX=y CONFIG_SND=y CONFIG_SOUND=y diff --git a/arch/arm64/configs/flounder_defconfig b/arch/arm64/configs/flounder_defconfig index 6c1c8886069..c83517d1083 100644 --- a/arch/arm64/configs/flounder_defconfig +++ b/arch/arm64/configs/flounder_defconfig @@ -589,6 +589,7 @@ CONFIG_DEBUG_ATOMIC_SLEEP=y CONFIG_DEBUG_INFO=y CONFIG_ENABLE_DEFAULT_TRACERS=y CONFIG_DYNAMIC_DEBUG=y +CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y CONFIG_SECURITY=y CONFIG_SECURITY_NETWORK=y CONFIG_SECURITY_SELINUX=y diff --git a/drivers/gpu/nvgpu/gk20a/pmu_gk20a.c b/drivers/gpu/nvgpu/gk20a/pmu_gk20a.c index 5e8c687abfd..1c0e5014fb6 100644 --- a/drivers/gpu/nvgpu/gk20a/pmu_gk20a.c +++ b/drivers/gpu/nvgpu/gk20a/pmu_gk20a.c @@ -1300,11 +1300,18 @@ static bool pmu_queue_is_empty(struct pmu_gk20a *pmu, { u32 head, tail; - pmu_queue_head(pmu, queue, &head, QUEUE_GET); + if (pmu_queue_head(pmu, queue, &head, QUEUE_GET)) { + gk20a_err(dev_from_gk20a(pmu->g), + "pmu_queue_is_empty: head queue index out of range"); + return false; + } if (queue->opened && queue->oflag == OFLAG_READ) tail = queue->position; - else - pmu_queue_tail(pmu, queue, &tail, QUEUE_GET); + else if (pmu_queue_tail(pmu, queue, &tail, QUEUE_GET)) { + gk20a_err(dev_from_gk20a(pmu->g), + "pmu_queue_is_empty: tail queue index out of range"); + return false; + } return head == tail; } @@ -1317,8 +1324,12 @@ static bool pmu_queue_has_room(struct pmu_gk20a *pmu, size = ALIGN(size, QUEUE_ALIGNMENT); - pmu_queue_head(pmu, queue, &head, QUEUE_GET); - pmu_queue_tail(pmu, queue, &tail, QUEUE_GET); + if (pmu_queue_head(pmu, queue, &head, QUEUE_GET) || + pmu_queue_tail(pmu, queue, &tail, QUEUE_GET)) { + gk20a_err(dev_from_gk20a(pmu->g), + "pmu_queue_has_room: queue index out of range"); + return false; + } if (head >= tail) { free = queue->offset + queue->size - head; @@ -1369,7 +1380,11 @@ static int pmu_queue_pop(struct pmu_gk20a *pmu, return -EINVAL; } - pmu_queue_head(pmu, queue, &head, QUEUE_GET); + if (pmu_queue_head(pmu, queue, &head, QUEUE_GET)) { + gk20a_err(dev_from_gk20a(pmu->g), + "pmu_queue_pop: head queue index out of range"); + return -EINVAL; + } tail = queue->position; if (head == tail) diff --git a/drivers/media/platform/tegra/nvavp/nvavp_dev.c b/drivers/media/platform/tegra/nvavp/nvavp_dev.c index c16d8aa2a38..c200038497b 100644 --- a/drivers/media/platform/tegra/nvavp/nvavp_dev.c +++ b/drivers/media/platform/tegra/nvavp/nvavp_dev.c @@ -139,6 +139,7 @@ struct nvavp_info { int mbox_from_avp_pend_irq; struct mutex open_lock; + struct mutex submit_lock; int refcount; int video_initialized; int video_refcnt; @@ -1031,6 +1032,7 @@ static int nvavp_pushbuffer_update(struct nvavp_info *nvavp, u32 phys_addr, u32 wordcount = 0; u32 index, value = -1; int ret = 0; + u32 max_index = 0; mutex_lock(&nvavp->open_lock); nvavp_runtime_get(nvavp); @@ -1045,7 +1047,9 @@ static int nvavp_pushbuffer_update(struct nvavp_info *nvavp, u32 phys_addr, mutex_lock(&channel_info->pushbuffer_lock); /* check for pushbuffer wrapping */ - if (channel_info->pushbuf_index >= channel_info->pushbuf_fence) + max_index = channel_info->pushbuf_fence; + max_index = ext_ucode_flag ? max_index : max_index - (sizeof(u32) * 4); + if (channel_info->pushbuf_index >= max_index) channel_info->pushbuf_index = 0; if (!ext_ucode_flag) { @@ -1678,24 +1682,31 @@ static int nvavp_pushbuffer_submit_ioctl(struct file *filp, unsigned int cmd, syncpt.id = NVSYNCPT_INVALID; syncpt.value = 0; + mutex_lock(&nvavp->submit_lock); if (_IOC_DIR(cmd) & _IOC_WRITE) { if (copy_from_user(&hdr, (void __user *)arg, - sizeof(struct nvavp_pushbuffer_submit_hdr))) + sizeof(struct nvavp_pushbuffer_submit_hdr))) { + mutex_unlock(&nvavp->submit_lock); return -EFAULT; + } } - if (!hdr.cmdbuf.mem) + if (!hdr.cmdbuf.mem) { + mutex_unlock(&nvavp->submit_lock); return 0; + } if (hdr.num_relocs > NVAVP_MAX_RELOCATION_COUNT) { dev_err(&nvavp->nvhost_dev->dev, "invalid num_relocs %d\n", hdr.num_relocs); + mutex_unlock(&nvavp->submit_lock); return -EINVAL; } if (copy_from_user(clientctx->relocs, (void __user *)hdr.relocs, sizeof(struct nvavp_reloc) * hdr.num_relocs)) { + mutex_unlock(&nvavp->submit_lock); return -EFAULT; } @@ -1703,6 +1714,7 @@ static int nvavp_pushbuffer_submit_ioctl(struct file *filp, unsigned int cmd, if (IS_ERR(cmdbuf_dmabuf)) { dev_err(&nvavp->nvhost_dev->dev, "invalid cmd buffer handle %08x\n", hdr.cmdbuf.mem); + mutex_unlock(&nvavp->submit_lock); return PTR_ERR(cmdbuf_dmabuf); } @@ -1832,6 +1844,7 @@ err_dmabuf_map: dma_buf_detach(cmdbuf_dmabuf, cmdbuf_attach); err_dmabuf_attach: dma_buf_put(cmdbuf_dmabuf); + mutex_unlock(&nvavp->submit_lock); return ret; } @@ -1845,19 +1858,26 @@ static int nvavp_pushbuffer_submit_compat_ioctl(struct file *filp, struct nvavp_pushbuffer_submit_hdr_v32 hdr_v32; struct nvavp_pushbuffer_submit_hdr __user *user_hdr; int ret = 0; + mutex_lock(&nvavp->submit_lock); if (_IOC_DIR(cmd) & _IOC_WRITE) { if (copy_from_user(&hdr_v32, (void __user *)arg, - sizeof(struct nvavp_pushbuffer_submit_hdr_v32))) + sizeof(struct nvavp_pushbuffer_submit_hdr_v32))) { + mutex_unlock(&nvavp->submit_lock); return -EFAULT; + } } - if (!hdr_v32.cmdbuf.mem) + if (!hdr_v32.cmdbuf.mem) { + mutex_unlock(&nvavp->submit_lock); return 0; + } user_hdr = compat_alloc_user_space(sizeof(*user_hdr)); - if (!access_ok(VERIFY_WRITE, user_hdr, sizeof(*user_hdr))) + if (!access_ok(VERIFY_WRITE, user_hdr, sizeof(*user_hdr))) { + mutex_unlock(&nvavp->submit_lock); return -EFAULT; + } if (__put_user(hdr_v32.cmdbuf.mem, &user_hdr->cmdbuf.mem) || __put_user(hdr_v32.cmdbuf.offset, &user_hdr->cmdbuf.offset) @@ -1867,21 +1887,29 @@ static int nvavp_pushbuffer_submit_compat_ioctl(struct file *filp, || __put_user(hdr_v32.num_relocs, &user_hdr->num_relocs) || __put_user((void __user *)(unsigned long)hdr_v32.syncpt, &user_hdr->syncpt) - || __put_user(hdr_v32.flags, &user_hdr->flags)) + || __put_user(hdr_v32.flags, &user_hdr->flags)) { + mutex_unlock(&nvavp->submit_lock); return -EFAULT; + } + mutex_unlock(&nvavp->submit_lock); ret = nvavp_pushbuffer_submit_ioctl(filp, cmd, (unsigned long)user_hdr); if (ret) return ret; + mutex_lock(&nvavp->submit_lock); if (__get_user(hdr_v32.syncpt, &user_hdr->syncpt)) + { + mutex_unlock(&nvavp->submit_lock); return -EFAULT; + } if (copy_to_user((void __user *)arg, &hdr_v32, sizeof(struct nvavp_pushbuffer_submit_hdr_v32))) { ret = -EFAULT; } + mutex_unlock(&nvavp->submit_lock); return ret; } #endif @@ -2542,6 +2570,7 @@ static int tegra_nvavp_probe(struct platform_device *ndev) nvavp->mbox_from_avp_pend_irq = irq; mutex_init(&nvavp->open_lock); + mutex_init(&nvavp->submit_lock); for (channel_id = 0; channel_id < NVAVP_NUM_CHANNELS; channel_id++) mutex_init(&nvavp->channel_info[channel_id].pushbuffer_lock); diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index c36aeadbb0b..5408870fafe 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -1626,6 +1626,13 @@ out3: if (info->unbind) info->unbind (dev, udev); out1: + /* subdrivers must undo all they did in bind() if they + * fail it, but we may fail later and a deferred kevent + * may trigger an error resubmitting itself and, worse, + * schedule a timer. So we kill it all just in case. + */ + cancel_work_sync(&dev->kevent); + del_timer_sync(&dev->delay); free_netdev(net); out: return status; diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index ce773cca2bf..87734840292 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1105,10 +1105,11 @@ static int proc_getdriver(struct dev_state *ps, void __user *arg) static int proc_connectinfo(struct dev_state *ps, void __user *arg) { - struct usbdevfs_connectinfo ci = { - .devnum = ps->dev->devnum, - .slow = ps->dev->speed == USB_SPEED_LOW - }; + struct usbdevfs_connectinfo ci; + + memset(&ci, 0, sizeof(ci)); + ci.devnum = ps->dev->devnum; + ci.slow = ps->dev->speed == USB_SPEED_LOW; if (copy_to_user(arg, &ci, sizeof(ci))) return -EFAULT; diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index c4a36ab46cd..94c5f4d5c84 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -220,7 +220,7 @@ struct ipv6_pinfo { struct ipv6_ac_socklist *ipv6_ac_list; struct ipv6_fl_socklist __rcu *ipv6_fl_list; - struct ipv6_txoptions *opt; + struct ipv6_txoptions __rcu *opt; struct sk_buff *pktoptions; struct sk_buff *rxpmtu; struct { diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index c5b6dbf9c2f..38e80bf6d26 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -700,6 +700,11 @@ extern int perf_proc_update_handler(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); +static inline bool perf_paranoid_any(void) +{ + return sysctl_perf_event_paranoid > 2; +} + static inline bool perf_paranoid_tracepoint_raw(void) { return sysctl_perf_event_paranoid > -1; diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 67b43806b62..aaff01b765f 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -203,6 +203,7 @@ extern rwlock_t ip6_ra_lock; */ struct ipv6_txoptions { + atomic_t refcnt; /* Length of this structure */ int tot_len; @@ -215,7 +216,7 @@ struct ipv6_txoptions { struct ipv6_opt_hdr *dst0opt; struct ipv6_rt_hdr *srcrt; /* Routing Header */ struct ipv6_opt_hdr *dst1opt; - + struct rcu_head rcu; /* Option buffer, as read by IPV6_PKTOPTIONS, starts here. */ }; @@ -246,6 +247,24 @@ struct ipv6_fl_socklist { struct rcu_head rcu; }; +static inline struct ipv6_txoptions *txopt_get(const struct ipv6_pinfo *np) +{ + struct ipv6_txoptions *opt; + + rcu_read_lock(); + opt = rcu_dereference(np->opt); + if (opt && !atomic_inc_not_zero(&opt->refcnt)) + opt = NULL; + rcu_read_unlock(); + return opt; +} + +static inline void txopt_put(struct ipv6_txoptions *opt) +{ + if (opt && atomic_dec_and_test(&opt->refcnt)) + kfree_rcu(opt, rcu); +} + extern struct ip6_flowlabel *fl6_sock_lookup(struct sock *sk, __be32 label); extern struct ipv6_txoptions *fl6_merge_options(struct ipv6_txoptions * opt_space, struct ip6_flowlabel * fl, diff --git a/kernel/events/core.c b/kernel/events/core.c index f8eb2b154bd..98f62dea199 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -156,8 +156,13 @@ static struct srcu_struct pmus_srcu; * 0 - disallow raw tracepoint access for unpriv * 1 - disallow cpu events for unpriv * 2 - disallow kernel profiling for unpriv + * 3 - disallow all unpriv perf event use */ +#ifdef CONFIG_SECURITY_PERF_EVENTS_RESTRICT +int sysctl_perf_event_paranoid __read_mostly = 3; +#else int sysctl_perf_event_paranoid __read_mostly = 1; +#endif /* Minimum for 512 kiB + 1 user control page */ int sysctl_perf_event_mlock __read_mostly = 512 + (PAGE_SIZE / 1024); /* 'free' kiB per user */ @@ -6572,6 +6577,9 @@ SYSCALL_DEFINE5(perf_event_open, if (flags & ~PERF_FLAG_ALL) return -EINVAL; + if (perf_paranoid_any() && !capable(CAP_SYS_ADMIN)) + return -EACCES; + err = perf_copy_attr(attr_uptr, &attr); if (err) return err; diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 4c3087dffe7..24f3ecf361e 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -895,14 +895,16 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, goto nla_put_failure; if (1) { - struct rtnl_link_ifmap map = { - .mem_start = dev->mem_start, - .mem_end = dev->mem_end, - .base_addr = dev->base_addr, - .irq = dev->irq, - .dma = dev->dma, - .port = dev->if_port, - }; + struct rtnl_link_ifmap map; + + memset(&map, 0, sizeof(map)); + map.mem_start = dev->mem_start; + map.mem_end = dev->mem_end; + map.base_addr = dev->base_addr; + map.irq = dev->irq; + map.dma = dev->dma; + map.port = dev->if_port; + if (nla_put(skb, IFLA_MAP, sizeof(map), &map)) goto nla_put_failure; } diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 6cf9f7782ad..86eedbaf037 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -235,7 +235,9 @@ static int dccp_v6_send_response(struct sock *sk, struct request_sock *req) security_req_classify_flow(req, flowi6_to_flowi(&fl6)); - final_p = fl6_update_dst(&fl6, np->opt, &final); + rcu_read_lock(); + final_p = fl6_update_dst(&fl6, rcu_dereference(np->opt), &final); + rcu_read_unlock(); dst = ip6_dst_lookup_flow(sk, &fl6, final_p, false); if (IS_ERR(dst)) { @@ -252,7 +254,10 @@ static int dccp_v6_send_response(struct sock *sk, struct request_sock *req) &ireq6->loc_addr, &ireq6->rmt_addr); fl6.daddr = ireq6->rmt_addr; - err = ip6_xmit(sk, skb, &fl6, np->opt, np->tclass); + rcu_read_lock(); + err = ip6_xmit(sk, skb, &fl6, rcu_dereference(np->opt), + np->tclass); + rcu_read_unlock(); err = net_xmit_eval(err); } @@ -448,6 +453,7 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk, { struct inet6_request_sock *ireq6 = inet6_rsk(req); struct ipv6_pinfo *newnp, *np = inet6_sk(sk); + struct ipv6_txoptions *opt; struct inet_sock *newinet; struct dccp6_sock *newdp6; struct sock *newsk; @@ -571,13 +577,15 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk, * Yes, keeping reference count would be much more clever, but we make * one more one thing there: reattach optmem to newsk. */ - if (np->opt != NULL) - newnp->opt = ipv6_dup_options(newsk, np->opt); - + opt = rcu_dereference(np->opt); + if (opt) { + opt = ipv6_dup_options(newsk, opt); + RCU_INIT_POINTER(newnp->opt, opt); + } inet_csk(newsk)->icsk_ext_hdr_len = 0; - if (newnp->opt != NULL) - inet_csk(newsk)->icsk_ext_hdr_len = (newnp->opt->opt_nflen + - newnp->opt->opt_flen); + if (opt) + inet_csk(newsk)->icsk_ext_hdr_len = opt->opt_nflen + + opt->opt_flen; dccp_sync_mss(newsk, dst_mtu(dst)); @@ -829,6 +837,7 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr, struct ipv6_pinfo *np = inet6_sk(sk); struct dccp_sock *dp = dccp_sk(sk); struct in6_addr *saddr = NULL, *final_p, final; + struct ipv6_txoptions *opt; struct flowi6 fl6; struct dst_entry *dst; int addr_type; @@ -931,7 +940,8 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr, fl6.fl6_sport = inet->inet_sport; security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); - final_p = fl6_update_dst(&fl6, np->opt, &final); + opt = rcu_dereference_protected(np->opt, sock_owned_by_user(sk)); + final_p = fl6_update_dst(&fl6, opt, &final); dst = ip6_dst_lookup_flow(sk, &fl6, final_p, true); if (IS_ERR(dst)) { @@ -951,9 +961,8 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr, __ip6_dst_store(sk, dst, NULL, NULL); icsk->icsk_ext_hdr_len = 0; - if (np->opt != NULL) - icsk->icsk_ext_hdr_len = (np->opt->opt_flen + - np->opt->opt_nflen); + if (opt) + icsk->icsk_ext_hdr_len = opt->opt_flen + opt->opt_nflen; inet->inet_dport = usin->sin6_port; diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 0f2e8b892b7..9c24fbf3d86 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -451,9 +451,11 @@ void inet6_destroy_sock(struct sock *sk) /* Free tx options */ - opt = xchg(&np->opt, NULL); - if (opt != NULL) - sock_kfree_s(sk, opt, opt->tot_len); + opt = xchg((__force struct ipv6_txoptions **)&np->opt, NULL); + if (opt) { + atomic_sub(opt->tot_len, &sk->sk_omem_alloc); + txopt_put(opt); + } } EXPORT_SYMBOL_GPL(inet6_destroy_sock); @@ -700,7 +702,10 @@ int inet6_sk_rebuild_header(struct sock *sk) fl6.flowi6_uid = sock_i_uid(sk); security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); - final_p = fl6_update_dst(&fl6, np->opt, &final); + rcu_read_lock(); + final_p = fl6_update_dst(&fl6, rcu_dereference(np->opt), + &final); + rcu_read_unlock(); dst = ip6_dst_lookup_flow(sk, &fl6, final_p, false); if (IS_ERR(dst)) { diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index e9a8ca614aa..58e6cf78ad4 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -169,8 +169,10 @@ ipv4_connected: security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); - opt = flowlabel ? flowlabel->opt : np->opt; + rcu_read_lock(); + opt = flowlabel ? flowlabel->opt : rcu_dereference(np->opt); final_p = fl6_update_dst(&fl6, opt, &final); + rcu_read_unlock(); dst = ip6_dst_lookup_flow(sk, &fl6, final_p, true); err = 0; diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 8d67900aa00..33dbd6c1a00 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -727,6 +727,7 @@ ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt) *((char **)&opt2->dst1opt) += dif; if (opt2->srcrt) *((char **)&opt2->srcrt) += dif; + atomic_set(&opt2->refcnt, 1); } return opt2; } @@ -790,7 +791,7 @@ ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt, return ERR_PTR(-ENOBUFS); memset(opt2, 0, tot_len); - + atomic_set(&opt2->refcnt, 1); opt2->tot_len = tot_len; p = (char *)(opt2 + 1); diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c index 65a46058c85..157f2b2bb3e 100644 --- a/net/ipv6/inet6_connection_sock.c +++ b/net/ipv6/inet6_connection_sock.c @@ -78,7 +78,9 @@ struct dst_entry *inet6_csk_route_req(struct sock *sk, memset(fl6, 0, sizeof(*fl6)); fl6->flowi6_proto = IPPROTO_TCP; fl6->daddr = treq->rmt_addr; - final_p = fl6_update_dst(fl6, np->opt, &final); + rcu_read_lock(); + final_p = fl6_update_dst(fl6, rcu_dereference(np->opt), &final); + rcu_read_unlock(); fl6->saddr = treq->loc_addr; fl6->flowi6_oif = treq->iif; fl6->flowi6_mark = inet_rsk(req)->ir_mark; @@ -215,7 +217,9 @@ static struct dst_entry *inet6_csk_route_socket(struct sock *sk, fl6->flowi6_uid = sock_i_uid(sk); security_sk_classify_flow(sk, flowi6_to_flowi(fl6)); - final_p = fl6_update_dst(fl6, np->opt, &final); + rcu_read_lock(); + final_p = fl6_update_dst(fl6, rcu_dereference(np->opt), &final); + rcu_read_unlock(); dst = __inet6_csk_dst_check(sk, np->dst_cookie); if (!dst) { @@ -249,7 +253,8 @@ int inet6_csk_xmit(struct sk_buff *skb, struct flowi *fl_unused) /* Restore final destination back after routing done */ fl6.daddr = np->daddr; - res = ip6_xmit(sk, skb, &fl6, np->opt, np->tclass); + res = ip6_xmit(sk, skb, &fl6, rcu_dereference(np->opt), + np->tclass); rcu_read_unlock(); return res; } diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index d1e2e8ef29c..f4d2412d9c6 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -110,10 +110,12 @@ struct ipv6_txoptions *ipv6_update_options(struct sock *sk, icsk->icsk_ext_hdr_len = opt->opt_flen + opt->opt_nflen; icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie); } - opt = xchg(&inet6_sk(sk)->opt, opt); + opt = xchg((__force struct ipv6_txoptions **)&inet6_sk(sk)->opt, + opt); } else { spin_lock(&sk->sk_dst_lock); - opt = xchg(&inet6_sk(sk)->opt, opt); + opt = xchg((__force struct ipv6_txoptions **)&inet6_sk(sk)->opt, + opt); spin_unlock(&sk->sk_dst_lock); } sk_dst_reset(sk); @@ -213,9 +215,12 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, sk->sk_socket->ops = &inet_dgram_ops; sk->sk_family = PF_INET; } - opt = xchg(&np->opt, NULL); - if (opt) - sock_kfree_s(sk, opt, opt->tot_len); + opt = xchg((__force struct ipv6_txoptions **)&np->opt, + NULL); + if (opt) { + atomic_sub(opt->tot_len, &sk->sk_omem_alloc); + txopt_put(opt); + } pktopt = xchg(&np->pktoptions, NULL); kfree_skb(pktopt); @@ -385,7 +390,8 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, if (optname != IPV6_RTHDR && !ns_capable(net->user_ns, CAP_NET_RAW)) break; - opt = ipv6_renew_options(sk, np->opt, optname, + opt = rcu_dereference_protected(np->opt, sock_owned_by_user(sk)); + opt = ipv6_renew_options(sk, opt, optname, (struct ipv6_opt_hdr __user *)optval, optlen); if (IS_ERR(opt)) { @@ -414,8 +420,10 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, retv = 0; opt = ipv6_update_options(sk, opt); sticky_done: - if (opt) - sock_kfree_s(sk, opt, opt->tot_len); + if (opt) { + atomic_sub(opt->tot_len, &sk->sk_omem_alloc); + txopt_put(opt); + } break; } @@ -468,6 +476,7 @@ sticky_done: break; memset(opt, 0, sizeof(*opt)); + atomic_set(&opt->refcnt, 1); opt->tot_len = sizeof(*opt) + optlen; retv = -EFAULT; if (copy_from_user(opt+1, optval, optlen)) @@ -484,8 +493,10 @@ update: retv = 0; opt = ipv6_update_options(sk, opt); done: - if (opt) - sock_kfree_s(sk, opt, opt->tot_len); + if (opt) { + atomic_sub(opt->tot_len, &sk->sk_omem_alloc); + txopt_put(opt); + } break; } case IPV6_UNICAST_HOPS: @@ -1085,10 +1096,11 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, case IPV6_RTHDR: case IPV6_DSTOPTS: { + struct ipv6_txoptions *opt; lock_sock(sk); - len = ipv6_getsockopt_sticky(sk, np->opt, - optname, optval, len); + opt = rcu_dereference_protected(np->opt, sock_owned_by_user(sk)); + len = ipv6_getsockopt_sticky(sk, opt, optname, optval, len); release_sock(sk); /* check if ipv6_getsockopt_sticky() returns err code */ if (len < 0) diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index a9db8d252c9..5693cf212e8 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -726,6 +726,7 @@ static int rawv6_probe_proto_opt(struct flowi6 *fl6, struct msghdr *msg) static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t len) { + struct ipv6_txoptions *opt_to_free = NULL; struct ipv6_txoptions opt_space; struct sockaddr_in6 * sin6 = (struct sockaddr_in6 *) msg->msg_name; struct in6_addr *daddr, *final_p, final; @@ -833,8 +834,10 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, if (!(opt->opt_nflen|opt->opt_flen)) opt = NULL; } - if (opt == NULL) - opt = np->opt; + if (!opt) { + opt = txopt_get(np); + opt_to_free = opt; + } if (flowlabel) opt = fl6_merge_options(&opt_space, flowlabel, opt); opt = ipv6_fixup_options(&opt_space, opt); @@ -901,6 +904,7 @@ done: dst_release(dst); out: fl6_sock_release(flowlabel); + txopt_put(opt_to_free); return err<0?err:len; do_confirm: dst_confirm(dst); diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index ba8622daffd..701d0656a40 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -237,7 +237,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) memset(&fl6, 0, sizeof(fl6)); fl6.flowi6_proto = IPPROTO_TCP; fl6.daddr = ireq6->rmt_addr; - final_p = fl6_update_dst(&fl6, np->opt, &final); + final_p = fl6_update_dst(&fl6, rcu_dereference(np->opt), &final); fl6.saddr = ireq6->loc_addr; fl6.flowi6_oif = sk->sk_bound_dev_if; fl6.flowi6_mark = ireq->ir_mark; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index f271ab9cf6d..ca9733326b7 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -135,6 +135,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, struct ipv6_pinfo *np = inet6_sk(sk); struct tcp_sock *tp = tcp_sk(sk); struct in6_addr *saddr = NULL, *final_p, final; + struct ipv6_txoptions *opt; struct rt6_info *rt; struct flowi6 fl6; struct dst_entry *dst; @@ -256,7 +257,8 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, fl6.fl6_sport = inet->inet_sport; fl6.flowi6_uid = sock_i_uid(sk); - final_p = fl6_update_dst(&fl6, np->opt, &final); + opt = rcu_dereference_protected(np->opt, sock_owned_by_user(sk)); + final_p = fl6_update_dst(&fl6, opt, &final); security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); @@ -285,9 +287,9 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, tcp_fetch_timewait_stamp(sk, dst); icsk->icsk_ext_hdr_len = 0; - if (np->opt) - icsk->icsk_ext_hdr_len = (np->opt->opt_flen + - np->opt->opt_nflen); + if (opt) + icsk->icsk_ext_hdr_len = opt->opt_flen + + opt->opt_nflen; tp->rx_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr); @@ -483,7 +485,8 @@ static int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst, fl6->daddr = treq->rmt_addr; skb_set_queue_mapping(skb, queue_mapping); - err = ip6_xmit(sk, skb, fl6, np->opt, np->tclass); + err = ip6_xmit(sk, skb, fl6, rcu_dereference(np->opt), + np->tclass); err = net_xmit_eval(err); } @@ -1092,6 +1095,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, struct inet6_request_sock *treq; struct ipv6_pinfo *newnp, *np = inet6_sk(sk); struct tcp6_sock *newtcp6sk; + struct ipv6_txoptions *opt; struct inet_sock *newinet; struct tcp_sock *newtp; struct sock *newsk; @@ -1225,13 +1229,15 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, but we make one more one thing there: reattach optmem to newsk. */ - if (np->opt) - newnp->opt = ipv6_dup_options(newsk, np->opt); - + opt = rcu_dereference(np->opt); + if (opt) { + opt = ipv6_dup_options(newsk, opt); + RCU_INIT_POINTER(newnp->opt, opt); + } inet_csk(newsk)->icsk_ext_hdr_len = 0; - if (newnp->opt) - inet_csk(newsk)->icsk_ext_hdr_len = (newnp->opt->opt_nflen + - newnp->opt->opt_flen); + if (opt) + inet_csk(newsk)->icsk_ext_hdr_len = opt->opt_nflen + + opt->opt_flen; tcp_mtup_init(newsk); tcp_sync_mss(newsk, dst_mtu(dst)); diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 9cb44ac023d..32fbb016343 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1019,6 +1019,7 @@ int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) msg->msg_name; struct in6_addr *daddr, *final_p, final; struct ipv6_txoptions *opt = NULL; + struct ipv6_txoptions *opt_to_free = NULL; struct ip6_flowlabel *flowlabel = NULL; struct flowi6 fl6; struct dst_entry *dst; @@ -1173,8 +1174,10 @@ do_udp_sendmsg: opt = NULL; connected = 0; } - if (opt == NULL) - opt = np->opt; + if (!opt) { + opt = txopt_get(np); + opt_to_free = opt; + } if (flowlabel) opt = fl6_merge_options(&opt_space, flowlabel, opt); opt = ipv6_fixup_options(&opt_space, opt); @@ -1275,6 +1278,7 @@ do_append_data: out: dst_release(dst); fl6_sock_release(flowlabel); + txopt_put(opt_to_free); if (!err) return len; /* diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c index e6e8408c9e3..3b61ddd6e4a 100644 --- a/net/l2tp/l2tp_ip6.c +++ b/net/l2tp/l2tp_ip6.c @@ -485,6 +485,7 @@ static int l2tp_ip6_sendmsg(struct kiocb *iocb, struct sock *sk, (struct sockaddr_l2tpip6 *) msg->msg_name; struct in6_addr *daddr, *final_p, final; struct ipv6_pinfo *np = inet6_sk(sk); + struct ipv6_txoptions *opt_to_free = NULL; struct ipv6_txoptions *opt = NULL; struct ip6_flowlabel *flowlabel = NULL; struct dst_entry *dst = NULL; @@ -575,8 +576,10 @@ static int l2tp_ip6_sendmsg(struct kiocb *iocb, struct sock *sk, opt = NULL; } - if (opt == NULL) - opt = np->opt; + if (!opt) { + opt = txopt_get(np); + opt_to_free = opt; + } if (flowlabel) opt = fl6_merge_options(&opt_space, flowlabel, opt); opt = ipv6_fixup_options(&opt_space, opt); @@ -637,6 +640,7 @@ done: dst_release(dst); out: fl6_sock_release(flowlabel); + txopt_put(opt_to_free); return err < 0 ? err : len; diff --git a/net/socket.c b/net/socket.c index e85c4ec47a1..b0870d12576 100644 --- a/net/socket.c +++ b/net/socket.c @@ -1769,6 +1769,8 @@ SYSCALL_DEFINE6(sendto, int, fd, void __user *, buff, size_t, len, if (len > INT_MAX) len = INT_MAX; + if (unlikely(!access_ok(VERIFY_READ, buff, len))) + return -EFAULT; sock = sockfd_lookup_light(fd, &err, &fput_needed); if (!sock) goto out; @@ -1828,6 +1830,8 @@ SYSCALL_DEFINE6(recvfrom, int, fd, void __user *, ubuf, size_t, size, if (size > INT_MAX) size = INT_MAX; + if (unlikely(!access_ok(VERIFY_WRITE, ubuf, size))) + return -EFAULT; sock = sockfd_lookup_light(fd, &err, &fput_needed); if (!sock) goto out; diff --git a/security/Kconfig b/security/Kconfig index 788af6d871e..7f968b746a5 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -18,6 +18,15 @@ config SECURITY_DMESG_RESTRICT If you are unsure how to answer this question, answer N. +config SECURITY_PERF_EVENTS_RESTRICT + bool "Restrict unprivileged use of performance events" + depends on PERF_EVENTS + help + If you say Y here, the kernel.perf_event_paranoid sysctl + will be set to 3 by default, and no unprivileged use of the + perf_event_open syscall will be permitted unless it is + changed. + config SECURITY bool "Enable different security models" depends on SYSFS diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index 19da589d4fe..7f310161528 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -480,7 +480,7 @@ static int snd_compress_check_input(struct snd_compr_params *params) { /* first let's check the buffer parameter's */ if (params->buffer.fragment_size == 0 || - params->buffer.fragments > SIZE_MAX / params->buffer.fragment_size) + params->buffer.fragments > INT_MAX / params->buffer.fragment_size) return -EINVAL; /* now codec parameters */ diff --git a/sound/core/timer.c b/sound/core/timer.c index 38a137d6b04..1a21148c21b 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -73,7 +73,7 @@ struct snd_timer_user { struct timespec tstamp; /* trigger tstamp */ wait_queue_head_t qchange_sleep; struct fasync_struct *fasync; - struct mutex tread_sem; + struct mutex ioctl_lock; }; /* list of timers */ @@ -1184,6 +1184,7 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri, tu->tstamp = *tstamp; if ((tu->filter & (1 << event)) == 0 || !tu->tread) return; + memset(&r1, 0, sizeof(r1)); r1.event = event; r1.tstamp = *tstamp; r1.val = resolution; @@ -1218,6 +1219,7 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri, } if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) && tu->last_resolution != resolution) { + memset(&r1, 0, sizeof(r1)); r1.event = SNDRV_TIMER_EVENT_RESOLUTION; r1.tstamp = tstamp; r1.val = resolution; @@ -1266,7 +1268,7 @@ static int snd_timer_user_open(struct inode *inode, struct file *file) return -ENOMEM; spin_lock_init(&tu->qlock); init_waitqueue_head(&tu->qchange_sleep); - mutex_init(&tu->tread_sem); + mutex_init(&tu->ioctl_lock); tu->ticks = 1; tu->queue_size = 128; tu->queue = kmalloc(tu->queue_size * sizeof(struct snd_timer_read), @@ -1286,8 +1288,10 @@ static int snd_timer_user_release(struct inode *inode, struct file *file) if (file->private_data) { tu = file->private_data; file->private_data = NULL; + mutex_lock(&tu->ioctl_lock); if (tu->timeri) snd_timer_close(tu->timeri); + mutex_unlock(&tu->ioctl_lock); kfree(tu->queue); kfree(tu->tqueue); kfree(tu); @@ -1525,7 +1529,6 @@ static int snd_timer_user_tselect(struct file *file, int err = 0; tu = file->private_data; - mutex_lock(&tu->tread_sem); if (tu->timeri) { snd_timer_close(tu->timeri); tu->timeri = NULL; @@ -1569,7 +1572,6 @@ static int snd_timer_user_tselect(struct file *file, } __err: - mutex_unlock(&tu->tread_sem); return err; } @@ -1683,6 +1685,7 @@ static int snd_timer_user_params(struct file *file, if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) { if (tu->tread) { struct snd_timer_tread tread; + memset(&tread, 0, sizeof(tread)); tread.event = SNDRV_TIMER_EVENT_EARLY; tread.tstamp.tv_sec = 0; tread.tstamp.tv_nsec = 0; @@ -1782,7 +1785,7 @@ enum { SNDRV_TIMER_IOCTL_PAUSE_OLD = _IO('T', 0x23), }; -static long snd_timer_user_ioctl(struct file *file, unsigned int cmd, +static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct snd_timer_user *tu; @@ -1799,17 +1802,11 @@ static long snd_timer_user_ioctl(struct file *file, unsigned int cmd, { int xarg; - mutex_lock(&tu->tread_sem); - if (tu->timeri) { /* too late */ - mutex_unlock(&tu->tread_sem); + if (tu->timeri) /* too late */ return -EBUSY; - } - if (get_user(xarg, p)) { - mutex_unlock(&tu->tread_sem); + if (get_user(xarg, p)) return -EFAULT; - } tu->tread = xarg ? 1 : 0; - mutex_unlock(&tu->tread_sem); return 0; } case SNDRV_TIMER_IOCTL_GINFO: @@ -1842,6 +1839,18 @@ static long snd_timer_user_ioctl(struct file *file, unsigned int cmd, return -ENOTTY; } +static long snd_timer_user_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct snd_timer_user *tu = file->private_data; + long ret; + + mutex_lock(&tu->ioctl_lock); + ret = __snd_timer_user_ioctl(file, cmd, arg); + mutex_unlock(&tu->ioctl_lock); + return ret; +} + static int snd_timer_user_fasync(int fd, struct file * file, int on) { struct snd_timer_user *tu; diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 07b1a3ad3e2..54494df33dd 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -1514,13 +1514,16 @@ int perf_evsel__open_strerror(struct perf_evsel *evsel, switch (err) { case EPERM: case EACCES: - return scnprintf(msg, size, "%s", - "You may not have permission to collect %sstats.\n" - "Consider tweaking /proc/sys/kernel/perf_event_paranoid:\n" - " -1 - Not paranoid at all\n" - " 0 - Disallow raw tracepoint access for unpriv\n" - " 1 - Disallow cpu events for unpriv\n" - " 2 - Disallow kernel profiling for unpriv", + return scnprintf(msg, size, + "You may not have permission to collect %sstats.\n\n" + "Consider tweaking /proc/sys/kernel/perf_event_paranoid,\n" + "which controls use of the performance events system by\n" + "unprivileged users (without CAP_SYS_ADMIN).\n\n" + "The default value is 1:\n\n" + " -1: Allow use of (almost) all events by all users\n" + ">= 0: Disallow raw tracepoint access by users without CAP_IOC_LOCK\n" + ">= 1: Disallow CPU event access by users without CAP_SYS_ADMIN\n" + ">= 2: Disallow kernel profiling by users without CAP_SYS_ADMIN", target->system_wide ? "system-wide " : ""); case ENOENT: return scnprintf(msg, size, "The %s event is not supported.", |
