/* DF-2989 (userland mirror of tree.h scan_info_done): the walk
 * 'while (*infopp != scan) infopp = &(*infopp)->link;' has no NULL exit.
 * With a corrupted rbh_inprog list that does not contain our scan, the
 * walk dereferences NULL (or wild memory).  Compiled against the same
 * macros from sys/sys/tree.h (RB_SCAN_LOCK is a no-op outside _KERNEL).
 *
 * demo1: healthy list            -> clean unlink (both kernels)
 * demo2: head->rbh_inprog hijacked to an unrelated scan chain
 *        -> stock tree.h: SIGSEGV (unbounded walk off the chain end)
 * build: cc -O -o tree2989 tree2989.c -I/usr/src/sys
 */
#include <sys/tree.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

struct item {
	RB_ENTRY(item)	entry;
	int		key;
};
static int	item_cmp(struct item *a, struct item *b);
RB_HEAD(item_tree, item);
RB_PROTOTYPE(item_tree, item, entry, item_cmp);
RB_GENERATE(item_tree, item, entry, item_cmp);

static int
item_cmp(struct item *a, struct item *b)
{
	return (a->key - b->key);
}

int
main(int argc, char **argv)
{
	struct item_tree head;
	struct item items[8];
	struct item_tree_scan_info info, other;
	int i;

	RB_INIT(&head);
	memset(&info, 0, sizeof(info));
	memset(&other, 0, sizeof(other));
	for (i = 0; i < 8; i++) {
		items[i].key = i * 10;
		RB_INSERT(item_tree, &head, &items[i]);
	}

	/* demo 1: healthy scan lifecycle */
	item_tree_scan_info_link(&info, &head);
	item_tree_scan_info_done(&info, &head);
	printf("demo1: healthy scan_info_done ok, inprog=%p\n",
	       (void *)head.rbh_inprog);

	/* demo 2: hijacked inprog list (prior corruption elsewhere) */
	other.link = NULL;
	item_tree_scan_info_link(&info, &head);
	/* corruption strikes AFTER our scan registered: head now points at
	 * an unrelated chain that does NOT contain &info */
	head.rbh_inprog = &other;
	printf("demo2: inprog=%p (hijacked), calling scan_info_done...\n",
	       (void *)head.rbh_inprog);
	fflush(stdout);
	item_tree_scan_info_done(&info, &head);
	printf("demo2: returned cleanly (fixed tree.h)\n");
	return 0;
}
