Re: [PATCH] ecryptfs: Fix inodes not being evicted until unmount

2021-02-02 Thread Jeffrey Mitchell
On Sat, Jan 30, 2021 at 11:06:40AM -0600, Tyler Hicks wrote:
> Hey Jeffrey and Dan - thanks for the patch! Unfortunately, I think this
> would allow the eCryptfs inode's nlink count to get out of sync with the
> lower inode's nlink count in the case of direct manipulation to the
> lower filesystem.

Hmm. What if I instead synchronize it before calling vfs_unlink(), then
call drop_nlink() if vfs_unlink() succeeds?

> Is the condition that you're trying to fix a result of going through the
> this code path?
> 
>  ecryptfs_unlink() -> ecryptfs_do_unlink() -> vfs_unlink() -> nfs_unlink() -> 
> nfs_sillyrename() -> nfs_async_unlink()

Yes, that is the code path that causes it.

V/R,
Jeffrey Mitchell


[PATCH] ecryptfs: fix kernel panic with null dev_name

2021-02-26 Thread Jeffrey Mitchell
When mounting eCryptfs, a null "dev_name" argument to ecryptfs_mount()
causes a kernel panic if the parsed options are valid. The easiest way to
reproduce this is to call mount() from userspace with an existing
eCryptfs mount's options and a "source" argument of 0.

Error out if "dev_name" is null in ecryptfs_mount()

Signed-off-by: Jeffrey Mitchell 
---
 fs/ecryptfs/main.c | 6 ++
 1 file changed, 6 insertions(+)

diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c
index e63259fdef28..b2f6a1937d23 100644
--- a/fs/ecryptfs/main.c
+++ b/fs/ecryptfs/main.c
@@ -492,6 +492,12 @@ static struct dentry *ecryptfs_mount(struct 
file_system_type *fs_type, int flags
goto out;
}
 
