Commit message: Redo minor cleanup in new_rbtree
CVE: CVE-2022-1444
CWE: CWE-416
Vulnerability description: heap-use-after-free in GitHub repository radareorg/radare2 prior to 5.7.0. This vulnerability is capable of inducing denial of service.

Project: radare2
File: new_rbtree.c

static void _set_link(RRBNode *parent, RRBNode *child, const int dir) {
	if (parent) {
		parent->link[dir] = child;
	}
	if (child) {
		child->parent = parent;
	}
}

Project: radare2
File: new_rbtree.c

R_API RRBNode *r_rbnode_prev(RRBNode *node) {
	r_return_val_if_fail (node, NULL);
	if (node->link[0]) {
		node = node->link[0];
		while (node->link[1]) {
			node = node->link[1];
		}
		return node;
	}
	RRBNode *parent = node->parent;
	while (parent && parent->link[0] == node) {
		node = parent;
		parent = node->parent;
	}
	return parent;
}

Project: radare2
File: new_rbtree.c

static RRBNode *_rot_twice(RRBNode *root, int dir) {
	r_return_val_if_fail (root, NULL);

	_set_link (root, _rot_once (root->link[!dir], !dir), !dir);
	return _rot_once (root, dir);
}

Project: radare2
File: new_rbtree.c

R_API void r_crbtree_clear(RRBTree *tree) {
	r_return_if_fail (tree);
	RRBNode *iter = tree->root, *save = NULL;

	// Rotate away the left links into a linked list so that
	// we can perform iterative destruction of the rbtree
	while (iter) {
		if (!iter->link[0]) {
			save = iter->link[1];
			if (tree->free) {
				tree->free (iter->data);
			}
			free (iter);
			tree->size--;
		} else {
			save = iter->link[0];
			_set_link (iter, save->link[1], 0);
			_set_link (save, iter, 1);
		}
		iter = save;
	}
	tree->root = NULL;
}

Project: radare2
File: new_rbtree.c

R_API void r_crbtree_free(RRBTree *tree) {
	if (!tree) {
		return;
	}
	r_crbtree_clear (tree);
	free (tree);
}

Project: radare2
File: new_rbtree.c

R_API void *r_crbtree_take(RRBTree *tree, void *data, RRBComparator cmp, void *user) {
	r_return_val_if_fail (tree && data && tree->size && tree->root && cmp, NULL);

	RRBNode head; /* Fake tree root */
	memset (&head, 0, sizeof (RRBNode));
	RRBNode *q = &head, *p = NULL, *g = NULL;
	RRBNode *found = NULL;
	int dir = 1, last;

	_set_link (q, tree->root, 1);

	/* Find in-order predecessor */
	while (q->link[dir]) {
		last = dir;

		g = p;
		p = q;
		q = q->link[dir];

		dir = cmp (data, q->data, user);
		if (dir == 0 && !found) {
			found = q;
		}

		dir = (bool)(dir > 0);

		if (IS_RED (q) || IS_RED (q->link[dir])) {
			continue;
		}
		if (IS_RED (q->link[!dir])) {
			_set_link (p, _rot_once (q, dir), last);
			p = p->link[last];
		} else {
			RRBNode *sibling = p->link[!last];
			if (sibling) {
				if (!IS_RED (sibling->link[!last]) && !IS_RED (sibling->link[last])) {
					/* Color flip */
					p->red = 0;
					sibling->red = 1;
					q->red = 1;
				} else if (g) {
					int dir2 = (bool)(g->link[1] == p);

					if (IS_RED (sibling->link[last])) {
						_set_link (g, _rot_twice (p, last), dir2);
					} else {
						_set_link (g, _rot_once (p, last), dir2);
					}

					/* Ensure correct coloring */
					q->red = g->link[dir2]->red = 1;
					g->link[dir2]->link[0]->red = 0;
					g->link[dir2]->link[1]->red = 0;
				}
			}
		}
	}

	void *ret = NULL;
	/* Replace and remove if found */
	if (found) {
		_set_link (p, q->link[q->link[0] == NULL], p->link[1] == q);
		if (q != found) {
			q->link[0] = NULL;
			q->link[1] = NULL;
			q->parent = NULL;
			_exchange_nodes (found, q);
		}
		ret = found->data;
		free (found);
		tree->size--;
	}

	/* Update root node */
	tree->root = head.link[1];
	if (tree->root) {
		tree->root->red = 0;
		tree->root->parent = NULL;
	} else {
		r_return_val_if_fail (tree->size == 0, NULL);
	}
	return ret;
}

