[LTP] [PATCH 1/2] syscalls: new test writev07

Jan Stancek jstancek@redhat.com
Wed Oct 5 14:36:53 CEST 2016


Verify writev() behaviour with partially valid iovec list.
Kernel <4.8 used to shorten write up to first bad invalid
iovec. Starting with 4.8, a writev with short data (under
page size) is likely to get shorten to 0 bytes and return
EFAULT.

This test doesn't make assumptions how much will write get
shortened. It only tests that file content/offset after
syscall corresponds to return value of writev().

See: [RFC] writev() semantics with invalid iovec in the middle
     https://marc.info/?l=linux-kernel&m=147388891614289&w=2

This test is meant to be replacement for writev03/writev04
and part of writev01, which all deal with partially invalid
iovec lists, and all currently fail with 4.8 kernel.

Signed-off-by: Jan Stancek <jstancek@redhat.com>
---
 runtest/ltplite                             |   1 +
 runtest/stress.part3                        |   1 +
 runtest/syscalls                            |   1 +
 testcases/kernel/syscalls/.gitignore        |   1 +
 testcases/kernel/syscalls/writev/writev07.c | 163 ++++++++++++++++++++++++++++
 5 files changed, 167 insertions(+)
 create mode 100644 testcases/kernel/syscalls/writev/writev07.c

diff --git a/runtest/ltplite b/runtest/ltplite
index 42d9a039ad34..1b4c5f6ee279 100644
--- a/runtest/ltplite
+++ b/runtest/ltplite
@@ -1047,6 +1047,7 @@ writev03 writev03
 writev04 writev04
 writev05 writev05
 writev06 writev06
+writev07 writev07
 
 #DESCRIPTION:Memory Mgmt tests
 mm01 mmap001 -m 10000
diff --git a/runtest/stress.part3 b/runtest/stress.part3
index fa75b5c08526..effa5fd9b14e 100644
--- a/runtest/stress.part3
+++ b/runtest/stress.part3
@@ -908,6 +908,7 @@ writev03 writev03
 writev04 writev04
 writev05 writev05
 writev06 writev06
+writev07 writev07
 
 pty01 pty01
 hangup01 hangup01
diff --git a/runtest/syscalls b/runtest/syscalls
index 01839e83bf83..88f75971cb3d 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -1426,6 +1426,7 @@ writev03 writev03
 writev04 writev04
 writev05 writev05
 writev06 writev06
+writev07 writev07
 
 perf_event_open01 perf_event_open01
 perf_event_open02 perf_event_open02
diff --git a/testcases/kernel/syscalls/.gitignore b/testcases/kernel/syscalls/.gitignore
index a13dbb3c5a99..f2aeab45e5bb 100644
--- a/testcases/kernel/syscalls/.gitignore
+++ b/testcases/kernel/syscalls/.gitignore
@@ -1104,6 +1104,7 @@
 /writev/writev04
 /writev/writev05
 /writev/writev06
+/writev/writev07
 /fanotify/fanotify01
 /fanotify/fanotify02
 /fanotify/fanotify03