+   if (!dev_name) {
+   rc = -EINVAL;
+   err = "Device name cannot be null";
+   goto out;
+   }
+
rc = ecryptfs_parse_options(sbi, raw_data, &check_ruid);
if (rc) {
err = "Error parsing options";
-- 
2.25.1



[PATCH] ecryptfs: add rename flag support

2021-02-26 Thread Jeffrey Mitchell
Currently, ecryptfs_rename() returns EINVAL if any flags are passed in.
However, if the lower filesystem has support for those flags, it doesn't
require any additional complexity to take advantage of it in eCryptfs.

Add flag support to ecryptfs_rename() by passing them through to the lower
filesystem

Signed-off-by: Jeffrey Mitchell 
---
 fs/ecryptfs/inode.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index 18e9285fbb4c..647afc2fd754 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -601,9 +601,6 @@ ecryptfs_rename(struct user_namespace *mnt_userns, struct 
inode *old_dir,
struct inode *target_inode;
struct renamedata rd = {};
 
-   if (flags)
-   return -EINVAL;
-
lower_old_dir_dentry = ecryptfs_dentry_to_lower(old_dentry->d_parent);
lower_new_dir_dentry = ecryptfs_dentry_to_lower(new_dentry->d_parent);
 
@@ -636,6 +633,7 @@ ecryptfs_rename(struct user_namespace *mnt_userns, struct 
inode *old_dir,
rd.new_mnt_userns   = &init_user_ns;
rd.new_dir  = d_inode(lower_new_dir_dentry);
rd.new_dentry   = lower_new_dentry;
+   rd.flags= flags;
rc = vfs_rename(&rd);
if (rc)
goto out_lock;
-- 
2.25.1



[PATCH] nfs: Fix getxattr kernel panic and memory overflow

2020-08-05 Thread Jeffrey Mitchell
Move the buffer size check to decode_attr_security_label() before memcpy()
Only call memcpy() if the buffer is large enough

Signed-off-by: Jeffrey Mitchell 
---
 fs/nfs/nfs4proc.c | 2 --
 fs/nfs/nfs4xdr.c  | 5 -
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 2e2dac29a9e9..45e0585e0667 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -5845,8 +5845,6 @@ static int _nfs4_get_security_label(struct inode *inode, 
void *buf,
return ret;
if (!(fattr.valid & NFS_ATTR_FATTR_V4_SECURITY_LABEL))
return -ENOENT;
-   if (buflen < label.len)
-   return -ERANGE;
return 0;
 }
 
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 47817ef0aadb..50162e0a959c 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -4166,7 +4166,10 @@ static int decode_attr_security_label(struct xdr_stream 
*xdr, uint32_t *bitmap,
return -EIO;
if (len < NFS4_MAXLABELLEN) {
if (label) {
-   memcpy(label->label, p, len);
+   if (label->len && label->len < len)
+   return -ERANGE;
+   if (label->len)
+   memcpy(label->label, p, len);
label->len = len;
label->pi = pi;
label->lfs = lfs;
-- 
2.25.1



[PATCH] nfs: Fix getxattr kernel panic and memory overflow

2020-08-05 Thread Jeffrey Mitchell
_nfs4_get_security_label() and decode_attr_security_label() run severe
risks with memory management and do not fully implement their
functionality. In the case that the buffer and length are both NULL, which
according to the getxattr man page should simply return the length of the
attribute, decode_attr_security_label() will kernel panic trying to write
to the null pointer. If the buffer length is nonzero but below the size of
the attribute, decode_attr_security_label() will write the data anyway,
overflowing the buffer, and it isn't until later in
_nfs4_get_security_label() that -ERANGE gets returned.

Here is some of the kernel panic output that I reproduced:
BUG: kernel NULL pointer dereference, address: 
[...]
RIP: 0010:__memcpy+0x12/0x20
[...]
Call Trace:
 decode_getfattr_attrs+0xb1f/0xdc0
 ? set_next_entity+0x8e/0x180
 decode_getfattr_generic.constprop.0+0x10f/0x210
 ? rpc_decode_header+0x570/0x570
 nfs4_xdr_dec_getattr+0x94/0xa0
[...]
 _nfs4_get_security_label+0x134/0x180
 ? _cond_resched+0x10/0x20
 ? __kmalloc+0x1f6/0x200
 nfs4_xattr_get_nfs4_label+0x89/0x120
 __vfs_getxattr+0x4e/0x70
 ecryptfs_getxattr_lower+0x4a/0x70
 ecryptfs_xattr_get+0x23/0x24
 __vfs_getxattr+0x4e/0x70
 sb_finish_set_opts+0x12c/0x240
 selinux_set_mnt_opts+0x288/0x6a0
 security_sb_set_mnt_opts+0x40/0x60
 vfs_get_tree+0x57/0xb0
 do_mount+0x742/0x9b0
 __x64_sys_mount+0x89/0xc0
 do_syscall_64+0x3e/0x70

- Jeffrey

Jeffrey Mitchell (1):
  nfs: Fix getxattr kernel panic and memory overflow

 fs/nfs/nfs4proc.c | 2 --
 fs/nfs/nfs4xdr.c  | 5 -
 2 files changed, 4 insertions(+), 3 deletions(-)

-- 
2.25.1



[PATCH] ecryptfs: Fix inodes not being evicted until unmount

2020-12-18 Thread Jeffrey Mitchell
On asynchronous base filesystems like NFS, eCryptFS leaves inodes for
deleted files in the cache until unmounting. Change call in
ecryptfs_do_unlink() from set_nlink() to drop_nlink() in order to reliably
evict inodes from the cache even on top of NFS.

Signed-off-by: Dan Robertson 
Signed-off-by: Jeffrey Mitchell 
---
 fs/ecryptfs/inode.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index e23752d..f7594b6 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -147,7 +147,7 @@ static int ecryptfs_do_unlink(struct inode *dir, struct 
dentry *dentry,
goto out_unlock;
}
fsstack_copy_attr_times(dir, lower_dir_inode);
-   set_nlink(inode, ecryptfs_inode_to_lower(inode)->i_nlink);
+   drop_nlink(inode);
inode->i_ctime = dir->i_ctime;
 out_unlock:
dput(lower_dentry);
-- 
2.7.4



Re: [PATCH] ecryptfs: Fix inodes not being evicted until unmount

2020-12-18 Thread Jeffrey Mitchell
Steps to reproduce issue:
Mount nfs
Inside the nfs mount, create back and front directories for ecryptfs
Use ecryptfs to mount one directory onto the other
Create a few files inside the ecryptfs mount
Set ftrace to monitor ecryptfs_do_unlink() and ecryptfs_evict_inode()
Delete files
Unmount ecryptfs

ftrace output before patch:
# tracer: function_graph
#
# CPU  DURATION  FUNCTION CALLS
# | |   | |   |   |   |
 1)   |  /* Tracing enabled */
 1)   |  /* Writing to files */
 1)   |  /* Unlinking first file */
 0) * 27804.34 us |  ecryptfs_do_unlink();
 1)   |  /* rm'ing second file */
 --
 0)  unlink-1384   =>rm-1385
 --

 0) * 32828.63 us |  ecryptfs_do_unlink();
 1)   |  /* Unmounting eCryptFS */
 --
 1)  trace-a-1365  =>  umount-1387
 --

 1)   2.408 us|  ecryptfs_evict_inode();
 1)   8.914 us|  ecryptfs_evict_inode();
 1)   3.344 us|  ecryptfs_evict_inode();

