/*
 * DF-3079 — procfs /proc/<pid>/mem exclusive-open (O_EXCL) flag is never
 * cleared when the last write-open did not use O_EXCL, permanently denying
 * O_EXCL opens for that pid until the vnode is reclaimed.
 *
 * sys/vfs/procfs/procfs_vnops.c:
 *   procfs_open  (lines ~184-205): on FWRITE open, pfs->pfs_flags =
 *       a_mode & (FWRITE|O_EXCL)  -> a plain O_RDWR open sets FWRITE only.
 *   procfs_close (lines ~239-241): clears (FWRITE|O_EXCL) ONLY IF
 *       (pfs->pfs_flags & O_EXCL) is already set -> plain-write close
 *       leaves FWRITE latched.
 *   next O_EXCL open: ((pfs_flags & FWRITE) && (mode & O_EXCL)) -> EBUSY.
 *
 * Build: cc -o procfs_sticky_excl procfs_sticky_excl.c
 * Run:   ./procfs_sticky_excl     (unprivileged is fine)
 * Expected (bug):  "BUG-REPRODUCED: EBUSY on O_EXCL after non-EXCL write open+close"
 */
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>

static int openmem(pid_t pid, int flags)
{
	char p[64];
	snprintf(p, sizeof(p), "/proc/%d/mem", (int)pid);
	errno = 0;
	return open(p, flags, 0);
}

int main(void)
{
	pid_t child;
	int fd, status, bug_hit;

	/* fresh same-uid child pid, never opened before -> control case */
	child = fork();
	if (child == 0) {
		pause();
		_exit(0);
	}
	if (child < 0) { perror("fork"); return 2; }

	/* Step 1: plain write open of OWN mem, no O_EXCL, then close */
	fd = openmem(getpid(), O_RDWR);
	printf("open(/proc/self/mem, O_RDWR)            = %-3d %s\n",
	    fd, fd < 0 ? strerror(errno) : "OK");
	if (fd >= 0)
		close(fd);

	/* Step 2: exclusive open of the same node -> expect the latched
	 * FWRITE flag to produce EBUSY forever (the bug) */
	fd = openmem(getpid(), O_RDWR | O_EXCL);
	printf("open(/proc/self/mem, O_RDWR|O_EXCL)     = %-3d %s\n",
	    fd, fd < 0 ? strerror(errno) : "OK");
	bug_hit = (fd < 0 && errno == EBUSY);
	if (fd >= 0)
		close(fd);

	/* Control: fresh child pid, O_EXCL open must succeed */
	fd = openmem(child, O_RDWR | O_EXCL);
	printf("open(/proc/child/mem, O_RDWR|O_EXCL)    = %-3d %s\n",
	    fd, fd < 0 ? strerror(errno) : "OK");
	if (fd >= 0)
		close(fd);

	kill(child, SIGKILL);
	waitpid(child, &status, 0);

	printf("%s\n", bug_hit ?
	    "BUG-REPRODUCED: EBUSY on O_EXCL after non-EXCL write open+close" :
	    "NOT-REPRODUCED: O_EXCL open still works");
	return bug_hit ? 0 : 1;
}
