[PATCHv2,1/1] ext4: don't put symlink in pagecache into highmem

Message ID 20180206190938.172792-1-jinqian@android.com
State New
Headers show
Series
  • [PATCHv2,1/1] ext4: don't put symlink in pagecache into highmem
Related show

Commit Message

Jin Qian Feb. 6, 2018, 7:09 p.m.
From: Jin Qian <jinqian@google.com>

partial backport from 21fc61c73c3903c4c312d0802da01ec2b323d174 upstream
to v4.4 to prevent virt_to_page on highmem.

ext4_encrypted_follow_link uses kmap() for cpage
  caddr = kmap(cpage);

_ext4_fname_disk_to_usr calls virt_to_page on the kmapped address.
  _ext4_fname_disk_to_usr()
    ext4_fname_decrypt()
      sg_init_one()
        sg_init_one(&src_sg, iname->name, iname->len);
          sg_set_page(sg, virt_to_page(buf), buflen, offset_in_page(buf));

Cc: stable@vger.kernel.org
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Jin Qian <jinqian@google.com>
Signed-off-by: Jin Qian <jinqian@android.com>
---
 fs/ext4/inode.c    |  1 +
 fs/ext4/namei.c    |  1 +
 fs/ext4/symlink.c  | 10 +++-------
 fs/inode.c         |  6 ++++++
 include/linux/fs.h |  1 +
 5 files changed, 12 insertions(+), 7 deletions(-)

Comments

Greg KH Feb. 6, 2018, 8:39 p.m. | #1
On Tue, Feb 06, 2018 at 11:09:37AM -0800, Jin Qian wrote:
> From: Jin Qian <jinqian@google.com>
> 
> partial backport from 21fc61c73c3903c4c312d0802da01ec2b323d174 upstream
> to v4.4 to prevent virt_to_page on highmem.
> 
> ext4_encrypted_follow_link uses kmap() for cpage
>   caddr = kmap(cpage);
> 
> _ext4_fname_disk_to_usr calls virt_to_page on the kmapped address.
>   _ext4_fname_disk_to_usr()
>     ext4_fname_decrypt()
>       sg_init_one()
>         sg_init_one(&src_sg, iname->name, iname->len);
>           sg_set_page(sg, virt_to_page(buf), buflen, offset_in_page(buf));
> 
> Cc: stable@vger.kernel.org
> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
> Signed-off-by: Jin Qian <jinqian@google.com>
> Signed-off-by: Jin Qian <jinqian@android.com>

Ted, any objection to this patch?

thanks,

greg k-h
Theodore Ts'o Feb. 6, 2018, 11:11 p.m. | #2
On Tue, Feb 06, 2018 at 12:39:53PM -0800, Greg KH wrote:
> On Tue, Feb 06, 2018 at 11:09:37AM -0800, Jin Qian wrote:
> > From: Jin Qian <jinqian@google.com>
> > 
> > partial backport from 21fc61c73c3903c4c312d0802da01ec2b323d174 upstream
> > to v4.4 to prevent virt_to_page on highmem.
>
> Ted, any objection to this patch?

No objections with my ext4 hat on.

It should be noted though that this is a partial backport because it
only fixes ext4, while Al's original upstream fix addressed a much
larger set of file systems.  In the Android kernel the f2fs fix had
been backported separately.  But for the upstream kernel, it *might*
be the case that we should try backporting the original commit so that
in case there is some other general purpose distribution decides (a)
to base their system on 4.4, and (b) support a 32-bit kernel, they get
the more general bug fixes which applies for btrfs, isofs, ocfs2, nfs,
etc.

I haevn't been paying attention to what LTS kernels general purpose
distro's are using, so I don't know how important this would be.  And
if there are companies like Cloudflare which are using upstream LTS
kernel, it seems unlikely they would want to use a 32-bit kernel,
so.... shrug.  Greg, I'll let you decide if you want to backport the
full commit or not.

(We had a similar discussion on the AOSP kernel, and came to the
conclusion that we only needed to make the patch support ext4.  No one
was going to test the other file systems besides ext4 and f2fs,
anyway.  But the calculus might be different might be different for
the general upstream LTS kernel.)

				- Ted
Eric Biggers Feb. 6, 2018, 11:38 p.m. | #3
On Tue, Feb 06, 2018 at 06:11:49PM -0500, Theodore Ts'o wrote:
> On Tue, Feb 06, 2018 at 12:39:53PM -0800, Greg KH wrote:
> > On Tue, Feb 06, 2018 at 11:09:37AM -0800, Jin Qian wrote:
> > > From: Jin Qian <jinqian@google.com>
> > > 
> > > partial backport from 21fc61c73c3903c4c312d0802da01ec2b323d174 upstream
> > > to v4.4 to prevent virt_to_page on highmem.
> >
> > Ted, any objection to this patch?
> 
> No objections with my ext4 hat on.
> 
> It should be noted though that this is a partial backport because it
> only fixes ext4, while Al's original upstream fix addressed a much
> larger set of file systems.  In the Android kernel the f2fs fix had
> been backported separately.  But for the upstream kernel, it *might*
> be the case that we should try backporting the original commit so that
> in case there is some other general purpose distribution decides (a)
> to base their system on 4.4, and (b) support a 32-bit kernel, they get
> the more general bug fixes which applies for btrfs, isofs, ocfs2, nfs,
> etc.
> 
> I haevn't been paying attention to what LTS kernels general purpose
> distro's are using, so I don't know how important this would be.  And
> if there are companies like Cloudflare which are using upstream LTS
> kernel, it seems unlikely they would want to use a 32-bit kernel,
> so.... shrug.  Greg, I'll let you decide if you want to backport the
> full commit or not.
> 
> (We had a similar discussion on the AOSP kernel, and came to the
> conclusion that we only needed to make the patch support ext4.  No one
> was going to test the other file systems besides ext4 and f2fs,
> anyway.  But the calculus might be different might be different for
> the general upstream LTS kernel.)
> 