ftrace output after patch:
# tracer: function_graph
#
# CPU  DURATION  FUNCTION CALLS
# | |   | |   |   |   |
 0)   |  /* Tracing enabled */
 0)   |  /* Writing to files */
 0)   |  /* Unlinking first file */
 1) * 24728.81 us |  ecryptfs_do_unlink();
 1) + 20.923 us   |  ecryptfs_evict_inode();
 0)   |  /* rm'ing second file */
 --
 1)  unlink-1333   =>rm-1334
 --

 1) * 41093.71 us |  ecryptfs_do_unlink();
 1) + 11.384 us   |  ecryptfs_evict_inode();
 0)   |  /* Unmounting eCryptFS */
 --
 0)  trace-a-1314  =>  umount-1336
 --

 0)   2.986 us|  ecryptfs_evict_inode();

On Fri, Dec 18, 2020 at 01:07:30PM -0600, Jeffrey Mitchell wrote:
> On asynchronous base filesystems like NFS, eCryptFS leaves inodes for
> deleted files in the cache until unmounting. Change call in
> ecryptfs_do_unlink() from set_nlink() to drop_nlink() in order to reliably
> evict inodes from the cache even on top of NFS.
> 
> Signed-off-by: Dan Robertson 
> Signed-off-by: Jeffrey Mitchell 
> ---
>  fs/ecryptfs/inode.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
> index e23752d..f7594b6 100644
> --- a/fs/ecryptfs/inode.c
> +++ b/fs/ecryptfs/inode.c
> @@ -147,7 +147,7 @@ static int ecryptfs_do_unlink(struct inode *dir, struct 
> dentry *dentry,
>   goto out_unlock;
>   }
>   fsstack_copy_attr_times(dir, lower_dir_inode);
> - set_nlink(inode, ecryptfs_inode_to_lower(inode)->i_nlink);
> + drop_nlink(inode);
>   inode->i_ctime = dir->i_ctime;
>  out_unlock:
>   dput(lower_dentry);
> -- 
> 2.7.4
> 


[PATCH] xfs: set inode size after creating symlink

2021-01-21 Thread Jeffrey Mitchell
When XFS creates a new symlink, it writes its size to disk but not to the
VFS inode. This causes i_size_read() to return 0 for that symlink until
it is re-read from disk, for example when the system is rebooted.

I found this inconsistency while protecting directories with eCryptFS.
The command "stat path/to/symlink/in/ecryptfs" will report "Size: 0" if
the symlink was created after the last reboot on an XFS root.

Call i_size_write() in xfs_symlink()

Signed-off-by: Jeffrey Mitchell 
---
 fs/xfs/xfs_symlink.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/fs/xfs/xfs_symlink.c b/fs/xfs/xfs_symlink.c
