From: Jakub Kicinski kuba@kernel.org
stable inclusion from stable-v4.19.273 commit aa07c86e43ed8780d610ecfb2ce13da326729201 category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/I6HZHU CVE: CVE-2023-26545
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=...
--------------------------------
commit fda6c89fe3d9aca073495a664e1d5aea28cd4377 upstream.
lianhui reports that when MPLS fails to register the sysctl table under new location (during device rename) the old pointers won't get overwritten and may be freed again (double free).
Handle this gracefully. The best option would be unregistering the MPLS from the device completely on failure, but unfortunately mpls_ifdown() can fail. So failing fully is also unreliable.
Another option is to register the new table first then only remove old one if the new one succeeds. That requires more code, changes order of notifications and two tables may be visible at the same time.
sysctl point is not used in the rest of the code - set to NULL on failures and skip unregister if already NULL.
Reported-by: lianhui tang bluetlh@gmail.com Fixes: 0fae3bf018d9 ("mpls: handle device renames for per-device sysctls") Signed-off-by: Jakub Kicinski kuba@kernel.org Signed-off-by: David S. Miller davem@davemloft.net Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org Signed-off-by: Zhengchao Shao shaozhengchao@huawei.com Reviewed-by: Liu Jian liujian56@huawei.com Reviewed-by: Wang Weiyang wangweiyang2@huawei.com Reviewed-by: Yue Haibing yuehaibing@huawei.com Signed-off-by: Yongqiang Liu liuyongqiang13@huawei.com --- net/mpls/af_mpls.c | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index 7623d9aec636..c7fd387baa61 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -1375,6 +1375,7 @@ static int mpls_dev_sysctl_register(struct net_device *dev, free: kfree(table); out: + mdev->sysctl = NULL; return -ENOBUFS; }
@@ -1384,6 +1385,9 @@ static void mpls_dev_sysctl_unregister(struct net_device *dev, struct net *net = dev_net(dev); struct ctl_table *table;
+ if (!mdev->sysctl) + return; + table = mdev->sysctl->ctl_table_arg; unregister_net_sysctl_table(mdev->sysctl); kfree(table);