From: Sasha Levin <sashal@kernel.org>
To: linux-kernel@vger.kernel.org, stable@vger.kernel.org
Cc: "Matthew Wilcox (Oracle)" <willy@infradead.org>,
Brendan Gregg <bgregg@netflix.com>,
Sasha Levin <sashal@kernel.org>,
linux-fsdevel@vger.kernel.org
Subject: [PATCH AUTOSEL 5.1 03/39] idr: Fix idr_get_next race with idr_remove
Date: Tue, 2 Jul 2019 22:14:38 -0400 [thread overview]
Message-ID: <20190703021514.17727-3-sashal@kernel.org> (raw)
In-Reply-To: <20190703021514.17727-1-sashal@kernel.org>
From: "Matthew Wilcox (Oracle)" <willy@infradead.org>
[ Upstream commit 5c089fd0c73411f2170ab795c9ffc16718c7d007 ]
If the entry is deleted from the IDR between the call to
radix_tree_iter_find() and rcu_dereference_raw(), idr_get_next()
will return NULL, which will end the iteration prematurely. We should
instead continue to the next entry in the IDR. This only happens if the
iteration is protected by the RCU lock. Most IDR users use a spinlock
or semaphore to exclude simultaneous modifications. It was noticed once
the PID allocator was converted to use the IDR, as it uses the RCU lock,
but there may be other users elsewhere in the kernel.
We can't use the normal pattern of calling radix_tree_deref_retry()
(which catches both a retry entry in a leaf node and a node entry in
the root) as the IDR supports storing entries which are unaligned,
which will trigger an infinite loop if they are encountered. Instead,
we have to explicitly check whether the entry is a retry entry.
Fixes: 0a835c4f090a ("Reimplement IDR and IDA using the radix tree")
Reported-by: Brendan Gregg <bgregg@netflix.com>
Tested-by: Brendan Gregg <bgregg@netflix.com>
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
lib/idr.c | 14 +++++++--
tools/testing/radix-tree/idr-test.c | 46 +++++++++++++++++++++++++++++
2 files changed, 58 insertions(+), 2 deletions(-)
diff --git a/lib/idr.c b/lib/idr.c
index cb1db9b8d3f6..da3021e7c2b5 100644
--- a/lib/idr.c
+++ b/lib/idr.c
@@ -227,11 +227,21 @@ void *idr_get_next(struct idr *idr, int *nextid)
{
struct radix_tree_iter iter;
void __rcu **slot;
+ void *entry = NULL;
unsigned long base = idr->idr_base;
unsigned long id = *nextid;
id = (id < base) ? 0 : id - base;
- slot = radix_tree_iter_find(&idr->idr_rt, &iter, id);
+ radix_tree_for_each_slot(slot, &idr->idr_rt, &iter, id) {
+ entry = rcu_dereference_raw(*slot);
+ if (!entry)
+ continue;
+ if (!xa_is_internal(entry))
+ break;
+ if (slot != &idr->idr_rt.xa_head && !xa_is_retry(entry))
+ break;
+ slot = radix_tree_iter_retry(&iter);
+ }
if (!slot)
return NULL;
id = iter.index + base;
@@ -240,7 +250,7 @@ void *idr_get_next(struct idr *idr, int *nextid)
return NULL;
*nextid = id;
- return rcu_dereference_raw(*slot);
+ return entry;
}
EXPORT_SYMBOL(idr_get_next);
diff --git a/tools/testing/radix-tree/idr-test.c b/tools/testing/radix-tree/idr-test.c
index 1b63bdb7688f..fe33be4c2475 100644
--- a/tools/testing/radix-tree/idr-test.c
+++ b/tools/testing/radix-tree/idr-test.c
@@ -287,6 +287,51 @@ static void idr_align_test(struct idr *idr)
}
}
+DEFINE_IDR(find_idr);
+
+static void *idr_throbber(void *arg)
+{
+ time_t start = time(NULL);
+ int id = *(int *)arg;
+
+ rcu_register_thread();
+ do {
+ idr_alloc(&find_idr, xa_mk_value(id), id, id + 1, GFP_KERNEL);
+ idr_remove(&find_idr, id);
+ } while (time(NULL) < start + 10);
+ rcu_unregister_thread();
+
+ return NULL;
+}
+
+void idr_find_test_1(int anchor_id, int throbber_id)
+{
+ pthread_t throbber;
+ time_t start = time(NULL);
+
+ pthread_create(&throbber, NULL, idr_throbber, &throbber_id);
+
+ BUG_ON(idr_alloc(&find_idr, xa_mk_value(anchor_id), anchor_id,
+ anchor_id + 1, GFP_KERNEL) != anchor_id);
+
+ do {
+ int id = 0;
+ void *entry = idr_get_next(&find_idr, &id);
+ BUG_ON(entry != xa_mk_value(id));
+ } while (time(NULL) < start + 11);
+
+ pthread_join(throbber, NULL);
+
+ idr_remove(&find_idr, anchor_id);
+ BUG_ON(!idr_is_empty(&find_idr));
+}
+
+void idr_find_test(void)
+{
+ idr_find_test_1(100000, 0);
+ idr_find_test_1(0, 100000);
+}
+
void idr_checks(void)
{
unsigned long i;
@@ -368,6 +413,7 @@ void idr_checks(void)
idr_u32_test(1);
idr_u32_test(0);
idr_align_test(&idr);
+ idr_find_test();
}
#define module_init(x)
--
2.20.1
next prev parent reply other threads:[~2019-07-03 2:23 UTC|newest]
Thread overview: 41+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-07-03 2:14 [PATCH AUTOSEL 5.1 01/39] ARM: dts: meson8: fix GPU interrupts and drop an undocumented property Sasha Levin
2019-07-03 2:14 ` [PATCH AUTOSEL 5.1 02/39] ARM: dts: meson8b: fix the operating voltage of the Mali GPU Sasha Levin
2019-07-03 2:14 ` Sasha Levin [this message]
2019-07-03 2:14 ` [PATCH AUTOSEL 5.1 04/39] irqchip/irq-csky-mpintc: Support auto irq deliver to all cpus Sasha Levin
2019-07-03 2:14 ` [PATCH AUTOSEL 5.1 05/39] irqchip/gic-v3-its: Fix command queue pointer comparison bug Sasha Levin
2019-07-03 2:14 ` [PATCH AUTOSEL 5.1 06/39] clk: ti: clkctrl: Fix returning uninitialized data Sasha Levin
2019-07-03 2:14 ` [PATCH AUTOSEL 5.1 07/39] efi/bgrt: Drop BGRT status field reserved bits check Sasha Levin
2019-07-03 2:14 ` [PATCH AUTOSEL 5.1 08/39] arm64: dts: ls1028a: Fix CPU idle fail Sasha Levin
2019-07-11 5:04 ` Ran Wang
2019-07-11 6:43 ` Greg KH
2019-07-03 2:14 ` [PATCH AUTOSEL 5.1 09/39] selftests/powerpc: Add test of fork with mapping above 512TB Sasha Levin
2019-07-03 2:14 ` [PATCH AUTOSEL 5.1 10/39] perf/core: Fix perf_sample_regs_user() mm check Sasha Levin
2019-07-03 2:14 ` [PATCH AUTOSEL 5.1 11/39] ARM: dts: gemini Fix up DNS-313 compatible string Sasha Levin
2019-07-03 2:14 ` [PATCH AUTOSEL 5.1 12/39] ARM: omap2: remove incorrect __init annotation Sasha Levin
2019-07-03 2:14 ` [PATCH AUTOSEL 5.1 13/39] afs: Fix uninitialised spinlock afs_volume::cb_break_lock Sasha Levin
2019-07-03 2:14 ` [PATCH AUTOSEL 5.1 14/39] x86/efi: fix a -Wtype-limits compilation warning Sasha Levin
2019-07-03 2:14 ` [PATCH AUTOSEL 5.1 15/39] x86/apic: Fix integer overflow on 10 bit left shift of cpu_khz Sasha Levin
2019-07-03 2:14 ` [PATCH AUTOSEL 5.1 16/39] be2net: fix link failure after ethtool offline test Sasha Levin
2019-07-03 2:14 ` [PATCH AUTOSEL 5.1 17/39] ppp: mppe: Add softdep to arc4 Sasha Levin
2019-07-03 2:14 ` [PATCH AUTOSEL 5.1 18/39] net: stmmac: fixed new system time seconds value calculation Sasha Levin
2019-07-03 2:14 ` [PATCH AUTOSEL 5.1 19/39] net: stmmac: set IC bit when transmitting frames with HW timestamp Sasha Levin
2019-07-03 2:14 ` [PATCH AUTOSEL 5.1 20/39] sis900: fix TX completion Sasha Levin
2019-07-03 2:14 ` [PATCH AUTOSEL 5.1 21/39] ARM: dts: imx6ul: fix PWM[1-4] interrupts Sasha Levin
2019-07-03 2:14 ` [PATCH AUTOSEL 5.1 22/39] qmi_wwan: Fix out-of-bounds read Sasha Levin
2019-07-03 2:14 ` [PATCH AUTOSEL 5.1 23/39] pinctrl: mcp23s08: Fix add_data and irqchip_add_nested call order Sasha Levin
2019-07-03 2:14 ` [PATCH AUTOSEL 5.1 24/39] pinctrl: ocelot: fix gpio direction for pins after 31 Sasha Levin
2019-07-03 2:15 ` [PATCH AUTOSEL 5.1 25/39] pinctrl: ocelot: fix pinmuxing " Sasha Levin
2019-07-03 2:15 ` [PATCH AUTOSEL 5.1 26/39] dm table: don't copy from a NULL pointer in realloc_argv() Sasha Levin
2019-07-03 2:15 ` [PATCH AUTOSEL 5.1 27/39] dm verity: use message limit for data block corruption message Sasha Levin
2019-07-03 2:15 ` [PATCH AUTOSEL 5.1 28/39] x86/boot/64: Fix crash if kernel image crosses page table boundary Sasha Levin
2019-07-03 2:15 ` [PATCH AUTOSEL 5.1 29/39] x86/boot/64: Add missing fixup_pointer() for next_early_pgt access Sasha Levin
2019-07-03 2:15 ` [PATCH AUTOSEL 5.1 30/39] HID: chicony: add another quirk for PixArt mouse Sasha Levin
2019-07-03 2:15 ` [PATCH AUTOSEL 5.1 31/39] HID: uclogic: Add support for Huion HS64 tablet Sasha Levin
2019-07-03 2:15 ` [PATCH AUTOSEL 5.1 32/39] HID: multitouch: Add pointstick support for ALPS Touchpad Sasha Levin
2019-07-03 2:15 ` [PATCH AUTOSEL 5.1 33/39] pinctrl: mediatek: Ignore interrupts that are wake only during resume Sasha Levin
2019-07-03 2:15 ` [PATCH AUTOSEL 5.1 34/39] net: aquantia: fix vlans not working over bridged network Sasha Levin
2019-07-03 2:15 ` [PATCH AUTOSEL 5.1 35/39] cpu/hotplug: Fix out-of-bounds read when setting fail state Sasha Levin
2019-07-03 2:15 ` [PATCH AUTOSEL 5.1 36/39] pinctrl: mediatek: Update cur_mask in mask/mask ops Sasha Levin
2019-07-03 2:15 ` [PATCH AUTOSEL 5.1 37/39] mm/oom_kill.c: fix uninitialized oc->constraint Sasha Levin
2019-07-03 2:15 ` [PATCH AUTOSEL 5.1 38/39] fork,memcg: alloc_thread_stack_node needs to set tsk->stack Sasha Levin
2019-07-03 2:15 ` [PATCH AUTOSEL 5.1 39/39] linux/kernel.h: fix overflow for DIV_ROUND_UP_ULL Sasha Levin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20190703021514.17727-3-sashal@kernel.org \
--to=sashal@kernel.org \
--cc=bgregg@netflix.com \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=stable@vger.kernel.org \
--cc=willy@infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).