Project: radare2
File: new_rbtree.c

R_API bool r_crbtree_delete(RRBTree *tree, void *data, RRBComparator cmp, void *user) {
	r_return_val_if_fail (tree && data && tree->size && tree->root && cmp, false);
	data = r_crbtree_take (tree, data, cmp, user);
	if (tree->free) {
		tree->free (data);
	}
	return !!data;
}

Project: radare2
File: new_rbtree.c

static RRBNode *_rot_once(RRBNode *root, int dir) {
	r_return_val_if_fail (root, NULL);

	// save is new parent of root and root is parent of save's previous child
	RRBNode *save = root->link[!dir];
	_set_link (root, save->link[dir], !dir);
	_set_link (save, root, dir);

	root->red = 1;
	save->red = 0;

	return save;
}

Project: radare2
File: new_rbtree.c

R_API RRBNode *r_crbtree_first_node(RRBTree *tree) {
	r_return_val_if_fail (tree, NULL);
	if (!tree->root) {
		// empty tree
		return NULL;
	}
	RRBNode *node = tree->root;
	while (node->link[0]) {
		node = node->link[0];
	}
	return node;
}

Project: radare2
File: new_rbtree.c

static void _exchange_nodes(RRBNode *node_a, RRBNode *node_b) {
	if (!node_a || !node_b) {
		return;
	}
	RRBNode node_a_tmp, node_b_tmp;
	memcpy (&node_a_tmp, node_a, sizeof (RRBNode));
	memcpy (&node_b_tmp, node_b, sizeof (RRBNode));
	node_a->link[0] = node_b_tmp.link[0];
	node_a->link[1] = node_b_tmp.link[1];
	node_a->red = node_b_tmp.red;
	node_b->link[0] = node_a_tmp.link[0];
	node_b->link[1] = node_a_tmp.link[1];
	node_b->red = node_a_tmp.red;
	if (node_a->parent == node_b->parent) {
		if (node_a->parent) {
			if (node_a->parent->link[0] == node_a) {
				node_a->parent->link[0] = node_b;
				node_a->parent->link[1] = node_a;
			} else {
				node_a->parent->link[1] = node_b;
				node_a->parent->link[0] = node_a;
			}
		}
		if (node_a->link[0]) {
			node_a->link[0]->parent = node_a;
		}
		if (node_a->link[1]) {
			node_a->link[1]->parent = node_a;
		}
		if (node_b->link[0]) {
			node_b->link[0]->parent = node_b;
		}
		if (node_b->link[1]) {
			node_b->link[1]->parent = node_b;
		}
		return;
	}
	RRBNode *parent_a = node_a->parent;
	RRBNode *parent_b = node_b->parent;
	if (parent_a) {
		if (parent_a->link[0] == node_a) {
			parent_a->link[0] = node_b;
		} else {
			parent_a->link[1] = node_b;
		}
	}
	node_b->parent = parent_a;
	if (parent_b) {
		if (parent_b->link[0] == node_b) {
			parent_b->link[0] = node_a;
		} else {
			parent_b->link[1] = node_a;
		}
	}
	node_a->parent = parent_b;
	if (node_a->link[0]) {
		node_a->link[0]->parent = node_a;
	}
	if (node_a->link[1]) {
		node_a->link[1]->parent = node_a;
	}
	if (node_b->link[0]) {
		node_b->link[0]->parent = node_b;
	}
	if (node_b->link[1]) {
		node_b->link[1]->parent = node_b;
	}
}

Project: radare2
File: new_rbtree.c

