[8/9] fs: don't allow kernel reads and writes without iter ops
diff mbox series

Message ID 20200626075836.1998185-9-hch@lst.de
State New
Headers show
Series
  • [1/9] fs: refactor new_sync_read
Related show

Commit Message

Christoph Hellwig June 26, 2020, 7:58 a.m. UTC
Don't allow calling ->read or ->write with set_fs as a preparation for
killing off set_fs.  While I've not triggered any of these cases in my
setups as all the usual suspect (file systems, pipes, sockets, block
devices, system character devices) use the iter ops this is almost
going to be guaranteed to eventuall break something, so print a detailed
error message helping to debug such cases.  The fix will be to switch the
affected driver to use the iter ops.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/read_write.c | 33 ++++++++++++++++++---------------
 1 file changed, 18 insertions(+), 15 deletions(-)

Comments

Luis Chamberlain June 26, 2020, 12:27 p.m. UTC | #1
On Fri, Jun 26, 2020 at 09:58:35AM +0200, Christoph Hellwig wrote:
> diff --git a/fs/read_write.c b/fs/read_write.c
> index e765c95ff3440d..ae463bcadb6906 100644
> --- a/fs/read_write.c
> +++ b/fs/read_write.c
> @@ -420,6 +420,18 @@ ssize_t iter_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos,
>  	return ret;
>  }
>  
> +static void warn_unsupported(struct file *file, const char *op)
> +{
> +	char pathname[128], *path;

Why 128? How about kstrdup_quotable_file()?

  Luis
Christoph Hellwig June 26, 2020, 1:37 p.m. UTC | #2
On Fri, Jun 26, 2020 at 12:27:52PM +0000, Luis Chamberlain wrote:
> On Fri, Jun 26, 2020 at 09:58:35AM +0200, Christoph Hellwig wrote:
> > diff --git a/fs/read_write.c b/fs/read_write.c
> > index e765c95ff3440d..ae463bcadb6906 100644
> > --- a/fs/read_write.c
> > +++ b/fs/read_write.c
> > @@ -420,6 +420,18 @@ ssize_t iter_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos,
> >  	return ret;
> >  }
> >  
> > +static void warn_unsupported(struct file *file, const char *op)
> > +{
> > +	char pathname[128], *path;
> 
> Why 128? How about kstrdup_quotable_file()?

This is in the read/write path for the case where we did not end up
calling in the driver.  This is far less stack usage than any read/write
method would have used eventually.
Matthew Wilcox June 26, 2020, 1:51 p.m. UTC | #3
On Fri, Jun 26, 2020 at 09:58:35AM +0200, Christoph Hellwig wrote:
> +static void warn_unsupported(struct file *file, const char *op)
> +{
> +	char pathname[128], *path;
> +
> +	path = file_path(file, pathname, sizeof(pathname));
> +	if (IS_ERR(path))
> +		path = "(unknown)";
> +	pr_warn_ratelimited(
> +		"kernel %s not supported for file %s (pid: %d comm: %.20s)\n",
> +		op, path, current->pid, current->comm);
> +}
> +

how about just:

	pr_warn_ratelimited(
		"kernel %s not supported for file %pD4 (pid: %d comm: %.20s)\n",
			op, file, current->pid, current->comm);

also, is the pid really that interesting?
Kees Cook June 26, 2020, 9:05 p.m. UTC | #4
On Fri, Jun 26, 2020 at 02:51:47PM +0100, Matthew Wilcox wrote:
> On Fri, Jun 26, 2020 at 09:58:35AM +0200, Christoph Hellwig wrote:
> > +static void warn_unsupported(struct file *file, const char *op)
> > +{
> > +	char pathname[128], *path;
> > +
> > +	path = file_path(file, pathname, sizeof(pathname));
> > +	if (IS_ERR(path))
> > +		path = "(unknown)";
> > +	pr_warn_ratelimited(
> > +		"kernel %s not supported for file %s (pid: %d comm: %.20s)\n",
> > +		op, path, current->pid, current->comm);
> > +}
> > +
> 
> how about just:
> 
> 	pr_warn_ratelimited(
> 		"kernel %s not supported for file %pD4 (pid: %d comm: %.20s)\n",
> 			op, file, current->pid, current->comm);
> 
> also, is the pid really that interesting?

Yes, pid matters, especially when there may be many of something running
(e.g. rsync).
Christoph Hellwig June 27, 2020, 7:10 a.m. UTC | #5
On Fri, Jun 26, 2020 at 02:51:47PM +0100, Matthew Wilcox wrote:
> On Fri, Jun 26, 2020 at 09:58:35AM +0200, Christoph Hellwig wrote:
> > +static void warn_unsupported(struct file *file, const char *op)
> > +{
> > +	char pathname[128], *path;
> > +
> > +	path = file_path(file, pathname, sizeof(pathname));
> > +	if (IS_ERR(path))
> > +		path = "(unknown)";
> > +	pr_warn_ratelimited(
> > +		"kernel %s not supported for file %s (pid: %d comm: %.20s)\n",
> > +		op, path, current->pid, current->comm);
> > +}
> > +
> 
> how about just:
> 
> 	pr_warn_ratelimited(
> 		"kernel %s not supported for file %pD4 (pid: %d comm: %.20s)\n",
> 			op, file, current->pid, current->comm);

Sure, we could use %pD for a few less line of code and a little less
typesafety.  I'm not a huge fan of these custom, unchecked format
specifiers, but given that they exist we might a well use them.

Patch
diff mbox series

diff --git a/fs/read_write.c b/fs/read_write.c
index e765c95ff3440d..ae463bcadb6906 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -420,6 +420,18 @@  ssize_t iter_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos,
 	return ret;
 }
 
