/* DF-2909: sys_varsym_set replace is non-atomic remove-then-insert.
 * Two same-uid processes racing USER-level sets of one name can both pass
 * the remove and both insert -> duplicate shadowed TAILQ entries.
 * Detection: after the race, ONE unset must delete the var; if a value
 * still resolves afterwards, a second (duplicate) entry survived.
 * Also: invalid level (99) silently returns success.
 *
 * varsym syscalls: 450 set, 451 get, 452 list.
 * VARSYM_PROC=1, VARSYM_USER=2, VARSYM_SYS=3, VARSYM_PRISON=4.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/wait.h>
#include <errno.h>

#define SYS_varsym_set 450
#define SYS_varsym_get 451
#define SYS_varsym_list 452
#define VARSYM_USER 2
#define VARSYM_USER_MASK (1 << VARSYM_USER)

#define NAME "DFRACE2909"
#define NPROC 4
#define ITERS 60000

static int vset(int level, const char *name, const char *data)
{
	return syscall(SYS_varsym_set, level, name, data);
}
static int vget(int mask, const char *name, char *buf, int bufsize)
{
	return syscall(SYS_varsym_get, mask, name, buf, bufsize);
}

int main(void)
{
	char buf[256];
	int i, j, st, dup = 0;

	/* invalid level claim: returns 0 doing nothing */
	errno = 0;
	i = vset(99, NAME, "ignored");
	printf("invalid-level set rc=%d errno=%d (claim: silent success)\n",
	       i, errno);

	/* seed */
	if (vset(VARSYM_USER, NAME, "seed") != 0) {
		perror("seed set"); return 1;
	}
	memset(buf, 0, sizeof(buf));
	if (vget(VARSYM_USER_MASK, NAME, buf, sizeof(buf)) != 0 ||
	    strcmp(buf, "seed") != 0) {
		printf("seed get failed (buf=%s errno=%d)\n", buf, errno);
		return 1;
	}
	printf("seed ok: %s\n", buf);

	for (i = 0; i < NPROC; i++) {
		if (fork() == 0) {
			char v[64];
			for (j = 0; j < ITERS; j++) {
				snprintf(v, sizeof(v), "p%d-%d", i, j);
				vset(VARSYM_USER, NAME, v);
			}
			_exit(0);
		}
	}
	for (i = 0; i < NPROC; i++)
		wait(&st);

	/* one delete must remove the var entirely unless duplicates exist */
	vset(VARSYM_USER, NAME, NULL);
	memset(buf, 0, sizeof(buf));
	errno = 0;
	memset(buf, 0, sizeof(buf));
	errno = 0;
	i = vget(VARSYM_USER_MASK, NAME, buf, sizeof(buf));
	if (i == 0) {
		printf("DUPLICATE CONFIRMED: after one unset, %s still = \"%s\" (len %d)\n",
		       NAME, buf, i);
		dup = 1;
		/* cleanup remaining copy */
		vset(VARSYM_USER, NAME, NULL);
	} else {
		printf("no duplicate survived this run (single unset removed it)\n");
	}
	return dup ? 9 : 0;
}