diff --git a/testcases/kernel/syscalls/writev/writev07.c b/testcases/kernel/syscalls/writev/writev07.c
new file mode 100644
index 000000000000..8677fc62a8b6
--- /dev/null
+++ b/testcases/kernel/syscalls/writev/writev07.c
@@ -0,0 +1,163 @@
+/*
+ *
+ *   Copyright (c) Linux Test Project, 2016
+ *
+ *   This program is free software;  you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
+ *   the GNU General Public License for more details.
+ */
+
+/*
+ * Test Description:
+ *   Verify writev() behaviour with partially valid iovec list.
+ *   Kernel <4.8 used to shorten write up to first bad invalid
+ *   iovec. Starting with 4.8, a writev with short data (under
+ *   page size) is likely to get shorten to 0 bytes and return
+ *   EFAULT.
+ *
+ *   This test doesn't make assumptions how much will write get
+ *   shortened. It only tests that file content/offset after
+ *   syscall corresponds to return value of writev().
+ *
+ *   See: [RFC] writev() semantics with invalid iovec in the middle
+ *        https://marc.info/?l=linux-kernel&m=147388891614289&w=2
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include "tst_test.h"
+
+#define TESTFILE "testfile"
+#define CHUNK 64
+#define BUFSIZE (CHUNK * 8)
+
+static void *bad_addr;
+
+static void dump_buf(unsigned char *buf, int len)
+{
+	int i;
+
+	for (i = 0; i < len; i++) {
+		printf("0x%02x ", *(buf + i));
+		if (i % 16 == 15)
+			printf("\n");
+	}
+	printf("\n");
+}
+
+static void test_partially_valid_iovec(int initial_file_offset)
+{
+	int i, fd;
+	unsigned char buffer[BUFSIZE], fpattern[BUFSIZE], tmp[BUFSIZE];
+	long off_after;
+	struct iovec wr_iovec[] = {
+		{ buffer + CHUNK, CHUNK * 2 },
+		{ bad_addr, CHUNK },
+		{ buffer + CHUNK * 3, CHUNK },
+		{ buffer + CHUNK * 2, CHUNK * 2 },
+	};
+
+	tst_res(TINFO, "starting test with initial file offset: %d ",
+		initial_file_offset);
+
+	for (i = 0; i < BUFSIZE; i++)
+		buffer[i] = i % (CHUNK - 1);
+
+	memset(fpattern, 0xff, BUFSIZE);
+	tst_fill_file(TESTFILE, 0xff, CHUNK, BUFSIZE / CHUNK);
+
+	fd = SAFE_OPEN(TESTFILE, O_RDWR, 0644);
+	SAFE_LSEEK(fd, initial_file_offset, SEEK_SET);
+	TEST(writev(fd, wr_iovec, ARRAY_SIZE(wr_iovec)));
+	off_after = (long) SAFE_LSEEK(fd, 0, SEEK_CUR);
+
+	/* bad errno */
+	if (TEST_RETURN == -1 && TEST_ERRNO != EFAULT) {
+		tst_res(TFAIL | TTERRNO, "unexpected errno");
+		SAFE_CLOSE(fd);
+		return;
+	}
+
+	/* nothing has been written */
+	if (TEST_RETURN == -1 && TEST_ERRNO == EFAULT) {
+		tst_res(TINFO, "got EFAULT");
+		/* initial file content remains untouched */
+		SAFE_LSEEK(fd, 0, SEEK_SET);
+		SAFE_READ(1, fd, tmp, BUFSIZE);
+		if (memcmp(tmp, fpattern, BUFSIZE))
+			tst_res(TFAIL, "file was written to");
+		else
+			tst_res(TPASS, "file stayed untouched");
+
+		/* offset hasn't changed */
+		if (off_after == initial_file_offset)
+			tst_res(TPASS, "offset stayed unchanged");
+		else
+			tst_res(TFAIL, "offset changed to %ld",
+				off_after);
+
+		SAFE_CLOSE(fd);
+		return;
+	}
+
+	/* writev() wrote more bytes than bytes preceding invalid iovec */
+	tst_res(TINFO, "writev() has written %ld bytes", TEST_RETURN);
+	if (TEST_RETURN > (long) wr_iovec[0].iov_base) {
+		tst_res(TFAIL, "writev wrote more than expected");
+		SAFE_CLOSE(fd);
+		return;
+	}
+
+	/* file content matches written bytes */
+	SAFE_LSEEK(fd, initial_file_offset, SEEK_SET);
+	SAFE_READ(1, fd, tmp, TEST_RETURN);
+	if (memcmp(tmp, wr_iovec[0].iov_base, TEST_RETURN) == 0) {
+		tst_res(TPASS, "file has expected content");
+	} else {
+		tst_res(TFAIL, "file has unexpected content");
+		tst_res(TINFO, "expected:");
+		dump_buf(wr_iovec[0].iov_base, TEST_RETURN);
+		tst_res(TINFO, "actual file content:");
+		dump_buf(tmp, TEST_RETURN);
+	}
+
+	/* file offset has been updated according to written bytes */
+	if (off_after == initial_file_offset + TEST_RETURN)
+		tst_res(TPASS, "offset at %ld as expected", off_after);
+	else
+		tst_res(TFAIL, "offset unexpected %ld", off_after);
+
+	SAFE_CLOSE(fd);
+}
+
+static void test_writev(void)
+{
+	test_partially_valid_iovec(0);
+	test_partially_valid_iovec(CHUNK + 1);
+	test_partially_valid_iovec(getpagesize());
+	test_partially_valid_iovec(getpagesize() + 1);
+}
+
+static void setup(void)
+{
+	bad_addr = SAFE_MMAP(0, 1, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS,
+			0, 0);
+}
+
+static struct tst_test test = {
+	.tid = "access05",
+	.needs_tmpdir = 1,
+	.setup = setup,
+	.test_all = test_writev,
+};
-- 
1.8.3.1



More information about the ltp mailing list