+static void warn_unsupported(struct file *file, const char *op)
+{
+	char pathname[128], *path;
+
+	path = file_path(file, pathname, sizeof(pathname));
+	if (IS_ERR(path))
+		path = "(unknown)";
+	pr_warn_ratelimited(
+		"kernel %s not supported for file %s (pid: %d comm: %.20s)\n",
+		op, path, current->pid, current->comm);
+}
+
 ssize_t __kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
 {
 	ssize_t ret;
@@ -431,13 +443,7 @@  ssize_t __kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
 
 	if (count > MAX_RW_COUNT)
 		count =  MAX_RW_COUNT;
-	if (file->f_op->read) {
-		mm_segment_t old_fs = get_fs();
-
-		set_fs(KERNEL_DS);
-		ret = file->f_op->read(file, (void __user *)buf, count, pos);
-		set_fs(old_fs);
-	} else if (file->f_op->read_iter) {
+	if (file->f_op->read_iter) {
 		struct kvec iov = { .iov_base = buf, .iov_len = count };
 		struct kiocb kiocb;
 		struct iov_iter iter;
@@ -448,6 +454,8 @@  ssize_t __kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
 		ret = file->f_op->read_iter(&kiocb, &iter);
 		*pos = kiocb.ki_pos;
 	} else {
+		if (file->f_op->read)
+			warn_unsupported(file, "read");
 		ret = -EINVAL;
 	}
 	if (ret > 0) {
@@ -532,14 +540,7 @@  ssize_t __kernel_write(struct file *file, const void *buf, size_t count,
 
 	if (count > MAX_RW_COUNT)
 		count =  MAX_RW_COUNT;
-	if (file->f_op->write) {
-		mm_segment_t old_fs = get_fs();
-
-		set_fs(KERNEL_DS);
-		ret = file->f_op->write(file, (__force const char __user *)buf,
-				count, pos);
-		set_fs(old_fs);
-	} else if (file->f_op->write_iter) {
+	if (file->f_op->write_iter) {
 		struct kvec iov = { .iov_base = (void *)buf, .iov_len = count };
 		struct kiocb kiocb;
 		struct iov_iter iter;
@@ -551,6 +552,8 @@  ssize_t __kernel_write(struct file *file, const void *buf, size_t count,
 		if (ret > 0)
 			*pos = kiocb.ki_pos;
 	} else {
+		if (file->f_op->write)
+			warn_unsupported(file, "write");
 		ret = -EINVAL;
 	}
 	if (ret > 0) {