index 1f43fd7f3209..c835827ae389 100644
--- a/fs/xfs/xfs_symlink.c
+++ b/fs/xfs/xfs_symlink.c
@@ -300,6 +300,7 @@ xfs_symlink(
}
ASSERT(pathlen == 0);
}
+   i_size_write(VFS_I(ip), ip->i_d.di_size);
 
/*
 * Create the directory entry for the symlink.
-- 
2.25.1



Re: [PATCH] xfs: set inode size after creating symlink

2021-01-21 Thread Jeffrey Mitchell
On Thu, Jan 21, 2021 at 10:41:37AM -0800, Darrick J. Wong wrote:
> Do directories have the same problem?

Yes, I just checked in a VM. While ecryptfs does call vfs_getattr(), it
only uses the "blocks" value to supplement the data from an identical
generic_fillattr() call to what ecryptfs_getattr_link() uses. The reported
size still comes from i_size_read().

V/R,
Jeffrey Mitchell


[PATCH] nfs: Fix security label length not being reset

2020-09-14 Thread Jeffrey Mitchell
In nfs_readdir_page_filler(), reset security label buffer length before
every reuse

Signed-off-by: Jeffrey Mitchell 
---
 fs/nfs/dir.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index a12f42e7d8c7..5ff6af4478a5 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -579,6 +579,9 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, 
struct nfs_entry *en
xdr_set_scratch_buffer(&stream, page_address(scratch), PAGE_SIZE);
 
do {
+   if (entry->label)
+   entry->label->len = NFS4_MAXLABELLEN;
+
status = xdr_decode(desc, entry, &stream);
if (status != 0) {
if (status == -EAGAIN)
-- 
2.25.1



[PATCH] nfs: Fix security label length not being reset

2020-09-14 Thread Jeffrey Mitchell
nfs_readdir_page_filler() iterates over entries in a directory, reusing
the same security label buffer, but does not reset the buffer's length.
This causes decode_attr_security_label() to return -ERANGE if an entry's
security label is longer than the previous one's. This error, in
nfs4_decode_dirent(), only gets passed up as -EAGAIN, which causes another
failed attempt to copy into the buffer. The second error is ignored and
the remaining entries do not show up in ls, specifically the getdents64()
syscall.

Reproduce by creating multiple files in NFS and giving one of the later
files a longer security label. ls will not see that file nor any that are
added afterwards, though they will exist on the backend.

- Jeffrey

Jeffrey Mitchell (1):
  nfs: Fix security label length not being reset

 fs/nfs/dir.c | 3 +++
 1 file changed, 3 insertions(+)

-- 
2.25.1



[PATCH v2] nfs: Fix security label length not being reset

2020-09-15 Thread Jeffrey Mitchell
nfs_readdir_page_filler() iterates over entries in a directory, reusing
the same security label buffer, but does not reset the buffer's length.
This causes decode_attr_security_label() to return -ERANGE if an entry's
security label is longer than the previous one's. This error, in
nfs4_decode_dirent(), only gets passed up as -EAGAIN, which causes another
failed attempt to copy into the buffer. The second error is ignored and
the remaining entries do not show up in ls, specifically the getdents64()
syscall.

Reproduce by creating multiple files in NFS and giving one of the later
files a longer security label. ls will not see that file nor any that are
added afterwards, though they will exist on the backend.

In nfs_readdir_page_filler(), reset security label buffer length before
every reuse

Signed-off-by: Jeffrey Mitchell 
---
v2: Added explanation from cover letter as requested by J. Bruce Fields


 fs/nfs/dir.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index e732580fe47b..cb52db9a0cfb 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -579,6 +579,9 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, 
struct nfs_entry *en
xdr_set_scratch_buffer(&stream, page_address(scratch), PAGE_SIZE);
 
do {
+   if (entry->label)
+   entry->label->len = NFS4_MAXLABELLEN;
+
status = xdr_decode(desc, entry, &stream);
if (status != 0) {
if (status == -EAGAIN)
-- 
2.25.1



[PATCH] ecryptfs: Fix inodes not being evicted until unmount

2020-05-08 Thread Jeffrey Mitchell
On asynchronous base filesystems like NFS, eCryptFS leaves inodes for
deleted files in the cache until unmounting. Change call in
ecryptfs_do_unlink() from set_nlink() to drop_nlink() in order to reliably
evict inodes from the cache even on top of NFS.

Signed-off-by: Dan Robertson 
Signed-off-by: Jeffrey Mitchell 
---
 fs/ecryptfs/inode.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index e23752d..f7594b6 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -147,7 +147,7 @@ static int ecryptfs_do_unlink(struct inode *dir, struct 
dentry *dentry,
goto out_unlock;
}
fsstack_copy_attr_times(dir, lower_dir_inode);
-   set_nlink(inode, ecryptfs_inode_to_lower(inode)->i_nlink);
+   drop_nlink(inode);
inode->i_ctime = dir->i_ctime;
 out_unlock:
dput(lower_dentry);
-- 
2.7.4



Re: [PATCH] ecryptfs: Fix inodes not being evicted until unmount

2020-05-08 Thread Jeffrey Mitchell
Steps to reproduce issue:
Mount nfs
Inside the nfs mount, create back and front directories for ecryptfs
Use ecryptfs to mount one directory onto the other
Create a few files inside the ecryptfs mount
Set ftrace to monitor ecryptfs_do_unlink() and ecryptfs_evict_inode()
Delete files
Unmount ecryptfs

ftrace output before patch:
# tracer: function_graph
#
# CPU  DURATION  FUNCTION CALLS
# | |   | |   |   |   |
 1)   |  /* Tracing enabled */
 1)   |  /* Writing to files */
 1)   |  /* Unlinking first file */
 0) * 27804.34 us |  ecryptfs_do_unlink();
 1)   |  /* rm'ing second file */
 --
 0)  unlink-1384   =>rm-1385
 --
 
 0) * 32828.63 us |  ecryptfs_do_unlink();
 1)   |  /* Unmounting eCryptFS */
 --
 1)  trace-a-1365  =>  umount-1387
 --
 
 1)   2.408 us|  ecryptfs_evict_inode();
 1)   8.914 us|  ecryptfs_evict_inode();
 1)   3.344 us|  ecryptfs_evict_inode();

