lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Wed, 02 Oct 2013 21:38:32 +0400
From:	Maxim Patlasov <MPatlasov@...allels.com>
To:	miklos@...redi.hu
Cc:	fuse-devel@...ts.sourceforge.net, linux-fsdevel@...r.kernel.org,
	linux-kernel@...r.kernel.org
Subject: [PATCH 2/4] fuse: writepages: crop secondary requests

If writeback happens while fuse is in FUSE_NOWRITE condition, the request
will be queued but not processed immediately (see fuse_flush_writepages()).
Until FUSE_NOWRITE becomes relaxed, more writebacks can happen. They will
be queued as "secondary" requests to that first ("primary") request.

Existing implementation crops only primary request. This is not correct
because a subsequent extending write(2) may increase i_size and then secondary
requests won't be cropped properly. The result would be stale data written to
the server to a file offset where zeros must be.

Similar problem may happen if secondary requests are attached to an in-flight
request that was already cropped.

The patch solves the issue by cropping all secondary requests in
fuse_writepage_end(). Thanks to Miklos for idea.

Signed-off-by: Maxim Patlasov <MPatlasov@...allels.com>
---
 fs/fuse/file.c |   52 +++++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 41 insertions(+), 11 deletions(-)

diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 575e44f..a3c7123 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -1435,6 +1435,22 @@ static void fuse_writepage_finish(struct fuse_conn *fc, struct fuse_req *req)
 	wake_up(&fi->page_waitq);
 }
 
+/* Returns zero if the request is truncated off completely */
+static inline loff_t fuse_crop_request(struct fuse_req *req, loff_t size)
+{
+	struct fuse_write_in *inarg = &req->misc.write.in;
+	__u64 data_size = inarg->size ? : req->num_pages * PAGE_CACHE_SIZE;
+
+	if (inarg->offset + data_size <= size)
+		inarg->size = data_size;
+	else if (inarg->offset < size)
+		inarg->size = size - inarg->offset;
+	else
+		return 0;
+
+	return inarg->size;
+}
+
 /* Called under fc->lock, may release and reacquire it */
 static void fuse_send_writepage(struct fuse_conn *fc, struct fuse_req *req)
 __releases(fc->lock)
@@ -1442,22 +1458,14 @@ __acquires(fc->lock)
 {
 	struct fuse_inode *fi = get_fuse_inode(req->inode);
 	loff_t size = i_size_read(req->inode);
-	struct fuse_write_in *inarg = &req->misc.write.in;
-	__u64 data_size = req->num_pages * PAGE_CACHE_SIZE;
 
 	if (!fc->connected)
 		goto out_free;
 
-	if (inarg->offset + data_size <= size) {
-		inarg->size = data_size;
-	} else if (inarg->offset < size) {
-		inarg->size = size - inarg->offset;
-	} else {
-		/* Got truncated off completely */
-		goto out_free;
-	}
+	req->in.args[1].size = fuse_crop_request(req, size);
+	if (req->in.args[1].size == 0)
+		goto out_free; /* Got truncated off completely */
 
-	req->in.args[1].size = inarg->size;
 	fi->writectr++;
 	fuse_request_send_background_locked(fc, req);
 	return;
@@ -1495,13 +1503,24 @@ static void fuse_writepage_end(struct fuse_conn *fc, struct fuse_req *req)
 {
 	struct inode *inode = req->inode;
 	struct fuse_inode *fi = get_fuse_inode(inode);
+	struct fuse_req *drop = NULL;
 
 	mapping_set_error(inode->i_mapping, req->out.h.error);
 	spin_lock(&fc->lock);
 	while (req->misc.write.next) {
 		struct fuse_req *next = req->misc.write.next;
+		struct fuse_write_in *inarg = &req->misc.write.in;
+		loff_t size = inarg->offset + inarg->size;
+
 		req->misc.write.next = next->misc.write.next;
 		next->misc.write.next = NULL;
+
+		if (fuse_crop_request(next, size) == 0) {
+			next->misc.write.next = drop;
+			drop = next;
+			continue;
+		}
+
 		list_add(&next->writepages_entry, &fi->writepages);
 		list_add_tail(&next->list, &fi->queued_writes);
 		fuse_flush_writepages(inode);
@@ -1510,6 +1529,17 @@ static void fuse_writepage_end(struct fuse_conn *fc, struct fuse_req *req)
 	fuse_writepage_finish(fc, req);
 	spin_unlock(&fc->lock);
 	fuse_writepage_free(fc, req);
+
+	while (drop) {
+		struct fuse_req *next = drop->misc.write.next;
+		struct backing_dev_info *bdi =
+			drop->inode->i_mapping->backing_dev_info;
+		dec_bdi_stat(bdi, BDI_WRITEBACK);
+		dec_zone_page_state(drop->pages[0], NR_WRITEBACK_TEMP);
+		fuse_writepage_free(fc, drop);
+		fuse_put_request(fc, drop);
+		drop = next;
+	}
 }
 
 static struct fuse_file *fuse_write_file_get(struct fuse_conn *fc,

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