diff options
author | Matthew Wilcox <willy@infradead.org> | 2018-06-25 06:56:50 -0400 |
---|---|---|
committer | Matthew Wilcox <willy@infradead.org> | 2018-09-29 22:47:48 -0400 |
commit | 66ee620f06f99d72475db6eb638559ba608c7dee (patch) | |
tree | 6014376eae1f939a364350975337301cfb10dbb5 /lib/idr.c | |
parent | 3d0186bb068e6cc6c23dc1d2f0b1cf64894c40ea (diff) | |
download | lwn-66ee620f06f99d72475db6eb638559ba608c7dee.tar.gz lwn-66ee620f06f99d72475db6eb638559ba608c7dee.zip |
idr: Permit any valid kernel pointer to be stored
An upcoming change to the encoding of internal entries will set the bottom
two bits to 0b10. Unfortunately, m68k only aligns some data structures
to 2 bytes, so the IDR will interpret them as internal entries and things
will go badly wrong.
Change the radix tree so that it stops either when the node indicates
that it's the bottom of the tree (shift == 0) or when the entry is not an
internal entry. This means we cannot insert an arbitrary kernel pointer
as a multiorder entry, but the IDR does not permit multiorder entries.
Annoyingly, this means the IDR can no longer take advantage of the radix
tree's ability to store a single entry at offset 0 without allocating
memory. A pointer which is 2-byte aligned cannot be stored directly in
the root as it would be indistinguishable from a node, so we must allocate
a node in order to store a 2-byte pointer at index 0. The idr_replace()
function does not take a GFP flags argument, so cannot allocate memory.
If a user inserts a 4-byte aligned pointer at index 0 and then replaces
it with a 2-byte aligned pointer, we must be able to store it.
Arbitrary pointer values are still not permitted; pointers of the
form 2 + (i * 4) for values of i between 0 and 1023 are reserved for
the implementation. These are not valid kernel pointers as they would
point into the zero page.
This change does cause a runtime memory consumption regression for
the IDA. I will recover that later.
Signed-off-by: Matthew Wilcox <willy@infradead.org>
Tested-by: Guenter Roeck <linux@roeck-us.net>
Diffstat (limited to 'lib/idr.c')
-rw-r--r-- | lib/idr.c | 4 |
1 files changed, 0 insertions, 4 deletions
diff --git a/lib/idr.c b/lib/idr.c index fab2fd5bc326..729e381e23b4 100644 --- a/lib/idr.c +++ b/lib/idr.c @@ -39,8 +39,6 @@ int idr_alloc_u32(struct idr *idr, void *ptr, u32 *nextid, unsigned int base = idr->idr_base; unsigned int id = *nextid; - if (WARN_ON_ONCE(radix_tree_is_internal_node(ptr))) - return -EINVAL; if (WARN_ON_ONCE(!(idr->idr_rt.gfp_mask & ROOT_IS_IDR))) idr->idr_rt.gfp_mask |= IDR_RT_MARKER; @@ -295,8 +293,6 @@ void *idr_replace(struct idr *idr, void *ptr, unsigned long id) void __rcu **slot = NULL; void *entry; - if (WARN_ON_ONCE(radix_tree_is_internal_node(ptr))) - return ERR_PTR(-EINVAL); id -= idr->idr_base; entry = __radix_tree_lookup(&idr->idr_rt, id, &node, &slot); |