Skip to content

Commit 1cbbd99

Browse files
dhowellsgregkh
authored andcommitted
assoc_array: Fix a buggy node-splitting case
commit ea6789980fdaa610d7eb63602c746bf6ec70cd2b upstream. This fixes CVE-2017-12193. Fix a case in the assoc_array implementation in which a new leaf is added that needs to go into a node that happens to be full, where the existing leaves in that node cluster together at that level to the exclusion of new leaf. What needs to happen is that the existing leaves get moved out to a new node, N1, at level + 1 and the existing node needs replacing with one, N0, that has pointers to the new leaf and to N1. The code that tries to do this gets this wrong in two ways: (1) The pointer that should've pointed from N0 to N1 is set to point recursively to N0 instead. (2) The backpointer from N0 needs to be set correctly in the case N0 is either the root node or reached through a shortcut. Fix this by removing this path and using the split_node path instead, which achieves the same end, but in a more general way (thanks to Eric Biggers for spotting the redundancy). The problem manifests itself as: BUG: unable to handle kernel NULL pointer dereference at 0000000000000010 IP: assoc_array_apply_edit+0x59/0xe5 Fixes: 3cb9895 ("Add a generic associative array implementation.") Reported-and-tested-by: WU Fan <u3536072@connect.hku.hk> Signed-off-by: David Howells <dhowells@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 2c99438 commit 1cbbd99

1 file changed

Lines changed: 17 additions & 34 deletions

File tree

lib/assoc_array.c

Lines changed: 17 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -598,21 +598,31 @@ static bool assoc_array_insert_into_terminal_node(struct assoc_array_edit *edit,
598598
if ((edit->segment_cache[ASSOC_ARRAY_FAN_OUT] ^ base_seg) == 0)
599599
goto all_leaves_cluster_together;
600600

601-
/* Otherwise we can just insert a new node ahead of the old
602-
* one.
601+
/* Otherwise all the old leaves cluster in the same slot, but
602+
* the new leaf wants to go into a different slot - so we
603+
* create a new node (n0) to hold the new leaf and a pointer to
604+
* a new node (n1) holding all the old leaves.
605+
*
606+
* This can be done by falling through to the node splitting
607+
* path.
603608
*/
604-
goto present_leaves_cluster_but_not_new_leaf;
609+
pr_devel("present leaves cluster but not new leaf\n");
605610
}
606611

607612
split_node:
608613
pr_devel("split node\n");
609614

610-
/* We need to split the current node; we know that the node doesn't
611-
* simply contain a full set of leaves that cluster together (it
612-
* contains meta pointers and/or non-clustering leaves).
615+
/* We need to split the current node. The node must contain anything
616+
* from a single leaf (in the one leaf case, this leaf will cluster
617+
* with the new leaf) and the rest meta-pointers, to all leaves, some
618+
* of which may cluster.
619+
*
620+
* It won't contain the case in which all the current leaves plus the
621+
* new leaves want to cluster in the same slot.
613622
*
614623
* We need to expel at least two leaves out of a set consisting of the
615-
* leaves in the node and the new leaf.
624+
* leaves in the node and the new leaf. The current meta pointers can
625+
* just be copied as they shouldn't cluster with any of the leaves.
616626
*
617627
* We need a new node (n0) to replace the current one and a new node to
618628
* take the expelled nodes (n1).
@@ -717,33 +727,6 @@ static bool assoc_array_insert_into_terminal_node(struct assoc_array_edit *edit,
717727
pr_devel("<--%s() = ok [split node]\n", __func__);
718728
return true;
719729

720-
present_leaves_cluster_but_not_new_leaf:
721-
/* All the old leaves cluster in the same slot, but the new leaf wants
722-
* to go into a different slot, so we create a new node to hold the new
723-
* leaf and a pointer to a new node holding all the old leaves.
724-
*/
725-
pr_devel("present leaves cluster but not new leaf\n");
726-
727-
new_n0->back_pointer = node->back_pointer;
728-
new_n0->parent_slot = node->parent_slot;
729-
new_n0->nr_leaves_on_branch = node->nr_leaves_on_branch;
730-
new_n1->back_pointer = assoc_array_node_to_ptr(new_n0);
731-
new_n1->parent_slot = edit->segment_cache[0];
732-
new_n1->nr_leaves_on_branch = node->nr_leaves_on_branch;
733-
edit->adjust_count_on = new_n0;
734-
735-
for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++)
736-
new_n1->slots[i] = node->slots[i];
737-
738-
new_n0->slots[edit->segment_cache[0]] = assoc_array_node_to_ptr(new_n0);
739-
edit->leaf_p = &new_n0->slots[edit->segment_cache[ASSOC_ARRAY_FAN_OUT]];
740-
741-
edit->set[0].ptr = &assoc_array_ptr_to_node(node->back_pointer)->slots[node->parent_slot];
742-
edit->set[0].to = assoc_array_node_to_ptr(new_n0);
743-
edit->excised_meta[0] = assoc_array_node_to_ptr(node);
744-
pr_devel("<--%s() = ok [insert node before]\n", __func__);
745-
return true;
746-
747730
all_leaves_cluster_together:
748731
/* All the leaves, new and old, want to cluster together in this node
749732
* in the same slot, so we have to replace this node with a shortcut to

0 commit comments

Comments
 (0)