R_API RRBNode *r_crbtree_last_node(RRBTree *tree) {
	r_return_val_if_fail (tree, NULL);
	if (!tree->root) {
		// empty tree
		return NULL;
	}
	RRBNode *node = tree->root;
	while (node->link[1]) {
		node = node->link[1];
	}
	return node;
}

Project: radare2
File: new_rbtree.c

R_API void *r_crbtree_find(RRBTree *tree, void *data, RRBComparator cmp, void *user) {
	r_return_val_if_fail (tree && cmp, NULL);
	RRBNode *node = r_crbtree_find_node (tree, data, cmp, user);
	return node ? node->data : NULL;
}

Project: radare2
File: new_rbtree.c

R_API bool r_crbtree_insert(RRBTree *tree, void *data, RRBComparator cmp, void *user) {
	r_return_val_if_fail (tree && data && cmp, false);
	bool inserted = false;

	if (!tree->root) {
		tree->root = _node_new (data, NULL);
		if (!tree->root) {
			return false;
		}
		inserted = true;
		goto out_exit;
	}

	RRBNode head; /* Fake tree root */
	memset (&head, 0, sizeof (RRBNode));
	RRBNode *g = NULL, *parent = &head; /* Grandparent & parent */
	RRBNode *p = NULL, *q = tree->root; /* Iterator & parent */
	int dir = 0, last = 0; /* Directions */

	_set_link (parent, q, 1);

	for (;;) {
		if (!q) {
			/* Insert a node at first null link(also set its parent link) */
			q = _node_new (data, p);
			if (!q) {
				return false;
			}
			p->link[dir] = q;
			inserted = true;
		} else if (IS_RED (q->link[0]) && IS_RED (q->link[1])) {
			/* Simple red violation: color flip */
			q->red = 1;
			q->link[0]->red = 0;
			q->link[1]->red = 0;
		}

		if (IS_RED (q) && IS_RED (p)) {
#if 0
			// coverity error, parent is never null
			/* Hard red violation: rotate */
			if (!parent) {
				return false;
			}
#endif
			int dir2 = parent->link[1] == g;
			if (q == p->link[last]) {
				_set_link (parent, _rot_once (g, !last), dir2);
			} else {
				_set_link (parent, _rot_twice (g, !last), dir2);
			}
		}

		if (inserted) {
			break;
		}

		last = dir;
		dir = cmp (data, q->data, user) >= 0;

		if (g) {
			parent = g;
		}

		g = p;
		p = q;
		q = q->link[dir];
	}

	/* Update root(it may different due to root rotation) */
	tree->root = head.link[1];

out_exit:
	/* Invariant: root is black */
	tree->root->red = 0;
	tree->root->parent = NULL;
	if (inserted) {
		tree->size++;
	}

	return inserted;
}

Project: radare2
File: new_rbtree.c

R_API RRBTree *r_crbtree_new(RRBFree freefn) {
	RRBTree *tree = R_NEW0 (RRBTree);
	if (tree) {
		tree->free = freefn;
	}
	return tree;
}

Project: radare2
File: new_rbtree.c

R_API RRBNode *r_crbtree_find_node(RRBTree *tree, void *data, RRBComparator cmp, void *user) {
	r_return_val_if_fail (tree && cmp, NULL);

	RRBNode *iter = tree->root;
	while (iter) {
		const int dir = cmp (data, iter->data, user);
		if (!dir) {
			return iter;
		}
		iter = iter->link[dir > 0];
	}
	return NULL;
}

Project: radare2
File: new_rbtree.c

static RRBNode *_node_new(void *data, RRBNode *parent) {
	RRBNode *node = R_NEW0 (RRBNode);
	r_return_val_if_fail (node, NULL);

	node->red = 1;
	node->data = data;
	node->parent = parent;

	return node;
}

Project: radare2
File: new_rbtree.c

R_API RRBNode *r_rbnode_next(RRBNode *node) {
	r_return_val_if_fail (node, NULL);
	if (node->link[1]) {
		node = node->link[1];
		while (node->link[0]) {
			node = node->link[0];
		}
		return node;
	}
	RRBNode *parent = node->parent;
	while (parent && parent->link[1] == node) {
		node = parent;
		parent = node->parent;
	}
	return parent;
}