From 951ae7dd395dd1407cfc6d7f2f59163850ec0362 Mon Sep 17 00:00:00 2001 From: Yannick Cote Date: Tue, 14 Mar 2023 21:40:48 -0400 Subject: [KPATCH CVE-2022-4744] kpatch fixes for CVE-2022-4744 Kernels: 5.14.0-162.6.1.el9_1 5.14.0-162.12.1.el9_1 5.14.0-162.18.1.el9_1 Kpatch-MR: https://gitlab.com/redhat/prdsc/rhel/src/kpatch/rhel-9/-/merge_requests/28 Approved-by: Joe Lawrence (@joe.lawrence) Changes since last build: [x86_64]: control.o: changed function: snd_ctl_elem_read control.o: changed function: snd_ctl_ioctl dev.o: changed function: register_netdevice tun.o: changed function: tun_free_netdev tun.o: changed function: tun_set_iff.constprop.0 tun.o: new function: kpp_cve_2022_4744_tun_net_init [ppc64le]: dev.o: changed function: register_netdevice tun.o: changed function: tun_free_netdev tun.o: changed function: tun_set_iff.constprop.0 tun.o: new function: kpp_cve_2022_4744_tun_net_init --------------------------- Modifications: - add shadow variables for tun->ifr, tun->file, ndo_init - call init from register_netdevice() when shadow variables are detected - renamed new tun_net_init() -> kpp_cve_2022_4744_tun_net_init() - code to allocate, maintain and remove shadow variables commit 8ab79b18abf2f3a2cf33903794ff0de7cec105fc Author: Jon Maloy Date: Wed Mar 8 11:35:45 2023 -0500 tun: avoid double free in tun_free_netdev Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2156372 Upstream: Merged CVE: CVE-2022-4744 commit 158b515f703e75e7d68289bf4d98c664e1d632df Author: George Kennedy Date: Thu Dec 16 13:25:32 2021 -0500 tun: avoid double free in tun_free_netdev Avoid double free in tun_free_netdev() by moving the dev->tstats and tun->security allocs to a new ndo_init routine (tun_net_init()) that will be called by register_netdevice(). ndo_init is paired with the desctructor (tun_free_netdev()), so if there's an error in register_netdevice() the destructor will handle the frees. BUG: KASAN: double-free or invalid-free in selinux_tun_dev_free_security+0x1a/0x20 security/selinux/hooks.c:5605 CPU: 0 PID: 25750 Comm: syz-executor416 Not tainted 5.16.0-rc2-syzk #1 Hardware name: Red Hat KVM, BIOS Call Trace: __dump_stack lib/dump_stack.c:88 [inline] dump_stack_lvl+0x89/0xb5 lib/dump_stack.c:106 print_address_description.constprop.9+0x28/0x160 mm/kasan/report.c:247 kasan_report_invalid_free+0x55/0x80 mm/kasan/report.c:372 ____kasan_slab_free mm/kasan/common.c:346 [inline] __kasan_slab_free+0x107/0x120 mm/kasan/common.c:374 kasan_slab_free include/linux/kasan.h:235 [inline] slab_free_hook mm/slub.c:1723 [inline] slab_free_freelist_hook mm/slub.c:1749 [inline] slab_free mm/slub.c:3513 [inline] kfree+0xac/0x2d0 mm/slub.c:4561 selinux_tun_dev_free_security+0x1a/0x20 security/selinux/hooks.c:5605 security_tun_dev_free_security+0x4f/0x90 security/security.c:2342 tun_free_netdev+0xe6/0x150 drivers/net/tun.c:2215 netdev_run_todo+0x4df/0x840 net/core/dev.c:10627 rtnl_unlock+0x13/0x20 net/core/rtnetlink.c:112 __tun_chr_ioctl+0x80c/0x2870 drivers/net/tun.c:3302 tun_chr_ioctl+0x2f/0x40 drivers/net/tun.c:3311 vfs_ioctl fs/ioctl.c:51 [inline] __do_sys_ioctl fs/ioctl.c:874 [inline] __se_sys_ioctl fs/ioctl.c:860 [inline] __x64_sys_ioctl+0x19d/0x220 fs/ioctl.c:860 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x3a/0x80 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x44/0xae Reported-by: syzkaller Signed-off-by: George Kennedy Suggested-by: Jakub Kicinski Link: https://lore.kernel.org/r/1639679132-19884-1-git-send-email-george.kennedy@oracle.com Signed-off-by: Jakub Kicinski Signed-off-by: Jon Maloy Signed-off-by: Yannick Cote --- drivers/net/tun.c | 135 ++++++++++++++++++++++++++++------------------ net/core/dev.c | 31 ++++++++++- 2 files changed, 112 insertions(+), 54 deletions(-) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index a3aec566fb4b..2a2fee5e3c10 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -216,6 +216,9 @@ struct veth { __be16 h_vlan_TCI; }; +static void tun_flow_init(struct tun_struct *tun); +static void tun_flow_uninit(struct tun_struct *tun); + static int tun_napi_receive(struct napi_struct *napi, int budget) { struct tun_file *tfile = container_of(napi, struct tun_file, napi); @@ -953,6 +956,67 @@ static int check_filter(struct tap_filter *filter, const struct sk_buff *skb) static const struct ethtool_ops tun_ethtool_ops; +/* CVE-2022-4744 - kpatch gathered definitions */ +#ifndef __GENKSYMS__ +/* Note: avoid symvers churn for tun_get_tx_ring and tun_get_socket */ +# include +# define KLP_CVE_2022_4744 0x2022474400000001 +struct klp_cve_2022_4774_t { + struct ifreq tun_ifr; + struct file tun_file; + int (*ndo_init)(struct net_device *netdev); +}; +#endif +int kpp_cve_2022_4744_tun_net_init(struct net_device *dev) +{ + struct tun_struct *tun = netdev_priv(dev); + int err; + struct klp_cve_2022_4774_t *klp_sv; + + klp_sv = klp_shadow_get(tun, KLP_CVE_2022_4744); + if (!klp_sv) + return -EINVAL; + + dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); + if (!dev->tstats) + return -ENOMEM; + + spin_lock_init(&tun->lock); + + err = security_tun_dev_alloc_security(&tun->security); + if (err < 0) { + free_percpu(dev->tstats); + return err; + } + + tun_flow_init(tun); + + dev->hw_features = NETIF_F_SG | NETIF_F_FRAGLIST | + TUN_USER_FEATURES | NETIF_F_HW_VLAN_CTAG_TX | + NETIF_F_HW_VLAN_STAG_TX; + dev->features = dev->hw_features | NETIF_F_LLTX; + dev->vlan_features = dev->features & + ~(NETIF_F_HW_VLAN_CTAG_TX | + NETIF_F_HW_VLAN_STAG_TX); + klp_sv = klp_shadow_get(tun, KLP_CVE_2022_4744); + if (klp_sv) { + tun->flags = (tun->flags & ~TUN_FEATURES) | + (klp_sv->tun_ifr.ifr_flags & TUN_FEATURES); + + INIT_LIST_HEAD(&tun->disabled); + err = tun_attach(tun, &klp_sv->tun_file, false, + klp_sv->tun_ifr.ifr_flags & IFF_NAPI, + klp_sv->tun_ifr.ifr_flags & IFF_NAPI_FRAGS, false); + } + if (err < 0) { + tun_flow_uninit(tun); + security_tun_dev_free_security(tun->security); + free_percpu(dev->tstats); + return err; + } + return 0; +} + /* Net device detach from fd. */ static void tun_net_uninit(struct net_device *dev) { @@ -2206,15 +2270,11 @@ static void tun_free_netdev(struct net_device *dev) BUG_ON(!(list_empty(&tun->disabled))); free_percpu(dev->tstats); - /* We clear tstats so that tun_set_iff() can tell if - * tun_free_netdev() has been called from register_netdevice(). - */ - dev->tstats = NULL; - tun_flow_uninit(tun); security_tun_dev_free_security(tun->security); __tun_set_ebpf(tun, &tun->steering_prog, NULL); __tun_set_ebpf(tun, &tun->filter_prog, NULL); + klp_shadow_free(tun, KLP_CVE_2022_4744, NULL); } static void tun_setup(struct net_device *dev) @@ -2716,41 +2776,30 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) tun->rx_batched = 0; RCU_INIT_POINTER(tun->steering_prog, NULL); - dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); - if (!dev->tstats) { - err = -ENOMEM; - goto err_free_dev; - } +/* kpatch */ +{ + struct klp_cve_2022_4774_t *klp_sv; - spin_lock_init(&tun->lock); + klp_sv = klp_shadow_alloc(tun, KLP_CVE_2022_4744, + sizeof(*klp_sv), GFP_KERNEL, NULL, NULL); + if (!klp_sv) { + free_netdev(dev); + return -ENOMEM; + } - err = security_tun_dev_alloc_security(&tun->security); - if (err < 0) - goto err_free_stat; + memcpy(&klp_sv->tun_ifr, ifr, sizeof(*ifr)); + memcpy(&klp_sv->tun_file, file, sizeof(*file)); + klp_sv->ndo_init = kpp_cve_2022_4744_tun_net_init; +} tun_net_init(dev); - tun_flow_init(tun); - - dev->hw_features = NETIF_F_SG | NETIF_F_FRAGLIST | - TUN_USER_FEATURES | NETIF_F_HW_VLAN_CTAG_TX | - NETIF_F_HW_VLAN_STAG_TX; - dev->features = dev->hw_features | NETIF_F_LLTX; - dev->vlan_features = dev->features & - ~(NETIF_F_HW_VLAN_CTAG_TX | - NETIF_F_HW_VLAN_STAG_TX); - - tun->flags = (tun->flags & ~TUN_FEATURES) | - (ifr->ifr_flags & TUN_FEATURES); - - INIT_LIST_HEAD(&tun->disabled); - err = tun_attach(tun, file, false, ifr->ifr_flags & IFF_NAPI, - ifr->ifr_flags & IFF_NAPI_FRAGS, false); - if (err < 0) - goto err_free_flow; err = register_netdevice(tun->dev); - if (err < 0) - goto err_detach; + if (err < 0) { + klp_shadow_free(tun, KLP_CVE_2022_4744, NULL); + free_netdev(dev); + return err; + } /* free_netdev() won't check refcnt, to avoid race * with dev_put() we need publish tun after registration. */ @@ -2767,24 +2816,6 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) strcpy(ifr->ifr_name, tun->dev->name); return 0; - -err_detach: - tun_detach_all(dev); - /* We are here because register_netdevice() has failed. - * If register_netdevice() already called tun_free_netdev() - * while dealing with the error, dev->stats has been cleared. - */ - if (!dev->tstats) - goto err_free_dev; - -err_free_flow: - tun_flow_uninit(tun); - security_tun_dev_free_security(tun->security); -err_free_stat: - free_percpu(dev->tstats); -err_free_dev: - free_netdev(dev); - return err; } static void tun_get_iff(struct tun_struct *tun, struct ifreq *ifr) diff --git a/net/core/dev.c b/net/core/dev.c index 89ff6b1e7735..d5f07da178dc 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -9544,6 +9544,15 @@ EXPORT_SYMBOL(netif_tx_stop_all_queues); * will not get the same name. */ +/* CVE-2022-4744 - kpatch gathered definitions */ +#include +#define KLP_CVE_2022_4744 0x2022474400000001 +struct klp_cve_2022_4774_t { + struct ifreq tun_ifr; + struct file tun_file; + int (*ndo_init)(struct net_device *netdev); +}; + int register_netdevice(struct net_device *dev) { int ret; @@ -9576,15 +9585,33 @@ int register_netdevice(struct net_device *dev) if (!dev->name_node) goto out; +/* kpatch */ +{ /* Init, if this function is available */ - if (dev->netdev_ops->ndo_init) { - ret = dev->netdev_ops->ndo_init(dev); + struct klp_cve_2022_4774_t *klp_sv; + int (*ndo_init)(struct net_device *netdev) = NULL; + + /* Does device has shadow variable ndo_init? */ + if (netdev_priv(dev)) { + klp_sv = klp_shadow_get(netdev_priv(dev), KLP_CVE_2022_4744); + if (klp_sv && klp_sv->ndo_init) + ndo_init = klp_sv->ndo_init; + } + + /* How about typical netdev_ops->ndo_init */ + if (!ndo_init && dev->netdev_ops->ndo_init) + ndo_init = dev->netdev_ops->ndo_init; + + /* Run ndo_init callback if found */ + if (ndo_init) { + ret = ndo_init(dev); if (ret) { if (ret > 0) ret = -EIO; goto err_free_name; } } +} if (((dev->hw_features | dev->features) & NETIF_F_HW_VLAN_CTAG_FILTER) && -- 2.39.2