Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

assoc_array: Fix a buggy node-splitting case

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: 3cb989501c26 ("Add a generic associative array implementation.")
Reported-and-tested-by: WU Fan <u3536072@connect.hku.hk>
Signed-off-by: David Howells <dhowells@redhat.com>
Cc: stable@vger.kernel.org [v3.13-rc1+]
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

David Howells and committed by
Linus Torvalds
ea678998 78140234

+17 -34
+17 -34
lib/assoc_array.c
··· 598 598 if ((edit->segment_cache[ASSOC_ARRAY_FAN_OUT] ^ base_seg) == 0) 599 599 goto all_leaves_cluster_together; 600 600 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. 603 608 */ 604 - goto present_leaves_cluster_but_not_new_leaf; 609 + pr_devel("present leaves cluster but not new leaf\n"); 605 610 } 606 611 607 612 split_node: 608 613 pr_devel("split node\n"); 609 614 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. 613 622 * 614 623 * 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. 616 626 * 617 627 * We need a new node (n0) to replace the current one and a new node to 618 628 * take the expelled nodes (n1). ··· 725 715 edit->set[0].ptr = &assoc_array_ptr_to_shortcut(ptr)->next_node; 726 716 edit->excised_meta[0] = assoc_array_node_to_ptr(node); 727 717 pr_devel("<--%s() = ok [split node]\n", __func__); 728 - return true; 729 - 730 - present_leaves_cluster_but_not_new_leaf: 731 - /* All the old leaves cluster in the same slot, but the new leaf wants 732 - * to go into a different slot, so we create a new node to hold the new 733 - * leaf and a pointer to a new node holding all the old leaves. 734 - */ 735 - pr_devel("present leaves cluster but not new leaf\n"); 736 - 737 - new_n0->back_pointer = node->back_pointer; 738 - new_n0->parent_slot = node->parent_slot; 739 - new_n0->nr_leaves_on_branch = node->nr_leaves_on_branch; 740 - new_n1->back_pointer = assoc_array_node_to_ptr(new_n0); 741 - new_n1->parent_slot = edit->segment_cache[0]; 742 - new_n1->nr_leaves_on_branch = node->nr_leaves_on_branch; 743 - edit->adjust_count_on = new_n0; 744 - 745 - for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) 746 - new_n1->slots[i] = node->slots[i]; 747 - 748 - new_n0->slots[edit->segment_cache[0]] = assoc_array_node_to_ptr(new_n0); 749 - edit->leaf_p = &new_n0->slots[edit->segment_cache[ASSOC_ARRAY_FAN_OUT]]; 750 - 751 - edit->set[0].ptr = &assoc_array_ptr_to_node(node->back_pointer)->slots[node->parent_slot]; 752 - edit->set[0].to = assoc_array_node_to_ptr(new_n0); 753 - edit->excised_meta[0] = assoc_array_node_to_ptr(node); 754 - pr_devel("<--%s() = ok [insert node before]\n", __func__); 755 718 return true; 756 719 757 720 all_leaves_cluster_together: