| From foo@baz Fri Jan 4 20:27:35 CET 2019 |
| From: Cong Wang <xiyou.wangcong@gmail.com> |
| Date: Sat, 29 Dec 2018 13:56:36 -0800 |
| Subject: ax25: fix a use-after-free in ax25_fillin_cb() |
| |
| From: Cong Wang <xiyou.wangcong@gmail.com> |
| |
| [ Upstream commit c433570458e49bccea5c551df628d058b3526289 ] |
| |
| There are multiple issues here: |
| |
| 1. After freeing dev->ax25_ptr, we need to set it to NULL otherwise |
| we may use a dangling pointer. |
| |
| 2. There is a race between ax25_setsockopt() and device notifier as |
| reported by syzbot. Close it by holding RTNL lock. |
| |
| 3. We need to test if dev->ax25_ptr is NULL before using it. |
| |
| Reported-and-tested-by: syzbot+ae6bb869cbed29b29040@syzkaller.appspotmail.com |
| Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| net/ax25/af_ax25.c | 11 +++++++++-- |
| net/ax25/ax25_dev.c | 2 ++ |
| 2 files changed, 11 insertions(+), 2 deletions(-) |
| |
| --- a/net/ax25/af_ax25.c |
| +++ b/net/ax25/af_ax25.c |
| @@ -654,15 +654,22 @@ static int ax25_setsockopt(struct socket |
| break; |
| } |
| |
| - dev = dev_get_by_name(&init_net, devname); |
| + rtnl_lock(); |
| + dev = __dev_get_by_name(&init_net, devname); |
| if (!dev) { |
| + rtnl_unlock(); |
| res = -ENODEV; |
| break; |
| } |
| |
| ax25->ax25_dev = ax25_dev_ax25dev(dev); |
| + if (!ax25->ax25_dev) { |
| + rtnl_unlock(); |
| + res = -ENODEV; |
| + break; |
| + } |
| ax25_fillin_cb(ax25, ax25->ax25_dev); |
| - dev_put(dev); |
| + rtnl_unlock(); |
| break; |
| |
| default: |
| --- a/net/ax25/ax25_dev.c |
| +++ b/net/ax25/ax25_dev.c |
| @@ -116,6 +116,7 @@ void ax25_dev_device_down(struct net_dev |
| if ((s = ax25_dev_list) == ax25_dev) { |
| ax25_dev_list = s->next; |
| spin_unlock_bh(&ax25_dev_lock); |
| + dev->ax25_ptr = NULL; |
| dev_put(dev); |
| kfree(ax25_dev); |
| return; |
| @@ -125,6 +126,7 @@ void ax25_dev_device_down(struct net_dev |
| if (s->next == ax25_dev) { |
| s->next = ax25_dev->next; |
| spin_unlock_bh(&ax25_dev_lock); |
| + dev->ax25_ptr = NULL; |
| dev_put(dev); |
| kfree(ax25_dev); |
| return; |