Well, the main point of backporting this change is to fix symlink decryption on
32-bit systems.  So, it would be needed on both ext4 and f2fs.  Jin, it might be
a good idea to fix f2fs in this patch at well, since unlike the AOSP kernels,
the LTS kernels do not have the latest f2fs backported to them.

I don't think backporting this change for other filesystems is particularly
important, since if I understand correctly, the reasons that Al made the change
originally were:

- to allow following symlinks in RCU mode, but that's not implemented in old
  kernels

- to prevent a process from using up all kmaps and deadlocking the system, which
  I'm not sure is a real problem (someone would need to try to put together a
  reproducer), but if so it would probably just be a local device of service.

Also if we actually backported the full commit there are follow-on fixes such as
e8ecde25f5e that would be needed as well but might be missed.

Thanks,

- Eric
Theodore Ts'o Feb. 7, 2018, 2:16 a.m. | #4
On Tue, Feb 06, 2018 at 03:38:09PM -0800, Eric Biggers wrote:
> I don't think backporting this change for other filesystems is particularly
> important, since if I understand correctly, the reasons that Al made the change
> originally were:
> 
> - to allow following symlinks in RCU mode, but that's not implemented in old
>   kernels

Yup.

> - to prevent a process from using up all kmaps and deadlocking the system, which
>   I'm not sure is a real problem (someone would need to try to put together a
>   reproducer), but if so it would probably just be a local device of service.

.. and *that's* only a problem on 32-bit systems.  And aside from
Android, it's unclear to me how much we need to support 32-bit systems
on upstream LTS kernels.  I suppose there might be Rasperry PI's which
are 32-bits and which might want to use btrfs.  Personally I'm not
sure we should care all that much, but others who care more about LTS
kernels and 32-bit systems might have a different opinion.

> Also if we actually backported the full commit there are follow-on fixes such as
> e8ecde25f5e that would be needed as well but might be missed.

Good point.

					- Ted

Patch

diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 4df1cb19a243..f0cabc8c96cb 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -4417,6 +4417,7 @@  struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
 			inode->i_op = &ext4_symlink_inode_operations;
 			ext4_set_aops(inode);
 		}
+		inode_nohighmem(inode);
 	} else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) ||
 	      S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
 		inode->i_op = &ext4_special_inode_operations;
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 4c36dca486cc..32960b3ecd4f 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -3151,6 +3151,7 @@  static int ext4_symlink(struct inode *dir,
 	if ((disk_link.len > EXT4_N_BLOCKS * 4)) {
 		if (!encryption_required)
 			inode->i_op = &ext4_symlink_inode_operations;
+		inode_nohighmem(inode);
 		ext4_set_aops(inode);
 		/*
 		 * We cannot call page_symlink() with transaction started
diff --git a/fs/ext4/symlink.c b/fs/ext4/symlink.c
index e8e7af62ac95..287c3980fa0b 100644
--- a/fs/ext4/symlink.c
+++ b/fs/ext4/symlink.c
@@ -45,7 +45,7 @@  static const char *ext4_encrypted_follow_link(struct dentry *dentry, void **cook
 		cpage = read_mapping_page(inode->i_mapping, 0, NULL);
 		if (IS_ERR(cpage))
 			return ERR_CAST(cpage);
-		caddr = kmap(cpage);
+		caddr = page_address(cpage);
 		caddr[size] = 0;
 	}
 
@@ -75,16 +75,12 @@  static const char *ext4_encrypted_follow_link(struct dentry *dentry, void **cook
 	/* Null-terminate the name */
 	if (res <= plen)
 		paddr[res] = '\0';
-	if (cpage) {
-		kunmap(cpage);
+	if (cpage)
 		page_cache_release(cpage);
-	}
 	return *cookie = paddr;
 errout:
-	if (cpage) {
-		kunmap(cpage);
+	if (cpage)
 		page_cache_release(cpage);
-	}
 	kfree(paddr);
 	return ERR_PTR(res);
 }
diff --git a/fs/inode.c b/fs/inode.c
index b0edef500590..b95615f3fc50 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -2028,3 +2028,9 @@  void inode_set_flags(struct inode *inode, unsigned int flags,
 				  new_flags) != old_flags));
 }
 EXPORT_SYMBOL(inode_set_flags);
+
+void inode_nohighmem(struct inode *inode)
+{
+	mapping_set_gfp_mask(inode->i_mapping, GFP_USER);
+}
+EXPORT_SYMBOL(inode_nohighmem);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index c8decb7075d6..f746a59fcc88 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -3066,5 +3066,6 @@  static inline bool dir_relax(struct inode *inode)
 }
 
 extern bool path_noexec(const struct path *path);
+extern void inode_nohighmem(struct inode *inode);
 
 #endif /* _LINUX_FS_H */