[LTP] [PATCH] kmsg01: fix race in SEEK_SET 0 test

Cyril Hrubis chrubis@suse.cz
Fri Mar 3 15:36:44 CET 2017


Hi!
> +static int read_msg_seqno(int fd, unsigned long *seqno)
> +{
> +	char msg[MAX_MSGSIZE];
> +
> +	TEST(read(fd, msg, sizeof(msg)));
> +	if (TEST_RETURN == -1 && TEST_ERRNO != EPIPE) {
> +		tst_brkm(TBROK|TTERRNO, cleanup,
> +			"failed to read /dev/kmsg");
> +	}

Shouldn't we return -1 here on EPIPE as well?

	if (TEST_ERRNO == EPIPE)
		return -1;

> +	if (get_msg_fields(msg, NULL, seqno) != 0) {
> +		tst_resm(TFAIL, "failed to parse seqid: %s", msg);
> +		return -1;
> +	}
> +
> +	return 0;
> +}
> +
>  static void test_seek(void)
>  {
> -	int k, j = NUM_READ_RETRY, fd;
> +	int j, fd, fd2;
>  	char msg[MAX_MSGSIZE];
> -	unsigned long seqno[2];
> +	unsigned long seqno[2], tmp;
> +	int done = 0;
>  
>  	/* 1. read() after SEEK_SET 0 returns same (first) message */
>  	tst_resm(TINFO, "TEST: seek SEEK_SET 0");
>  
> -	fd = open("/dev/kmsg", O_RDONLY | O_NONBLOCK);
> -	if (fd < 0)
> -		tst_brkm(TBROK|TERRNO, cleanup,	"failed to open /dev/kmsg");
> -
> -	while (j) {
> -		for (k = 0; k < 2; k++) {
> -			TEST(read(fd, msg, sizeof(msg)));
> -			if (TEST_RETURN == -1) {
> -				if (errno == EPIPE)
> -					break;
> -				else
> -					tst_brkm(TBROK|TTERRNO, cleanup,
> -						"failed to read /dev/kmsg");
> -			}
> -			if (get_msg_fields(msg, NULL, &seqno[k]) != 0)
> -				tst_resm(TFAIL, "failed to parse seqid: %s",
> -					msg);
> -			if (k == 0)
> -				if (lseek(fd, 0, SEEK_SET) == -1)
> -					tst_resm(TFAIL|TERRNO,
> -						"SEEK_SET 0 failed");
> +	for (j = 0; j < NUM_READ_RETRY && !done; j++) {
> +		/*
> +		 * j > 0 means we are trying again, because we most likely
> +		 * failed on read returning EPIPE - first message in buffer
> +		 * has been overwrittern. Give a second to whoever overwrote
> +		 * first message to finish.
> +		 */
> +		if (j)
> +			sleep(1);

What about changing it to 0.1s here? Or are there processes that keeps
feeding the kernel log buffer for a second or more?

> +		/*
> +		 * Open 2 fds. Use fd1 to read seqno1, then seek to
> +		 * begininng and read seqno2. Use fd2 to check if
> +		 * first entry in buffer got overwritten. If so,
> +		 * we'll have to repeat the test.
> +		 */
> +		fd = SAFE_OPEN(cleanup, "/dev/kmsg", O_RDONLY | O_NONBLOCK);
> +		fd2 = SAFE_OPEN(cleanup, "/dev/kmsg", O_RDONLY | O_NONBLOCK);
> +
> +		if (read_msg_seqno(fd, &seqno[0]))
> +			goto close_fds;
> +
> +		if (lseek(fd, 0, SEEK_SET) == -1) {
> +			tst_resm(TFAIL|TERRNO, "SEEK_SET 0 failed");
> +			goto done;

I wonder if this should be tst_brkm() rather than tst_resm(), since as
it is we would end up printing the TFAIL message below, or am I
mistaken?

>  		}
>  
> -		if (TEST_RETURN != -1)
> -			break;
> +		if (read_msg_seqno(fd, &seqno[1]))
> +			goto close_fds;
>  
> -		/* give a second to whoever overwrote first message to finish */
> -		sleep(1);
> -		j--;
> +		if (read_msg_seqno(fd2, &tmp))
> +			goto close_fds;
>  
> -		/* read above has returned EPIPE, reopen fd and try again */
> +done:
> +		done = 1;
> +close_fds:
>  		SAFE_CLOSE(cleanup, fd);
> -		fd = open("/dev/kmsg", O_RDONLY | O_NONBLOCK);
> -		if (fd < 0)
> -			tst_brkm(TBROK|TERRNO, cleanup,
> -				"failed to open /dev/kmsg");
> +		SAFE_CLOSE(cleanup, fd2);
>  	}
>  
> -	if (!j) {
> +	if (j == NUM_READ_RETRY) {
>  		tst_resm(TWARN, "exceeded: %d attempts", NUM_READ_RETRY);
>  	} else {
> -		if (seqno[0] != seqno[1])
> -			tst_resm(TFAIL, "SEEK_SET 0");
> -		else
> +		if (seqno[0] != seqno[1]) {
> +			tst_resm(TFAIL, "SEEK_SET 0, %lu != %lu",
> +				seqno[0], seqno[1]);
> +		} else {
>  			tst_resm(TPASS, "SEEK_SET 0");
> +		}
>  	}
>  
>  	/* 2. messages after SEEK_END 0 shouldn't contain MSG_PREFIX */
> +	fd = SAFE_OPEN(cleanup, "/dev/kmsg", O_RDONLY | O_NONBLOCK);
>  	tst_resm(TINFO, "TEST: seek SEEK_END 0");
>  	if (lseek(fd, 0, SEEK_END) == -1)
>  		tst_resm(TFAIL|TERRNO, "lseek SEEK_END 0 failed");

Otherwise it looks OK.

-- 
Cyril Hrubis
chrubis@suse.cz


More information about the ltp mailing list