* [PATCH 0/3] virtiofsd: Deal with empty filenames @ 2021-03-12 14:10 ` Greg Kurz 0 siblings, 0 replies; 19+ messages in thread From: Greg Kurz @ 2021-03-12 14:10 UTC (permalink / raw) To: qemu-devel Cc: virtio-fs, Miklos Szeredi, Dr. David Alan Gilbert, Stefan Hajnoczi, Greg Kurz There's no such thing as an empty file name in POSIX-compliant file systems. The current code base doesn't ensure the client doesn't send requests with such empty names. I've audited the code and only found one place where the behavior is somewhat altered in lookup_name() : res = do_statx(lo, dir->fd, name, &attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, &mnt_id); lookup_name() is used by lo_rmdir(), lo_rename() and lo_unlink() which all share the same behavior of doing some action on a file or directory under a given parent directory. But if an empty name reaches the code above, do_statx() returns the attributes of the parent directory itself and lookup_name() might return the inode of the parent directory. This could potentially cause security concerns in the callers. Fortunately, it doesn't as of today. If the parent directory is the root inode, lookup_name() returns NULL because lo_find() fails to find an inode with a matching .st_dev. Otherwise, lookup_name() does return the parent inode but the empty name then gets passed to either unlinkat(), renameat() or renameat2(), all of which fail with ENOENT in this case. Drop AT_EMPTY_PATH from the above code anyway to make it clear empty names aren't expected by the existing callers. If the need for it arises in the future, it can be added back but stay safe for now. The FUSE protocol doesn't have a notion of AT_EMPTY_PATH actually. The server should hence never see empty names in client requests. Detect this early and systematically fail the request with ENOENT in this case. No regression is observed with the POSIX-oriented pjdfstest file system test suite (https://github.com/pjd/pjdfstest). Greg Kurz (3): virtiofsd: Don't allow empty paths in lookup_name() virtiofsd: Convert some functions to return bool virtiofsd: Don't allow empty filenames tools/virtiofsd/passthrough_ll.c | 44 ++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 5 deletions(-) -- 2.26.2 ^ permalink raw reply [flat|nested] 19+ messages in thread
* [Virtio-fs] [PATCH 0/3] virtiofsd: Deal with empty filenames @ 2021-03-12 14:10 ` Greg Kurz 0 siblings, 0 replies; 19+ messages in thread From: Greg Kurz @ 2021-03-12 14:10 UTC (permalink / raw) To: qemu-devel; +Cc: virtio-fs There's no such thing as an empty file name in POSIX-compliant file systems. The current code base doesn't ensure the client doesn't send requests with such empty names. I've audited the code and only found one place where the behavior is somewhat altered in lookup_name() : res = do_statx(lo, dir->fd, name, &attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, &mnt_id); lookup_name() is used by lo_rmdir(), lo_rename() and lo_unlink() which all share the same behavior of doing some action on a file or directory under a given parent directory. But if an empty name reaches the code above, do_statx() returns the attributes of the parent directory itself and lookup_name() might return the inode of the parent directory. This could potentially cause security concerns in the callers. Fortunately, it doesn't as of today. If the parent directory is the root inode, lookup_name() returns NULL because lo_find() fails to find an inode with a matching .st_dev. Otherwise, lookup_name() does return the parent inode but the empty name then gets passed to either unlinkat(), renameat() or renameat2(), all of which fail with ENOENT in this case. Drop AT_EMPTY_PATH from the above code anyway to make it clear empty names aren't expected by the existing callers. If the need for it arises in the future, it can be added back but stay safe for now. The FUSE protocol doesn't have a notion of AT_EMPTY_PATH actually. The server should hence never see empty names in client requests. Detect this early and systematically fail the request with ENOENT in this case. No regression is observed with the POSIX-oriented pjdfstest file system test suite (https://github.com/pjd/pjdfstest). Greg Kurz (3): virtiofsd: Don't allow empty paths in lookup_name() virtiofsd: Convert some functions to return bool virtiofsd: Don't allow empty filenames tools/virtiofsd/passthrough_ll.c | 44 ++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 5 deletions(-) -- 2.26.2 ^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH 1/3] virtiofsd: Don't allow empty paths in lookup_name() 2021-03-12 14:10 ` [Virtio-fs] " Greg Kurz @ 2021-03-12 14:10 ` Greg Kurz -1 siblings, 0 replies; 19+ messages in thread From: Greg Kurz @ 2021-03-12 14:10 UTC (permalink / raw) To: qemu-devel Cc: virtio-fs, Miklos Szeredi, Dr. David Alan Gilbert, Stefan Hajnoczi, Greg Kurz When passed an empty filename, lookup_name() returns the inode of the parent directory, unless the parent is the root in which case the st_dev doesn't match and lo_find() returns NULL. This is because lookup_name() passes AT_EMPTY_PATH down to fstatat() or statx(). This behavior doesn't quite make sense because users of lookup_name() then pass the name to unlinkat(), renameat() or renameat2(), all of which will always fail on empty names. Drop AT_EMPTY_PATH from the flags in lookup_name() so that it has the consistent behavior of "returning an existing child inode or NULL" for all directories. Signed-off-by: Greg Kurz <groug@kaod.org> --- tools/virtiofsd/passthrough_ll.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c index fc7e1b1e8e2b..27a6c636dcaf 100644 --- a/tools/virtiofsd/passthrough_ll.c +++ b/tools/virtiofsd/passthrough_ll.c @@ -1308,8 +1308,7 @@ static struct lo_inode *lookup_name(fuse_req_t req, fuse_ino_t parent, return NULL; } - res = do_statx(lo, dir->fd, name, &attr, - AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, &mnt_id); + res = do_statx(lo, dir->fd, name, &attr, AT_SYMLINK_NOFOLLOW, &mnt_id); lo_inode_put(lo, &dir); if (res == -1) { return NULL; -- 2.26.2 ^ permalink raw reply related [flat|nested] 19+ messages in thread
* [Virtio-fs] [PATCH 1/3] virtiofsd: Don't allow empty paths in lookup_name() @ 2021-03-12 14:10 ` Greg Kurz 0 siblings, 0 replies; 19+ messages in thread From: Greg Kurz @ 2021-03-12 14:10 UTC (permalink / raw) To: qemu-devel; +Cc: virtio-fs When passed an empty filename, lookup_name() returns the inode of the parent directory, unless the parent is the root in which case the st_dev doesn't match and lo_find() returns NULL. This is because lookup_name() passes AT_EMPTY_PATH down to fstatat() or statx(). This behavior doesn't quite make sense because users of lookup_name() then pass the name to unlinkat(), renameat() or renameat2(), all of which will always fail on empty names. Drop AT_EMPTY_PATH from the flags in lookup_name() so that it has the consistent behavior of "returning an existing child inode or NULL" for all directories. Signed-off-by: Greg Kurz <groug@kaod.org> --- tools/virtiofsd/passthrough_ll.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c index fc7e1b1e8e2b..27a6c636dcaf 100644 --- a/tools/virtiofsd/passthrough_ll.c +++ b/tools/virtiofsd/passthrough_ll.c @@ -1308,8 +1308,7 @@ static struct lo_inode *lookup_name(fuse_req_t req, fuse_ino_t parent, return NULL; } - res = do_statx(lo, dir->fd, name, &attr, - AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, &mnt_id); + res = do_statx(lo, dir->fd, name, &attr, AT_SYMLINK_NOFOLLOW, &mnt_id); lo_inode_put(lo, &dir); if (res == -1) { return NULL; -- 2.26.2 ^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [Virtio-fs] [PATCH 1/3] virtiofsd: Don't allow empty paths in lookup_name() 2021-03-12 14:10 ` [Virtio-fs] " Greg Kurz (?) @ 2021-03-12 15:13 ` Connor Kuehl 2021-03-12 15:49 ` Greg Kurz -1 siblings, 1 reply; 19+ messages in thread From: Connor Kuehl @ 2021-03-12 15:13 UTC (permalink / raw) To: Greg Kurz, qemu-devel; +Cc: virtio-fs On 3/12/21 8:10 AM, Greg Kurz wrote: > When passed an empty filename, lookup_name() returns the inode of > the parent directory, unless the parent is the root in which case > the st_dev doesn't match and lo_find() returns NULL. This is > because lookup_name() passes AT_EMPTY_PATH down to fstatat() or > statx(). > > This behavior doesn't quite make sense because users of lookup_name() > then pass the name to unlinkat(), renameat() or renameat2(), all of > which will always fail on empty names. > > Drop AT_EMPTY_PATH from the flags in lookup_name() so that it has > the consistent behavior of "returning an existing child inode or > NULL" for all directories. > > Signed-off-by: Greg Kurz <groug@kaod.org> > --- > tools/virtiofsd/passthrough_ll.c | 3 +-- > 1 file changed, 1 insertion(+), 2 deletions(-) > > diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c > index fc7e1b1e8e2b..27a6c636dcaf 100644 > --- a/tools/virtiofsd/passthrough_ll.c > +++ b/tools/virtiofsd/passthrough_ll.c > @@ -1308,8 +1308,7 @@ static struct lo_inode *lookup_name(fuse_req_t req, fuse_ino_t parent, > return NULL; > } > > - res = do_statx(lo, dir->fd, name, &attr, > - AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, &mnt_id); > + res = do_statx(lo, dir->fd, name, &attr, AT_SYMLINK_NOFOLLOW, &mnt_id); > lo_inode_put(lo, &dir); > if (res == -1) { > return NULL; > Should the statx() in lo_do_lookup() have this flag removed as well? I don't think its callers will pass in an empty name because: - One of your later patches prevents lo_mknod_symlink() from doing so - lo_create() will fail an earlier call against the host file system (open) - lo_do_readdir() shouldn't be reading empty names because that implies someone was able to pull off making an entry with an empty name However, I don't know if there will one day be future callers to lo_do_lookup() that will depend on that flag. If the answer to the above is no, then: Reviewed-by: Connor Kuehl <ckuehl@redhat.com> ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [Virtio-fs] [PATCH 1/3] virtiofsd: Don't allow empty paths in lookup_name() 2021-03-12 15:13 ` Connor Kuehl @ 2021-03-12 15:49 ` Greg Kurz 0 siblings, 0 replies; 19+ messages in thread From: Greg Kurz @ 2021-03-12 15:49 UTC (permalink / raw) To: Connor Kuehl; +Cc: virtio-fs, qemu-devel On Fri, 12 Mar 2021 09:13:36 -0600 Connor Kuehl <ckuehl@redhat.com> wrote: > On 3/12/21 8:10 AM, Greg Kurz wrote: > > When passed an empty filename, lookup_name() returns the inode of > > the parent directory, unless the parent is the root in which case > > the st_dev doesn't match and lo_find() returns NULL. This is > > because lookup_name() passes AT_EMPTY_PATH down to fstatat() or > > statx(). > > > > This behavior doesn't quite make sense because users of lookup_name() > > then pass the name to unlinkat(), renameat() or renameat2(), all of > > which will always fail on empty names. > > > > Drop AT_EMPTY_PATH from the flags in lookup_name() so that it has > > the consistent behavior of "returning an existing child inode or > > NULL" for all directories. > > > > Signed-off-by: Greg Kurz <groug@kaod.org> > > --- > > tools/virtiofsd/passthrough_ll.c | 3 +-- > > 1 file changed, 1 insertion(+), 2 deletions(-) > > > > diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c > > index fc7e1b1e8e2b..27a6c636dcaf 100644 > > --- a/tools/virtiofsd/passthrough_ll.c > > +++ b/tools/virtiofsd/passthrough_ll.c > > @@ -1308,8 +1308,7 @@ static struct lo_inode *lookup_name(fuse_req_t req, fuse_ino_t parent, > > return NULL; > > } > > > > - res = do_statx(lo, dir->fd, name, &attr, > > - AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, &mnt_id); > > + res = do_statx(lo, dir->fd, name, &attr, AT_SYMLINK_NOFOLLOW, &mnt_id); > > lo_inode_put(lo, &dir); > > if (res == -1) { > > return NULL; > > > > Should the statx() in lo_do_lookup() have this flag removed as well? I This is different. newfd = openat(dir->fd, name, O_PATH | O_NOFOLLOW); if (newfd == -1) { goto out_err; } res = do_statx(lo, newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, &mnt_id); if (res == -1) { goto out_err; } No client originated name is passed to do_statx() in this case. On the contrary, AT_EMPTY_PATH is provided on purpose since the third argument is "". The goal here is to get the attributes of the entry newfd points to. Same stands for other occurences of AT_EMPTY_PATH with fstatat() in lo_getattr() and lo_link(), fchownat() in lo_setattr() or do_statx() again in setup_root(). > don't think its callers will pass in an empty name because: > > - One of your later patches prevents lo_mknod_symlink() from doing so > - lo_create() will fail an earlier call against the host file system > (open) > - lo_do_readdir() shouldn't be reading empty names because that > implies someone was able to pull off making an entry with an empty name > > However, I don't know if there will one day be future callers to > lo_do_lookup() that will depend on that flag. > > If the answer to the above is no, then: > It is :) > Reviewed-by: Connor Kuehl <ckuehl@redhat.com> > Thanks ! ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [Virtio-fs] [PATCH 1/3] virtiofsd: Don't allow empty paths in lookup_name() 2021-03-12 14:10 ` [Virtio-fs] " Greg Kurz (?) (?) @ 2021-03-14 23:38 ` Vivek Goyal -1 siblings, 0 replies; 19+ messages in thread From: Vivek Goyal @ 2021-03-14 23:38 UTC (permalink / raw) To: Greg Kurz; +Cc: virtio-fs, qemu-devel On Fri, Mar 12, 2021 at 03:10:01PM +0100, Greg Kurz wrote: > When passed an empty filename, lookup_name() returns the inode of > the parent directory, unless the parent is the root in which case > the st_dev doesn't match and lo_find() returns NULL. This is > because lookup_name() passes AT_EMPTY_PATH down to fstatat() or > statx(). > > This behavior doesn't quite make sense because users of lookup_name() > then pass the name to unlinkat(), renameat() or renameat2(), all of > which will always fail on empty names. > > Drop AT_EMPTY_PATH from the flags in lookup_name() so that it has > the consistent behavior of "returning an existing child inode or > NULL" for all directories. > > Signed-off-by: Greg Kurz <groug@kaod.org> Looks good to me. Can't think in what cases fuse will need to pass in empty path for lookup. Reviewed-by: Vivek Goyal <vgoyal@redhat.com> Vivek > --- > tools/virtiofsd/passthrough_ll.c | 3 +-- > 1 file changed, 1 insertion(+), 2 deletions(-) > > diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c > index fc7e1b1e8e2b..27a6c636dcaf 100644 > --- a/tools/virtiofsd/passthrough_ll.c > +++ b/tools/virtiofsd/passthrough_ll.c > @@ -1308,8 +1308,7 @@ static struct lo_inode *lookup_name(fuse_req_t req, fuse_ino_t parent, > return NULL; > } > > - res = do_statx(lo, dir->fd, name, &attr, > - AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, &mnt_id); > + res = do_statx(lo, dir->fd, name, &attr, AT_SYMLINK_NOFOLLOW, &mnt_id); > lo_inode_put(lo, &dir); > if (res == -1) { > return NULL; > -- > 2.26.2 > > _______________________________________________ > Virtio-fs mailing list > Virtio-fs@redhat.com > https://listman.redhat.com/mailman/listinfo/virtio-fs ^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH 2/3] virtiofsd: Convert some functions to return bool 2021-03-12 14:10 ` [Virtio-fs] " Greg Kurz @ 2021-03-12 14:10 ` Greg Kurz -1 siblings, 0 replies; 19+ messages in thread From: Greg Kurz @ 2021-03-12 14:10 UTC (permalink / raw) To: qemu-devel Cc: virtio-fs, Miklos Szeredi, Dr. David Alan Gilbert, Stefan Hajnoczi, Greg Kurz Both currently only return 0 or 1. Signed-off-by: Greg Kurz <groug@kaod.org> --- tools/virtiofsd/passthrough_ll.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c index 27a6c636dcaf..f63016d35626 100644 --- a/tools/virtiofsd/passthrough_ll.c +++ b/tools/virtiofsd/passthrough_ll.c @@ -221,17 +221,17 @@ static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st, static int xattr_map_client(const struct lo_data *lo, const char *client_name, char **out_name); -static int is_dot_or_dotdot(const char *name) +static bool is_dot_or_dotdot(const char *name) { return name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')); } /* Is `path` a single path component that is not "." or ".."? */ -static int is_safe_path_component(const char *path) +static bool is_safe_path_component(const char *path) { if (strchr(path, '/')) { - return 0; + return false; } return !is_dot_or_dotdot(path); -- 2.26.2 ^ permalink raw reply related [flat|nested] 19+ messages in thread
* [Virtio-fs] [PATCH 2/3] virtiofsd: Convert some functions to return bool @ 2021-03-12 14:10 ` Greg Kurz 0 siblings, 0 replies; 19+ messages in thread From: Greg Kurz @ 2021-03-12 14:10 UTC (permalink / raw) To: qemu-devel; +Cc: virtio-fs Both currently only return 0 or 1. Signed-off-by: Greg Kurz <groug@kaod.org> --- tools/virtiofsd/passthrough_ll.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c index 27a6c636dcaf..f63016d35626 100644 --- a/tools/virtiofsd/passthrough_ll.c +++ b/tools/virtiofsd/passthrough_ll.c @@ -221,17 +221,17 @@ static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st, static int xattr_map_client(const struct lo_data *lo, const char *client_name, char **out_name); -static int is_dot_or_dotdot(const char *name) +static bool is_dot_or_dotdot(const char *name) { return name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')); } /* Is `path` a single path component that is not "." or ".."? */ -static int is_safe_path_component(const char *path) +static bool is_safe_path_component(const char *path) { if (strchr(path, '/')) { - return 0; + return false; } return !is_dot_or_dotdot(path); -- 2.26.2 ^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [Virtio-fs] [PATCH 2/3] virtiofsd: Convert some functions to return bool 2021-03-12 14:10 ` [Virtio-fs] " Greg Kurz (?) @ 2021-03-12 15:13 ` Connor Kuehl -1 siblings, 0 replies; 19+ messages in thread From: Connor Kuehl @ 2021-03-12 15:13 UTC (permalink / raw) To: Greg Kurz, qemu-devel; +Cc: virtio-fs On 3/12/21 8:10 AM, Greg Kurz wrote: > Both currently only return 0 or 1. > > Signed-off-by: Greg Kurz <groug@kaod.org> > --- > tools/virtiofsd/passthrough_ll.c | 6 +++--- > 1 file changed, 3 insertions(+), 3 deletions(-) > > diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c > index 27a6c636dcaf..f63016d35626 100644 > --- a/tools/virtiofsd/passthrough_ll.c > +++ b/tools/virtiofsd/passthrough_ll.c > @@ -221,17 +221,17 @@ static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st, > static int xattr_map_client(const struct lo_data *lo, const char *client_name, > char **out_name); > > -static int is_dot_or_dotdot(const char *name) > +static bool is_dot_or_dotdot(const char *name) > { > return name[0] == '.' && > (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')); > } > > /* Is `path` a single path component that is not "." or ".."? */ > -static int is_safe_path_component(const char *path) > +static bool is_safe_path_component(const char *path) > { > if (strchr(path, '/')) { > - return 0; > + return false; > } > > return !is_dot_or_dotdot(path); > Reviewed-by: Connor Kuehl <ckuehl@redhat.com> ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [Virtio-fs] [PATCH 2/3] virtiofsd: Convert some functions to return bool 2021-03-12 14:10 ` [Virtio-fs] " Greg Kurz (?) (?) @ 2021-03-14 23:36 ` Vivek Goyal -1 siblings, 0 replies; 19+ messages in thread From: Vivek Goyal @ 2021-03-14 23:36 UTC (permalink / raw) To: Greg Kurz; +Cc: virtio-fs, qemu-devel On Fri, Mar 12, 2021 at 03:10:02PM +0100, Greg Kurz wrote: > Both currently only return 0 or 1. > > Signed-off-by: Greg Kurz <groug@kaod.org> Looks good to me. Reviewed-by: Vivek Goyal <vgoyal@redhat.com> Vivek > --- > tools/virtiofsd/passthrough_ll.c | 6 +++--- > 1 file changed, 3 insertions(+), 3 deletions(-) > > diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c > index 27a6c636dcaf..f63016d35626 100644 > --- a/tools/virtiofsd/passthrough_ll.c > +++ b/tools/virtiofsd/passthrough_ll.c > @@ -221,17 +221,17 @@ static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st, > static int xattr_map_client(const struct lo_data *lo, const char *client_name, > char **out_name); > > -static int is_dot_or_dotdot(const char *name) > +static bool is_dot_or_dotdot(const char *name) > { > return name[0] == '.' && > (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')); > } > > /* Is `path` a single path component that is not "." or ".."? */ > -static int is_safe_path_component(const char *path) > +static bool is_safe_path_component(const char *path) > { > if (strchr(path, '/')) { > - return 0; > + return false; > } > > return !is_dot_or_dotdot(path); > -- > 2.26.2 > > _______________________________________________ > Virtio-fs mailing list > Virtio-fs@redhat.com > https://listman.redhat.com/mailman/listinfo/virtio-fs ^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH 3/3] virtiofsd: Don't allow empty filenames 2021-03-12 14:10 ` [Virtio-fs] " Greg Kurz @ 2021-03-12 14:10 ` Greg Kurz -1 siblings, 0 replies; 19+ messages in thread From: Greg Kurz @ 2021-03-12 14:10 UTC (permalink / raw) To: qemu-devel Cc: virtio-fs, Miklos Szeredi, Dr. David Alan Gilbert, Stefan Hajnoczi, Greg Kurz POSIX.1-2017 clearly stipulates that empty filenames aren't allowed ([1] and [2]). Since virtiofsd is supposed to mirror the host file system hierarchy and the host can be assumed to be linux, we don't really expect clients to pass requests with an empty path in it. If they do so anyway, this would eventually cause an error when trying to create/lookup the actual inode on the underlying POSIX filesystem. But this could still confuse some code that wouldn't be ready to cope with this. Filter out empty names coming from the client at the top level, so that the rest doesn't have to care about it. This is done everywhere we already call is_safe_path_component(), but in a separate helper since the usual error for empty path names is ENOENT instead of EINVAL. [1] https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_170 [2] https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13 Signed-off-by: Greg Kurz <groug@kaod.org> --- tools/virtiofsd/passthrough_ll.c | 35 ++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c index f63016d35626..bff9dc2cd26d 100644 --- a/tools/virtiofsd/passthrough_ll.c +++ b/tools/virtiofsd/passthrough_ll.c @@ -237,6 +237,11 @@ static bool is_safe_path_component(const char *path) return !is_dot_or_dotdot(path); } +static bool is_empty(const char *name) +{ + return name[0] == '\0'; +} + static struct lo_data *lo_data(fuse_req_t req) { return (struct lo_data *)fuse_req_userdata(req); @@ -1083,6 +1088,11 @@ static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n", parent, name); + if (is_empty(name)) { + fuse_reply_err(req, ENOENT); + return; + } + /* * Don't use is_safe_path_component(), allow "." and ".." for NFS export * support. @@ -1174,6 +1184,11 @@ static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, struct fuse_entry_param e; struct lo_cred old = {}; + if (is_empty(name)) { + fuse_reply_err(req, ENOENT); + return; + } + if (!is_safe_path_component(name)) { fuse_reply_err(req, EINVAL); return; @@ -1246,6 +1261,11 @@ static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, char procname[64]; int saverr; + if (is_empty(name)) { + fuse_reply_err(req, ENOENT); + return; + } + if (!is_safe_path_component(name)) { fuse_reply_err(req, EINVAL); return; @@ -1323,6 +1343,11 @@ static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) struct lo_inode *inode; struct lo_data *lo = lo_data(req); + if (is_empty(name)) { + fuse_reply_err(req, ENOENT); + return; + } + if (!is_safe_path_component(name)) { fuse_reply_err(req, EINVAL); return; @@ -1352,6 +1377,11 @@ static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, struct lo_inode *newinode = NULL; struct lo_data *lo = lo_data(req); + if (is_empty(name) || is_empty(newname)) { + fuse_reply_err(req, ENOENT); + return; + } + if (!is_safe_path_component(name) || !is_safe_path_component(newname)) { fuse_reply_err(req, EINVAL); return; @@ -1405,6 +1435,11 @@ static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) struct lo_inode *inode; struct lo_data *lo = lo_data(req); + if (is_empty(name)) { + fuse_reply_err(req, ENOENT); + return; + } + if (!is_safe_path_component(name)) { fuse_reply_err(req, EINVAL); return; -- 2.26.2 ^ permalink raw reply related [flat|nested] 19+ messages in thread
* [Virtio-fs] [PATCH 3/3] virtiofsd: Don't allow empty filenames @ 2021-03-12 14:10 ` Greg Kurz 0 siblings, 0 replies; 19+ messages in thread From: Greg Kurz @ 2021-03-12 14:10 UTC (permalink / raw) To: qemu-devel; +Cc: virtio-fs POSIX.1-2017 clearly stipulates that empty filenames aren't allowed ([1] and [2]). Since virtiofsd is supposed to mirror the host file system hierarchy and the host can be assumed to be linux, we don't really expect clients to pass requests with an empty path in it. If they do so anyway, this would eventually cause an error when trying to create/lookup the actual inode on the underlying POSIX filesystem. But this could still confuse some code that wouldn't be ready to cope with this. Filter out empty names coming from the client at the top level, so that the rest doesn't have to care about it. This is done everywhere we already call is_safe_path_component(), but in a separate helper since the usual error for empty path names is ENOENT instead of EINVAL. [1] https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_170 [2] https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13 Signed-off-by: Greg Kurz <groug@kaod.org> --- tools/virtiofsd/passthrough_ll.c | 35 ++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c index f63016d35626..bff9dc2cd26d 100644 --- a/tools/virtiofsd/passthrough_ll.c +++ b/tools/virtiofsd/passthrough_ll.c @@ -237,6 +237,11 @@ static bool is_safe_path_component(const char *path) return !is_dot_or_dotdot(path); } +static bool is_empty(const char *name) +{ + return name[0] == '\0'; +} + static struct lo_data *lo_data(fuse_req_t req) { return (struct lo_data *)fuse_req_userdata(req); @@ -1083,6 +1088,11 @@ static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n", parent, name); + if (is_empty(name)) { + fuse_reply_err(req, ENOENT); + return; + } + /* * Don't use is_safe_path_component(), allow "." and ".." for NFS export * support. @@ -1174,6 +1184,11 @@ static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, struct fuse_entry_param e; struct lo_cred old = {}; + if (is_empty(name)) { + fuse_reply_err(req, ENOENT); + return; + } + if (!is_safe_path_component(name)) { fuse_reply_err(req, EINVAL); return; @@ -1246,6 +1261,11 @@ static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, char procname[64]; int saverr; + if (is_empty(name)) { + fuse_reply_err(req, ENOENT); + return; + } + if (!is_safe_path_component(name)) { fuse_reply_err(req, EINVAL); return; @@ -1323,6 +1343,11 @@ static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) struct lo_inode *inode; struct lo_data *lo = lo_data(req); + if (is_empty(name)) { + fuse_reply_err(req, ENOENT); + return; + } + if (!is_safe_path_component(name)) { fuse_reply_err(req, EINVAL); return; @@ -1352,6 +1377,11 @@ static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, struct lo_inode *newinode = NULL; struct lo_data *lo = lo_data(req); + if (is_empty(name) || is_empty(newname)) { + fuse_reply_err(req, ENOENT); + return; + } + if (!is_safe_path_component(name) || !is_safe_path_component(newname)) { fuse_reply_err(req, EINVAL); return; @@ -1405,6 +1435,11 @@ static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) struct lo_inode *inode; struct lo_data *lo = lo_data(req); + if (is_empty(name)) { + fuse_reply_err(req, ENOENT); + return; + } + if (!is_safe_path_component(name)) { fuse_reply_err(req, EINVAL); return; -- 2.26.2 ^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [Virtio-fs] [PATCH 3/3] virtiofsd: Don't allow empty filenames 2021-03-12 14:10 ` [Virtio-fs] " Greg Kurz (?) @ 2021-03-12 15:13 ` Connor Kuehl -1 siblings, 0 replies; 19+ messages in thread From: Connor Kuehl @ 2021-03-12 15:13 UTC (permalink / raw) To: Greg Kurz, qemu-devel; +Cc: virtio-fs On 3/12/21 8:10 AM, Greg Kurz wrote: > POSIX.1-2017 clearly stipulates that empty filenames aren't > allowed ([1] and [2]). Since virtiofsd is supposed to mirror > the host file system hierarchy and the host can be assumed to > be linux, we don't really expect clients to pass requests with > an empty path in it. If they do so anyway, this would eventually > cause an error when trying to create/lookup the actual inode > on the underlying POSIX filesystem. But this could still confuse > some code that wouldn't be ready to cope with this. > > Filter out empty names coming from the client at the top level, > so that the rest doesn't have to care about it. This is done > everywhere we already call is_safe_path_component(), but > in a separate helper since the usual error for empty path > names is ENOENT instead of EINVAL. > > [1] https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_170 > [2] https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13 > > Signed-off-by: Greg Kurz <groug@kaod.org> Reviewed-by: Connor Kuehl <ckuehl@redhat.com> ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [Virtio-fs] [PATCH 3/3] virtiofsd: Don't allow empty filenames 2021-03-12 14:10 ` [Virtio-fs] " Greg Kurz (?) (?) @ 2021-03-14 23:36 ` Vivek Goyal 2021-03-15 10:06 ` Greg Kurz -1 siblings, 1 reply; 19+ messages in thread From: Vivek Goyal @ 2021-03-14 23:36 UTC (permalink / raw) To: Greg Kurz; +Cc: virtio-fs, qemu-devel On Fri, Mar 12, 2021 at 03:10:03PM +0100, Greg Kurz wrote: > POSIX.1-2017 clearly stipulates that empty filenames aren't > allowed ([1] and [2]). Since virtiofsd is supposed to mirror > the host file system hierarchy and the host can be assumed to > be linux, we don't really expect clients to pass requests with > an empty path in it. If they do so anyway, this would eventually > cause an error when trying to create/lookup the actual inode > on the underlying POSIX filesystem. But this could still confuse > some code that wouldn't be ready to cope with this. > > Filter out empty names coming from the client at the top level, > so that the rest doesn't have to care about it. This is done > everywhere we already call is_safe_path_component(), but > in a separate helper since the usual error for empty path > names is ENOENT instead of EINVAL. > > [1] https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_170 > [2] https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13 > > Signed-off-by: Greg Kurz <groug@kaod.org> Hi Greg, Minor nit, if you happen to respin this patch, it probably should come before the first patch in series. Once we make it clear that file server is not expecting empty path in these top level functions, then it is easy to clear AT_EMPTY_PATH in function these paths are calling as appropriate. What about lo_create(). Should we put a check in there as well. We are passed xattr names in lo_getxattr()/lo_removexattr()/lo_setxattr(). In general, should we put an empty in_name check there as well and probably simply return -EINVAL. Thanks Vivek > --- > tools/virtiofsd/passthrough_ll.c | 35 ++++++++++++++++++++++++++++++++ > 1 file changed, 35 insertions(+) > > diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c > index f63016d35626..bff9dc2cd26d 100644 > --- a/tools/virtiofsd/passthrough_ll.c > +++ b/tools/virtiofsd/passthrough_ll.c > @@ -237,6 +237,11 @@ static bool is_safe_path_component(const char *path) > return !is_dot_or_dotdot(path); > } > > +static bool is_empty(const char *name) > +{ > + return name[0] == '\0'; > +} > + > static struct lo_data *lo_data(fuse_req_t req) > { > return (struct lo_data *)fuse_req_userdata(req); > @@ -1083,6 +1088,11 @@ static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) > fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n", parent, > name); > > + if (is_empty(name)) { > + fuse_reply_err(req, ENOENT); > + return; > + } > + > /* > * Don't use is_safe_path_component(), allow "." and ".." for NFS export > * support. > @@ -1174,6 +1184,11 @@ static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, > struct fuse_entry_param e; > struct lo_cred old = {}; > > + if (is_empty(name)) { > + fuse_reply_err(req, ENOENT); > + return; > + } > + > if (!is_safe_path_component(name)) { > fuse_reply_err(req, EINVAL); > return; > @@ -1246,6 +1261,11 @@ static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, > char procname[64]; > int saverr; > > + if (is_empty(name)) { > + fuse_reply_err(req, ENOENT); > + return; > + } > + > if (!is_safe_path_component(name)) { > fuse_reply_err(req, EINVAL); > return; > @@ -1323,6 +1343,11 @@ static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) > struct lo_inode *inode; > struct lo_data *lo = lo_data(req); > > + if (is_empty(name)) { > + fuse_reply_err(req, ENOENT); > + return; > + } > + > if (!is_safe_path_component(name)) { > fuse_reply_err(req, EINVAL); > return; > @@ -1352,6 +1377,11 @@ static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, > struct lo_inode *newinode = NULL; > struct lo_data *lo = lo_data(req); > > + if (is_empty(name) || is_empty(newname)) { > + fuse_reply_err(req, ENOENT); > + return; > + } > + > if (!is_safe_path_component(name) || !is_safe_path_component(newname)) { > fuse_reply_err(req, EINVAL); > return; > @@ -1405,6 +1435,11 @@ static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) > struct lo_inode *inode; > struct lo_data *lo = lo_data(req); > > + if (is_empty(name)) { > + fuse_reply_err(req, ENOENT); > + return; > + } > + > if (!is_safe_path_component(name)) { > fuse_reply_err(req, EINVAL); > return; > -- > 2.26.2 > > _______________________________________________ > Virtio-fs mailing list > Virtio-fs@redhat.com > https://listman.redhat.com/mailman/listinfo/virtio-fs ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [Virtio-fs] [PATCH 3/3] virtiofsd: Don't allow empty filenames 2021-03-14 23:36 ` Vivek Goyal @ 2021-03-15 10:06 ` Greg Kurz 2021-03-15 15:18 ` Dr. David Alan Gilbert 2021-03-15 17:55 ` Vivek Goyal 0 siblings, 2 replies; 19+ messages in thread From: Greg Kurz @ 2021-03-15 10:06 UTC (permalink / raw) To: Vivek Goyal; +Cc: virtio-fs, qemu-devel On Sun, 14 Mar 2021 19:36:04 -0400 Vivek Goyal <vgoyal@redhat.com> wrote: > On Fri, Mar 12, 2021 at 03:10:03PM +0100, Greg Kurz wrote: > > POSIX.1-2017 clearly stipulates that empty filenames aren't > > allowed ([1] and [2]). Since virtiofsd is supposed to mirror > > the host file system hierarchy and the host can be assumed to > > be linux, we don't really expect clients to pass requests with > > an empty path in it. If they do so anyway, this would eventually > > cause an error when trying to create/lookup the actual inode > > on the underlying POSIX filesystem. But this could still confuse > > some code that wouldn't be ready to cope with this. > > > > Filter out empty names coming from the client at the top level, > > so that the rest doesn't have to care about it. This is done > > everywhere we already call is_safe_path_component(), but > > in a separate helper since the usual error for empty path > > names is ENOENT instead of EINVAL. > > > > [1] https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_170 > > [2] https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13 > > > > Signed-off-by: Greg Kurz <groug@kaod.org> > > Hi Greg, > > Minor nit, if you happen to respin this patch, it probably should come > before the first patch in series. Once we make it clear that file server > is not expecting empty path in these top level functions, then it is > easy to clear AT_EMPTY_PATH in function these paths are calling as > appropriate. > The patch order is chronological : I just spotted the AT_EMPTY_PATH oddity before coming up with the bigger hammer of patch 3. But you're right, it probably makes more sense to do the other way around. > What about lo_create(). Should we put a check in there as well. > Good catch ! I'll post a v2 then ;) > We are passed xattr names in lo_getxattr()/lo_removexattr()/lo_setxattr(). > In general, should we put an empty in_name check there as well and > probably simply return -EINVAL. > An empty xattr name doesn't likely make sense either, even if this isn't written down anywhere, not in an explicit manner at least. The kernel checks this in setxattr() and errors out with -ERANGE in this case. error = strncpy_from_user(kname, name, sizeof(kname)); if (error == 0 || error == sizeof(kname)) error = -ERANGE; if (error < 0) return error; Same goes for the other *xattr() syscalls, i.e. nothing nasty can ever happen with an empty xattr name since this is always considered as an error by the kernel. Not sure this would bring much to also check this in QEMU. This is a bit different from the empty path name case because an empty path name is valid for syscalls that support AT_EMPTY_PATH, and we just want to make sure these are never exercised with names from the client. Cheers, -- Greg > Thanks > Vivek > > > --- > > tools/virtiofsd/passthrough_ll.c | 35 ++++++++++++++++++++++++++++++++ > > 1 file changed, 35 insertions(+) > > > > diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c > > index f63016d35626..bff9dc2cd26d 100644 > > --- a/tools/virtiofsd/passthrough_ll.c > > +++ b/tools/virtiofsd/passthrough_ll.c > > @@ -237,6 +237,11 @@ static bool is_safe_path_component(const char *path) > > return !is_dot_or_dotdot(path); > > } > > > > +static bool is_empty(const char *name) > > +{ > > + return name[0] == '\0'; > > +} > > + > > static struct lo_data *lo_data(fuse_req_t req) > > { > > return (struct lo_data *)fuse_req_userdata(req); > > @@ -1083,6 +1088,11 @@ static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) > > fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n", parent, > > name); > > > > + if (is_empty(name)) { > > + fuse_reply_err(req, ENOENT); > > + return; > > + } > > + > > /* > > * Don't use is_safe_path_component(), allow "." and ".." for NFS export > > * support. > > @@ -1174,6 +1184,11 @@ static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, > > struct fuse_entry_param e; > > struct lo_cred old = {}; > > > > + if (is_empty(name)) { > > + fuse_reply_err(req, ENOENT); > > + return; > > + } > > + > > if (!is_safe_path_component(name)) { > > fuse_reply_err(req, EINVAL); > > return; > > @@ -1246,6 +1261,11 @@ static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, > > char procname[64]; > > int saverr; > > > > + if (is_empty(name)) { > > + fuse_reply_err(req, ENOENT); > > + return; > > + } > > + > > if (!is_safe_path_component(name)) { > > fuse_reply_err(req, EINVAL); > > return; > > @@ -1323,6 +1343,11 @@ static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) > > struct lo_inode *inode; > > struct lo_data *lo = lo_data(req); > > > > + if (is_empty(name)) { > > + fuse_reply_err(req, ENOENT); > > + return; > > + } > > + > > if (!is_safe_path_component(name)) { > > fuse_reply_err(req, EINVAL); > > return; > > @@ -1352,6 +1377,11 @@ static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, > > struct lo_inode *newinode = NULL; > > struct lo_data *lo = lo_data(req); > > > > + if (is_empty(name) || is_empty(newname)) { > > + fuse_reply_err(req, ENOENT); > > + return; > > + } > > + > > if (!is_safe_path_component(name) || !is_safe_path_component(newname)) { > > fuse_reply_err(req, EINVAL); > > return; > > @@ -1405,6 +1435,11 @@ static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) > > struct lo_inode *inode; > > struct lo_data *lo = lo_data(req); > > > > + if (is_empty(name)) { > > + fuse_reply_err(req, ENOENT); > > + return; > > + } > > + > > if (!is_safe_path_component(name)) { > > fuse_reply_err(req, EINVAL); > > return; > > -- > > 2.26.2 > > > > _______________________________________________ > > Virtio-fs mailing list > > Virtio-fs@redhat.com > > https://listman.redhat.com/mailman/listinfo/virtio-fs > ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [Virtio-fs] [PATCH 3/3] virtiofsd: Don't allow empty filenames 2021-03-15 10:06 ` Greg Kurz @ 2021-03-15 15:18 ` Dr. David Alan Gilbert 2021-03-15 17:37 ` Greg Kurz 2021-03-15 17:55 ` Vivek Goyal 1 sibling, 1 reply; 19+ messages in thread From: Dr. David Alan Gilbert @ 2021-03-15 15:18 UTC (permalink / raw) To: Greg Kurz; +Cc: virtio-fs, qemu-devel, Vivek Goyal * Greg Kurz (groug@kaod.org) wrote: > On Sun, 14 Mar 2021 19:36:04 -0400 > Vivek Goyal <vgoyal@redhat.com> wrote: > > > On Fri, Mar 12, 2021 at 03:10:03PM +0100, Greg Kurz wrote: > > > POSIX.1-2017 clearly stipulates that empty filenames aren't > > > allowed ([1] and [2]). Since virtiofsd is supposed to mirror > > > the host file system hierarchy and the host can be assumed to > > > be linux, we don't really expect clients to pass requests with > > > an empty path in it. If they do so anyway, this would eventually > > > cause an error when trying to create/lookup the actual inode > > > on the underlying POSIX filesystem. But this could still confuse > > > some code that wouldn't be ready to cope with this. > > > > > > Filter out empty names coming from the client at the top level, > > > so that the rest doesn't have to care about it. This is done > > > everywhere we already call is_safe_path_component(), but > > > in a separate helper since the usual error for empty path > > > names is ENOENT instead of EINVAL. > > > > > > [1] https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_170 > > > [2] https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13 > > > > > > Signed-off-by: Greg Kurz <groug@kaod.org> > > > > Hi Greg, > > > > Minor nit, if you happen to respin this patch, it probably should come > > before the first patch in series. Once we make it clear that file server > > is not expecting empty path in these top level functions, then it is > > easy to clear AT_EMPTY_PATH in function these paths are calling as > > appropriate. > > > > The patch order is chronological : I just spotted the AT_EMPTY_PATH > oddity before coming up with the bigger hammer of patch 3. But you're > right, it probably makes more sense to do the other way around. > > > What about lo_create(). Should we put a check in there as well. > > > > Good catch ! I'll post a v2 then ;) I'm just brewing a pull now, since soft freeze is tomorrow. I'll take 3,1,2 - please follow up with a separate lo_create one - we can add that later. Dave > > We are passed xattr names in lo_getxattr()/lo_removexattr()/lo_setxattr(). > > In general, should we put an empty in_name check there as well and > > probably simply return -EINVAL. > > > > An empty xattr name doesn't likely make sense either, even if this > isn't written down anywhere, not in an explicit manner at least. > > The kernel checks this in setxattr() and errors out with -ERANGE > in this case. > > error = strncpy_from_user(kname, name, sizeof(kname)); > if (error == 0 || error == sizeof(kname)) > error = -ERANGE; > if (error < 0) > return error; > > Same goes for the other *xattr() syscalls, i.e. nothing nasty can ever > happen with an empty xattr name since this is always considered as an > error by the kernel. Not sure this would bring much to also check this > in QEMU. This is a bit different from the empty path name case because > an empty path name is valid for syscalls that support AT_EMPTY_PATH, > and we just want to make sure these are never exercised with names > from the client. > > Cheers, > > -- > Greg > > > Thanks > > Vivek > > > > > --- > > > tools/virtiofsd/passthrough_ll.c | 35 ++++++++++++++++++++++++++++++++ > > > 1 file changed, 35 insertions(+) > > > > > > diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c > > > index f63016d35626..bff9dc2cd26d 100644 > > > --- a/tools/virtiofsd/passthrough_ll.c > > > +++ b/tools/virtiofsd/passthrough_ll.c > > > @@ -237,6 +237,11 @@ static bool is_safe_path_component(const char *path) > > > return !is_dot_or_dotdot(path); > > > } > > > > > > +static bool is_empty(const char *name) > > > +{ > > > + return name[0] == '\0'; > > > +} > > > + > > > static struct lo_data *lo_data(fuse_req_t req) > > > { > > > return (struct lo_data *)fuse_req_userdata(req); > > > @@ -1083,6 +1088,11 @@ static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) > > > fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n", parent, > > > name); > > > > > > + if (is_empty(name)) { > > > + fuse_reply_err(req, ENOENT); > > > + return; > > > + } > > > + > > > /* > > > * Don't use is_safe_path_component(), allow "." and ".." for NFS export > > > * support. > > > @@ -1174,6 +1184,11 @@ static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, > > > struct fuse_entry_param e; > > > struct lo_cred old = {}; > > > > > > + if (is_empty(name)) { > > > + fuse_reply_err(req, ENOENT); > > > + return; > > > + } > > > + > > > if (!is_safe_path_component(name)) { > > > fuse_reply_err(req, EINVAL); > > > return; > > > @@ -1246,6 +1261,11 @@ static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, > > > char procname[64]; > > > int saverr; > > > > > > + if (is_empty(name)) { > > > + fuse_reply_err(req, ENOENT); > > > + return; > > > + } > > > + > > > if (!is_safe_path_component(name)) { > > > fuse_reply_err(req, EINVAL); > > > return; > > > @@ -1323,6 +1343,11 @@ static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) > > > struct lo_inode *inode; > > > struct lo_data *lo = lo_data(req); > > > > > > + if (is_empty(name)) { > > > + fuse_reply_err(req, ENOENT); > > > + return; > > > + } > > > + > > > if (!is_safe_path_component(name)) { > > > fuse_reply_err(req, EINVAL); > > > return; > > > @@ -1352,6 +1377,11 @@ static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, > > > struct lo_inode *newinode = NULL; > > > struct lo_data *lo = lo_data(req); > > > > > > + if (is_empty(name) || is_empty(newname)) { > > > + fuse_reply_err(req, ENOENT); > > > + return; > > > + } > > > + > > > if (!is_safe_path_component(name) || !is_safe_path_component(newname)) { > > > fuse_reply_err(req, EINVAL); > > > return; > > > @@ -1405,6 +1435,11 @@ static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) > > > struct lo_inode *inode; > > > struct lo_data *lo = lo_data(req); > > > > > > + if (is_empty(name)) { > > > + fuse_reply_err(req, ENOENT); > > > + return; > > > + } > > > + > > > if (!is_safe_path_component(name)) { > > > fuse_reply_err(req, EINVAL); > > > return; > > > -- > > > 2.26.2 > > > > > > _______________________________________________ > > > Virtio-fs mailing list > > > Virtio-fs@redhat.com > > > https://listman.redhat.com/mailman/listinfo/virtio-fs > > > > _______________________________________________ > Virtio-fs mailing list > Virtio-fs@redhat.com > https://listman.redhat.com/mailman/listinfo/virtio-fs -- Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [Virtio-fs] [PATCH 3/3] virtiofsd: Don't allow empty filenames 2021-03-15 15:18 ` Dr. David Alan Gilbert @ 2021-03-15 17:37 ` Greg Kurz 0 siblings, 0 replies; 19+ messages in thread From: Greg Kurz @ 2021-03-15 17:37 UTC (permalink / raw) To: Dr. David Alan Gilbert; +Cc: virtio-fs, qemu-devel, Vivek Goyal On Mon, 15 Mar 2021 15:18:48 +0000 "Dr. David Alan Gilbert" <dgilbert@redhat.com> wrote: > * Greg Kurz (groug@kaod.org) wrote: > > On Sun, 14 Mar 2021 19:36:04 -0400 > > Vivek Goyal <vgoyal@redhat.com> wrote: > > > > > On Fri, Mar 12, 2021 at 03:10:03PM +0100, Greg Kurz wrote: > > > > POSIX.1-2017 clearly stipulates that empty filenames aren't > > > > allowed ([1] and [2]). Since virtiofsd is supposed to mirror > > > > the host file system hierarchy and the host can be assumed to > > > > be linux, we don't really expect clients to pass requests with > > > > an empty path in it. If they do so anyway, this would eventually > > > > cause an error when trying to create/lookup the actual inode > > > > on the underlying POSIX filesystem. But this could still confuse > > > > some code that wouldn't be ready to cope with this. > > > > > > > > Filter out empty names coming from the client at the top level, > > > > so that the rest doesn't have to care about it. This is done > > > > everywhere we already call is_safe_path_component(), but > > > > in a separate helper since the usual error for empty path > > > > names is ENOENT instead of EINVAL. > > > > > > > > [1] https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_170 > > > > [2] https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13 > > > > > > > > Signed-off-by: Greg Kurz <groug@kaod.org> > > > > > > Hi Greg, > > > > > > Minor nit, if you happen to respin this patch, it probably should come > > > before the first patch in series. Once we make it clear that file server > > > is not expecting empty path in these top level functions, then it is > > > easy to clear AT_EMPTY_PATH in function these paths are calling as > > > appropriate. > > > > > > > The patch order is chronological : I just spotted the AT_EMPTY_PATH > > oddity before coming up with the bigger hammer of patch 3. But you're > > right, it probably makes more sense to do the other way around. > > > > > What about lo_create(). Should we put a check in there as well. > > > > > > > Good catch ! I'll post a v2 then ;) > > I'm just brewing a pull now, since soft freeze is tomorrow. > I'll take 3,1,2 - please follow up with a separate lo_create one - we > can add that later. > Sure, I'll do that. Cheers, -- Greg ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [Virtio-fs] [PATCH 3/3] virtiofsd: Don't allow empty filenames 2021-03-15 10:06 ` Greg Kurz 2021-03-15 15:18 ` Dr. David Alan Gilbert @ 2021-03-15 17:55 ` Vivek Goyal 1 sibling, 0 replies; 19+ messages in thread From: Vivek Goyal @ 2021-03-15 17:55 UTC (permalink / raw) To: Greg Kurz; +Cc: virtio-fs, qemu-devel On Mon, Mar 15, 2021 at 11:06:30AM +0100, Greg Kurz wrote: > On Sun, 14 Mar 2021 19:36:04 -0400 > Vivek Goyal <vgoyal@redhat.com> wrote: > > > On Fri, Mar 12, 2021 at 03:10:03PM +0100, Greg Kurz wrote: > > > POSIX.1-2017 clearly stipulates that empty filenames aren't > > > allowed ([1] and [2]). Since virtiofsd is supposed to mirror > > > the host file system hierarchy and the host can be assumed to > > > be linux, we don't really expect clients to pass requests with > > > an empty path in it. If they do so anyway, this would eventually > > > cause an error when trying to create/lookup the actual inode > > > on the underlying POSIX filesystem. But this could still confuse > > > some code that wouldn't be ready to cope with this. > > > > > > Filter out empty names coming from the client at the top level, > > > so that the rest doesn't have to care about it. This is done > > > everywhere we already call is_safe_path_component(), but > > > in a separate helper since the usual error for empty path > > > names is ENOENT instead of EINVAL. > > > > > > [1] https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_170 > > > [2] https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13 > > > > > > Signed-off-by: Greg Kurz <groug@kaod.org> > > > > Hi Greg, > > > > Minor nit, if you happen to respin this patch, it probably should come > > before the first patch in series. Once we make it clear that file server > > is not expecting empty path in these top level functions, then it is > > easy to clear AT_EMPTY_PATH in function these paths are calling as > > appropriate. > > > > The patch order is chronological : I just spotted the AT_EMPTY_PATH > oddity before coming up with the bigger hammer of patch 3. But you're > right, it probably makes more sense to do the other way around. > > > What about lo_create(). Should we put a check in there as well. > > > > Good catch ! I'll post a v2 then ;) > > > We are passed xattr names in lo_getxattr()/lo_removexattr()/lo_setxattr(). > > In general, should we put an empty in_name check there as well and > > probably simply return -EINVAL. > > > > An empty xattr name doesn't likely make sense either, even if this > isn't written down anywhere, not in an explicit manner at least. > > The kernel checks this in setxattr() and errors out with -ERANGE > in this case. > > error = strncpy_from_user(kname, name, sizeof(kname)); > if (error == 0 || error == sizeof(kname)) > error = -ERANGE; > if (error < 0) > return error; > > Same goes for the other *xattr() syscalls, i.e. nothing nasty can ever > happen with an empty xattr name since this is always considered as an > error by the kernel. Not sure this would bring much to also check this > in QEMU. This is a bit different from the empty path name case because > an empty path name is valid for syscalls that support AT_EMPTY_PATH, > and we just want to make sure these are never exercised with names > from the client. Fair enough. Lets not worry about empty name for xattr calls. That's probably for some other day. Vivek > > Cheers, > > -- > Greg > > > Thanks > > Vivek > > > > > --- > > > tools/virtiofsd/passthrough_ll.c | 35 ++++++++++++++++++++++++++++++++ > > > 1 file changed, 35 insertions(+) > > > > > > diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c > > > index f63016d35626..bff9dc2cd26d 100644 > > > --- a/tools/virtiofsd/passthrough_ll.c > > > +++ b/tools/virtiofsd/passthrough_ll.c > > > @@ -237,6 +237,11 @@ static bool is_safe_path_component(const char *path) > > > return !is_dot_or_dotdot(path); > > > } > > > > > > +static bool is_empty(const char *name) > > > +{ > > > + return name[0] == '\0'; > > > +} > > > + > > > static struct lo_data *lo_data(fuse_req_t req) > > > { > > > return (struct lo_data *)fuse_req_userdata(req); > > > @@ -1083,6 +1088,11 @@ static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) > > > fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n", parent, > > > name); > > > > > > + if (is_empty(name)) { > > > + fuse_reply_err(req, ENOENT); > > > + return; > > > + } > > > + > > > /* > > > * Don't use is_safe_path_component(), allow "." and ".." for NFS export > > > * support. > > > @@ -1174,6 +1184,11 @@ static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, > > > struct fuse_entry_param e; > > > struct lo_cred old = {}; > > > > > > + if (is_empty(name)) { > > > + fuse_reply_err(req, ENOENT); > > > + return; > > > + } > > > + > > > if (!is_safe_path_component(name)) { > > > fuse_reply_err(req, EINVAL); > > > return; > > > @@ -1246,6 +1261,11 @@ static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, > > > char procname[64]; > > > int saverr; > > > > > > + if (is_empty(name)) { > > > + fuse_reply_err(req, ENOENT); > > > + return; > > > + } > > > + > > > if (!is_safe_path_component(name)) { > > > fuse_reply_err(req, EINVAL); > > > return; > > > @@ -1323,6 +1343,11 @@ static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) > > > struct lo_inode *inode; > > > struct lo_data *lo = lo_data(req); > > > > > > + if (is_empty(name)) { > > > + fuse_reply_err(req, ENOENT); > > > + return; > > > + } > > > + > > > if (!is_safe_path_component(name)) { > > > fuse_reply_err(req, EINVAL); > > > return; > > > @@ -1352,6 +1377,11 @@ static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, > > > struct lo_inode *newinode = NULL; > > > struct lo_data *lo = lo_data(req); > > > > > > + if (is_empty(name) || is_empty(newname)) { > > > + fuse_reply_err(req, ENOENT); > > > + return; > > > + } > > > + > > > if (!is_safe_path_component(name) || !is_safe_path_component(newname)) { > > > fuse_reply_err(req, EINVAL); > > > return; > > > @@ -1405,6 +1435,11 @@ static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) > > > struct lo_inode *inode; > > > struct lo_data *lo = lo_data(req); > > > > > > + if (is_empty(name)) { > > > + fuse_reply_err(req, ENOENT); > > > + return; > > > + } > > > + > > > if (!is_safe_path_component(name)) { > > > fuse_reply_err(req, EINVAL); > > > return; > > > -- > > > 2.26.2 > > > > > > _______________________________________________ > > > Virtio-fs mailing list > > > Virtio-fs@redhat.com > > > https://listman.redhat.com/mailman/listinfo/virtio-fs > > > ^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2021-03-15 17:55 UTC | newest] Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2021-03-12 14:10 [PATCH 0/3] virtiofsd: Deal with empty filenames Greg Kurz 2021-03-12 14:10 ` [Virtio-fs] " Greg Kurz 2021-03-12 14:10 ` [PATCH 1/3] virtiofsd: Don't allow empty paths in lookup_name() Greg Kurz 2021-03-12 14:10 ` [Virtio-fs] " Greg Kurz 2021-03-12 15:13 ` Connor Kuehl 2021-03-12 15:49 ` Greg Kurz 2021-03-14 23:38 ` Vivek Goyal 2021-03-12 14:10 ` [PATCH 2/3] virtiofsd: Convert some functions to return bool Greg Kurz 2021-03-12 14:10 ` [Virtio-fs] " Greg Kurz 2021-03-12 15:13 ` Connor Kuehl 2021-03-14 23:36 ` Vivek Goyal 2021-03-12 14:10 ` [PATCH 3/3] virtiofsd: Don't allow empty filenames Greg Kurz 2021-03-12 14:10 ` [Virtio-fs] " Greg Kurz 2021-03-12 15:13 ` Connor Kuehl 2021-03-14 23:36 ` Vivek Goyal 2021-03-15 10:06 ` Greg Kurz 2021-03-15 15:18 ` Dr. David Alan Gilbert 2021-03-15 17:37 ` Greg Kurz 2021-03-15 17:55 ` Vivek Goyal
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.