Commit 2f47d959 authored by Phil Sutter's avatar Phil Sutter Committed by Dong Chenchen
Browse files

netfilter: IDLETIMER: Fix for possible ABBA deadlock

stable inclusion
from stable-v6.6.67
commit 8c2c8445cda8f59c38dec7dc10509bcb23ae26a0
category: bugfix
bugzilla: https://gitee.com/src-openeuler/kernel/issues/IBJCF5
CVE: CVE-2024-54683

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=8c2c8445cda8f59c38dec7dc10509bcb23ae26a0



--------------------------------

[ Upstream commit f36b01994d68ffc253c8296e2228dfe6e6431c03 ]

Deletion of the last rule referencing a given idletimer may happen at
the same time as a read of its file in sysfs:

| ======================================================
| WARNING: possible circular locking dependency detected
| 6.12.0-rc7-01692-g5e9a28f41134-dirty #594 Not tainted
| ------------------------------------------------------
| iptables/3303 is trying to acquire lock:
| ffff8881057e04b8 (kn->active#48){++++}-{0:0}, at: __kernfs_remove+0x20
|
| but task is already holding lock:
| ffffffffa0249068 (list_mutex){+.+.}-{3:3}, at: idletimer_tg_destroy_v]
|
| which lock already depends on the new lock.

A simple reproducer is:

| #!/bin/bash
|
| while true; do
|         iptables -A INPUT -i foo -j IDLETIMER --timeout 10 --label "testme"
|         iptables -D INPUT -i foo -j IDLETIMER --timeout 10 --label "testme"
| done &
| while true; do
|         cat /sys/class/xt_idletimer/timers/testme >/dev/null
| done

Avoid this by freeing list_mutex right after deleting the element from
the list, then continuing with the teardown.

Fixes: 0902b469 ("netfilter: xtables: idletimer target implementation")
Signed-off-by: default avatarPhil Sutter <phil@nwl.cc>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
Signed-off-by: default avatarDong Chenchen <dongchenchen2@huawei.com>
parent 01e57e5f
Loading
Loading
Loading
Loading
+28 −24
Original line number Diff line number Diff line
@@ -409,21 +409,23 @@ static void idletimer_tg_destroy(const struct xt_tgdtor_param *par)

	mutex_lock(&list_mutex);

	if (--info->timer->refcnt == 0) {
	if (--info->timer->refcnt > 0) {
		pr_debug("decreased refcnt of timer %s to %u\n",
			 info->label, info->timer->refcnt);
		mutex_unlock(&list_mutex);
		return;
	}

	pr_debug("deleting timer %s\n", info->label);

	list_del(&info->timer->entry);
	mutex_unlock(&list_mutex);

	timer_shutdown_sync(&info->timer->timer);
	cancel_work_sync(&info->timer->work);
	sysfs_remove_file(idletimer_tg_kobj, &info->timer->attr.attr);
	kfree(info->timer->attr.attr.name);
	kfree(info->timer);
	} else {
		pr_debug("decreased refcnt of timer %s to %u\n",
			 info->label, info->timer->refcnt);
	}

	mutex_unlock(&list_mutex);
}

static void idletimer_tg_destroy_v1(const struct xt_tgdtor_param *par)
@@ -434,10 +436,18 @@ static void idletimer_tg_destroy_v1(const struct xt_tgdtor_param *par)

	mutex_lock(&list_mutex);

	if (--info->timer->refcnt == 0) {
	if (--info->timer->refcnt > 0) {
		pr_debug("decreased refcnt of timer %s to %u\n",
			 info->label, info->timer->refcnt);
		mutex_unlock(&list_mutex);
		return;
	}

	pr_debug("deleting timer %s\n", info->label);

	list_del(&info->timer->entry);
	mutex_unlock(&list_mutex);

	if (info->timer->timer_type & XT_IDLETIMER_ALARM) {
		alarm_cancel(&info->timer->alarm);
	} else {
@@ -447,12 +457,6 @@ static void idletimer_tg_destroy_v1(const struct xt_tgdtor_param *par)
	sysfs_remove_file(idletimer_tg_kobj, &info->timer->attr.attr);
	kfree(info->timer->attr.attr.name);
	kfree(info->timer);
	} else {
		pr_debug("decreased refcnt of timer %s to %u\n",
			 info->label, info->timer->refcnt);
	}

	mutex_unlock(&list_mutex);
}