/* DF-2761: sys_setlogin mutates s_login under a SHARED p_token
 * (kern_prot.c:1290-1292) so two concurrent setlogin(2) calls can interleave
 * their 32-byte memcpy, and a concurrent getlogin(2) can observe a torn
 * login name.  Root-gated (SYSCAP_NOPROC_SETLOGIN). */
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/param.h>

static volatile int stop;
static long torn, reads;
static char tbufA[MAXLOGNAME], tbufB[MAXLOGNAME];

static void *writerA(void *x){ (void)x; while(!stop) setlogin(tbufA); return NULL; }
static void *writerB(void *x){ (void)x; while(!stop) setlogin(tbufB); return NULL; }

static void *reader(void *x)
{
	char buf[MAXLOGNAME];
	(void)x;
	while (!stop) {
		memset(buf, 0, sizeof(buf));
		if (getlogin_r(buf, sizeof(buf)) == 0 || 1) {
			int a = 0, b = 0; size_t i;
			for (i = 0; i < sizeof(buf) && buf[i]; i++) {
				if (buf[i] == 'A') a = 1;
				if (buf[i] == 'B') b = 1;
			}
			reads++;
			if (a && b) {
				torn++;
				if (torn <= 5)
					fprintf(stderr, "[+] TORN LOGIN OBSERVED: %s\n", buf);
			}
		}
	}
	return NULL;
}

int main(void)
{
	pthread_t wa, wb, rd;
	memset(tbufA, 'A', sizeof(tbufA)-1); tbufA[sizeof(tbufA)-1] = 0;
	memset(tbufB, 'B', sizeof(tbufB)-1); tbufB[sizeof(tbufB)-1] = 0;

	printf("[*] racing setlogin(A*32) vs setlogin(B*32) with concurrent getlogin\n");
	pthread_create(&wa, NULL, writerA, NULL);
	pthread_create(&wb, NULL, writerB, NULL);
	pthread_create(&rd, NULL, reader, NULL);
	sleep(5);
	stop = 1;
	pthread_join(wa, NULL); pthread_join(wb, NULL); pthread_join(rd, NULL);
	printf("[*] %ld reads, %ld torn (mixed A/B) observations\n", reads, torn);
	printf("[%c] %s\n", torn?'+':'-', torn ? "TORN NAMES REPRODUCED" : "no torn name observed this run");
	return torn ? 0 : 1;
}