ftrace output after patch:
# tracer: function_graph
#
# CPU  DURATION  FUNCTION CALLS
# | |   | |   |   |   |
 0)   |  /* Tracing enabled */
 0)   |  /* Writing to files */
 0)   |  /* Unlinking first file */
 1) * 24728.81 us |  ecryptfs_do_unlink();
 1) + 20.923 us   |  ecryptfs_evict_inode();
 0)   |  /* rm'ing second file */
 --
 1)  unlink-1333   =>rm-1334
 --
 
 1) * 41093.71 us |  ecryptfs_do_unlink();
 1) + 11.384 us   |  ecryptfs_evict_inode();
 0)   |  /* Unmounting eCryptFS */
 --
 0)  trace-a-1314  =>  umount-1336
 --
 
 0)   2.986 us|  ecryptfs_evict_inode();

On Fri, May 08, 2020 at 09:31:33AM -0500, Jeffrey Mitchell wrote:
> On asynchronous base filesystems like NFS, eCryptFS leaves inodes for
> deleted files in the cache until unmounting. Change call in
> ecryptfs_do_unlink() from set_nlink() to drop_nlink() in order to reliably
> evict inodes from the cache even on top of NFS.
> 
> Signed-off-by: Dan Robertson 
> Signed-off-by: Jeffrey Mitchell 
> ---
>  fs/ecryptfs/inode.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
> index e23752d..f7594b6 100644
> --- a/fs/ecryptfs/inode.c
> +++ b/fs/ecryptfs/inode.c
> @@ -147,7 +147,7 @@ static int ecryptfs_do_unlink(struct inode *dir, struct 
> dentry *dentry,
>   goto out_unlock;
>   }
>   fsstack_copy_attr_times(dir, lower_dir_inode);
> - set_nlink(inode, ecryptfs_inode_to_lower(inode)->i_nlink);
> + drop_nlink(inode);
>   inode->i_ctime = dir->i_ctime;
>  out_unlock:
>   dput(lower_dentry);
> -- 
> 2.7.4
>