Skip to content

Commit 1cffdf9

Browse files
Yifei-Liugregkh
authored andcommitted
jffs2: correct logic when creating a hole in jffs2_write_begin
[ Upstream commit 23892d3 ] Bug description and fix: 1. Write data to a file, say all 1s from offset 0 to 16. 2. Truncate the file to a smaller size, say 8 bytes. 3. Write new bytes (say 2s) from an offset past the original size of the file, say at offset 20, for 4 bytes. This is supposed to create a "hole" in the file, meaning that the bytes from offset 8 (where it was truncated above) up to the new write at offset 20, should all be 0s (zeros). 4. Flush all caches using "echo 3 > /proc/sys/vm/drop_caches" (or unmount and remount) the f/s. 5. Check the content of the file. It is wrong. The 1s that used to be between bytes 9 and 16, before the truncation, have REAPPEARED (they should be 0s). We wrote a script and helper C program to reproduce the bug (reproduce_jffs2_write_begin_issue.sh, write_file.c, and Makefile). We can make them available to anyone. The above example is shown when writing a small file within the same first page. But the bug happens for larger files, as long as steps 1, 2, and 3 above all happen within the same page. The problem was traced to the jffs2_write_begin code, where it goes into an 'if' statement intended to handle writes past the current EOF (i.e., writes that may create a hole). The code computes a 'pageofs' that is the floor of the write position (pos), aligned to the page size boundary. In other words, 'pageofs' will never be larger than 'pos'. The code then sets the internal jffs2_raw_inode->isize to the size of max(current inode size, pageofs) but that is wrong: the new file size should be the 'pos', which is larger than both the current inode size and pageofs. Similarly, the code incorrectly sets the internal jffs2_raw_inode->dsize to the difference between the pageofs minus current inode size; instead it should be the current pos minus the current inode size. Finally, inode->i_size was also set incorrectly. The patch below fixes this bug. The bug was discovered using a new tool for finding f/s bugs using model checking, called MCFS (Model Checking File Systems). Signed-off-by: Yifei Liu <[email protected]> Signed-off-by: Erez Zadok <[email protected]> Signed-off-by: Manish Adkar <[email protected]> Signed-off-by: Richard Weinberger <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent 71285be commit 1cffdf9

File tree

1 file changed

+7
-8
lines changed

1 file changed

+7
-8
lines changed

fs/jffs2/file.c

+7-8
Original file line numberDiff line numberDiff line change
@@ -137,19 +137,18 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping,
137137
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
138138
struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
139139
pgoff_t index = pos >> PAGE_SHIFT;
140-
uint32_t pageofs = index << PAGE_SHIFT;
141140
int ret = 0;
142141

143142
jffs2_dbg(1, "%s()\n", __func__);
144143

145-
if (pageofs > inode->i_size) {
146-
/* Make new hole frag from old EOF to new page */
144+
if (pos > inode->i_size) {
145+
/* Make new hole frag from old EOF to new position */
147146
struct jffs2_raw_inode ri;
148147
struct jffs2_full_dnode *fn;
149148
uint32_t alloc_len;
150149

151-
jffs2_dbg(1, "Writing new hole frag 0x%x-0x%x between current EOF and new page\n",
152-
(unsigned int)inode->i_size, pageofs);
150+
jffs2_dbg(1, "Writing new hole frag 0x%x-0x%x between current EOF and new position\n",
151+
(unsigned int)inode->i_size, (uint32_t)pos);
153152

154153
ret = jffs2_reserve_space(c, sizeof(ri), &alloc_len,
155154
ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
@@ -169,10 +168,10 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping,
169168
ri.mode = cpu_to_jemode(inode->i_mode);
170169
ri.uid = cpu_to_je16(i_uid_read(inode));
171170
ri.gid = cpu_to_je16(i_gid_read(inode));
172-
ri.isize = cpu_to_je32(max((uint32_t)inode->i_size, pageofs));
171+
ri.isize = cpu_to_je32((uint32_t)pos);
173172
ri.atime = ri.ctime = ri.mtime = cpu_to_je32(JFFS2_NOW());
174173
ri.offset = cpu_to_je32(inode->i_size);
175-
ri.dsize = cpu_to_je32(pageofs - inode->i_size);
174+
ri.dsize = cpu_to_je32((uint32_t)pos - inode->i_size);
176175
ri.csize = cpu_to_je32(0);
177176
ri.compr = JFFS2_COMPR_ZERO;
178177
ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
@@ -202,7 +201,7 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping,
202201
goto out_err;
203202
}
204203
jffs2_complete_reservation(c);
205-
inode->i_size = pageofs;
204+
inode->i_size = pos;
206205
mutex_unlock(&f->sem);
207206
}
208207

0 commit comments

Comments
 (0)