1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 *
4 * Copyright (c) Linux Test Project, 2016
5 */
6
7 /*
8 * Test Description:
9 * Verify writev() behaviour with partially valid iovec list.
10 * Kernel <4.8 used to shorten write up to first bad invalid
11 * iovec. Starting with 4.8, a writev with short data (under
12 * page size) is likely to get shorten to 0 bytes and return
13 * EFAULT.
14 *
15 * This test doesn't make assumptions how much will write get
16 * shortened. It only tests that file content/offset after
17 * syscall corresponds to return value of writev().
18 *
19 * See: [RFC] writev() semantics with invalid iovec in the middle
20 * https://marc.info/?l=linux-kernel&m=147388891614289&w=2
21 */
22
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <stdio.h>
26 #include <sys/mman.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <sys/uio.h>
30 #include "tst_test.h"
31
32 #define TESTFILE "testfile"
33 #define CHUNK 64
34 #define BUFSIZE (CHUNK * 4)
35
36 static void *bad_addr;
37
test_partially_valid_iovec(int initial_file_offset)38 static void test_partially_valid_iovec(int initial_file_offset)
39 {
40 int i, fd;
41 unsigned char buffer[BUFSIZE], fpattern[BUFSIZE], tmp[BUFSIZE];
42 long off_after;
43 struct iovec wr_iovec[] = {
44 { buffer, CHUNK },
45 { bad_addr, CHUNK },
46 { buffer + CHUNK, CHUNK },
47 { buffer + CHUNK * 2, CHUNK },
48 };
49
50 tst_res(TINFO, "starting test with initial file offset: %d ",
51 initial_file_offset);
52
53 for (i = 0; i < BUFSIZE; i++)
54 buffer[i] = i % (CHUNK - 1);
55
56 memset(fpattern, 0xff, BUFSIZE);
57 tst_fill_file(TESTFILE, 0xff, CHUNK, BUFSIZE / CHUNK);
58
59 fd = SAFE_OPEN(TESTFILE, O_RDWR, 0644);
60 SAFE_LSEEK(fd, initial_file_offset, SEEK_SET);
61 TEST(writev(fd, wr_iovec, ARRAY_SIZE(wr_iovec)));
62 off_after = (long) SAFE_LSEEK(fd, 0, SEEK_CUR);
63
64 /* bad errno */
65 if (TST_RET == -1 && TST_ERR != EFAULT) {
66 tst_res(TFAIL | TTERRNO, "unexpected errno");
67 SAFE_CLOSE(fd);
68 return;
69 }
70
71 /* nothing has been written */
72 if (TST_RET == -1 && TST_ERR == EFAULT) {
73 tst_res(TINFO, "got EFAULT");
74 /* initial file content remains untouched */
75 SAFE_LSEEK(fd, 0, SEEK_SET);
76 SAFE_READ(1, fd, tmp, BUFSIZE);
77 if (memcmp(tmp, fpattern, BUFSIZE))
78 tst_res(TFAIL, "file was written to");
79 else
80 tst_res(TPASS, "file stayed untouched");
81
82 /* offset hasn't changed */
83 if (off_after == initial_file_offset)
84 tst_res(TPASS, "offset stayed unchanged");
85 else
86 tst_res(TFAIL, "offset changed to %ld",
87 off_after);
88
89 SAFE_CLOSE(fd);
90 return;
91 }
92
93 /* writev() wrote more bytes than bytes preceding invalid iovec */
94 tst_res(TINFO, "writev() has written %ld bytes", TST_RET);
95 if (TST_RET > (long) wr_iovec[0].iov_len) {
96 tst_res(TFAIL, "writev wrote more than expected");
97 SAFE_CLOSE(fd);
98 return;
99 }
100
101 /* file content matches written bytes */
102 SAFE_LSEEK(fd, initial_file_offset, SEEK_SET);
103 SAFE_READ(1, fd, tmp, TST_RET);
104 if (memcmp(tmp, wr_iovec[0].iov_base, TST_RET) == 0) {
105 tst_res(TPASS, "file has expected content");
106 } else {
107 tst_res(TFAIL, "file has unexpected content");
108 tst_res_hexd(TFAIL, wr_iovec[0].iov_base, TST_RET,
109 "expected:");
110 tst_res_hexd(TFAIL, tmp, TST_RET,
111 "actual file content:");
112 }
113
114 /* file offset has been updated according to written bytes */
115 if (off_after == initial_file_offset + TST_RET)
116 tst_res(TPASS, "offset at %ld as expected", off_after);
117 else
118 tst_res(TFAIL, "offset unexpected %ld", off_after);
119
120 SAFE_CLOSE(fd);
121 }
122
test_writev(void)123 static void test_writev(void)
124 {
125 test_partially_valid_iovec(0);
126 test_partially_valid_iovec(CHUNK + 1);
127 test_partially_valid_iovec(getpagesize());
128 test_partially_valid_iovec(getpagesize() + 1);
129 }
130
setup(void)131 static void setup(void)
132 {
133 bad_addr = SAFE_MMAP(0, 1, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS,
134 0, 0);
135 }
136
137 static struct tst_test test = {
138 .needs_tmpdir = 1,
139 .setup = setup,
140 .test_all = test_writev,
141 };
142