diff mbox series

[v5,4/5] vvfat: Fix reading files with non-continuous clusters

Message ID 9c0be954b608da7d47c6a0c120da332c16798d2a.1718195956.git.amjadsharafi10@gmail.com (mailing list archive)
State New, archived
Headers show
Series vvfat: Fix write bugs for large files and add iotests | expand

Commit Message

Amjad Alsharafi June 12, 2024, 12:43 p.m. UTC
When reading with `read_cluster` we get the `mapping` with
`find_mapping_for_cluster` and then we call `open_file` for this
mapping.
The issue appear when its the same file, but a second cluster that is
not immediately after it, imagine clusters `500 -> 503`, this will give
us 2 mappings one has the range `500..501` and another `503..504`, both
point to the same file, but different offsets.

When we don't open the file since the path is the same, we won't assign
`s->current_mapping` and thus accessing way out of bound of the file.

From our example above, after `open_file` (that didn't open anything) we
will get the offset into the file with
`s->cluster_size*(cluster_num-s->current_mapping->begin)`, which will
give us `0x2000 * (504-500)`, which is out of bound for this mapping and
will produce some issues.

Signed-off-by: Amjad Alsharafi <amjadsharafi10@gmail.com>
---
 block/vvfat.c | 23 ++++++++++++++++-------
 1 file changed, 16 insertions(+), 7 deletions(-)

Comments

Kevin Wolf July 18, 2024, 3:20 p.m. UTC | #1
Am 12.06.2024 um 14:43 hat Amjad Alsharafi geschrieben:
> When reading with `read_cluster` we get the `mapping` with
> `find_mapping_for_cluster` and then we call `open_file` for this
> mapping.
> The issue appear when its the same file, but a second cluster that is
> not immediately after it, imagine clusters `500 -> 503`, this will give
> us 2 mappings one has the range `500..501` and another `503..504`, both
> point to the same file, but different offsets.
> 
> When we don't open the file since the path is the same, we won't assign
> `s->current_mapping` and thus accessing way out of bound of the file.
> 
> From our example above, after `open_file` (that didn't open anything) we
> will get the offset into the file with
> `s->cluster_size*(cluster_num-s->current_mapping->begin)`, which will
> give us `0x2000 * (504-500)`, which is out of bound for this mapping and
> will produce some issues.
> 
> Signed-off-by: Amjad Alsharafi <amjadsharafi10@gmail.com>
> ---
>  block/vvfat.c | 23 ++++++++++++++++-------
>  1 file changed, 16 insertions(+), 7 deletions(-)
> 
> diff --git a/block/vvfat.c b/block/vvfat.c
> index b63ac5d045..fc570d0610 100644
> --- a/block/vvfat.c
> +++ b/block/vvfat.c
> @@ -1360,15 +1360,24 @@ static int open_file(BDRVVVFATState* s,mapping_t* mapping)
>  {
>      if(!mapping)
>          return -1;
> +    int new_path = 1;
>      if(!s->current_mapping ||
> -            strcmp(s->current_mapping->path,mapping->path)) {
> -        /* open file */
> -        int fd = qemu_open_old(mapping->path,
> +            s->current_mapping->info.file.offset
> +                != mapping->info.file.offset ||

I'm wondering if this couldn't just be s->current_mapping != mapping?

> +            (new_path = strcmp(s->current_mapping->path, mapping->path))) {

If both the path and the offset change, we still want to set new_path, I
think. And if we didn't already have a mapping, we also need to open the
file.

Actually, setting a variable inside the condition makes it kind of hard
to read, so if s->current_mapping != mapping works, we can do the check
only in the conditon below:

> +        if (new_path) {

if (!s->current_mapping ||
    strcmp(s->current_mapping->path, mapping->path))

> +            /* open file */
> +            int fd = qemu_open_old(mapping->path,
>                                 O_RDONLY | O_BINARY | O_LARGEFILE);
> -        if(fd<0)
> -            return -1;
> -        vvfat_close_current_file(s);
> -        s->current_fd = fd;
> +            if (fd < 0) {
> +                return -1;
> +            }
> +            vvfat_close_current_file(s);
> +
> +            s->current_fd = fd;
> +        }
> +        assert(s->current_fd);
>          s->current_mapping = mapping;
>      }

Kevin
Amjad Alsharafi July 19, 2024, 12:20 a.m. UTC | #2
On Thu, Jul 18, 2024 at 05:20:36PM +0200, Kevin Wolf wrote:
> Am 12.06.2024 um 14:43 hat Amjad Alsharafi geschrieben:
> > When reading with `read_cluster` we get the `mapping` with
> > `find_mapping_for_cluster` and then we call `open_file` for this
> > mapping.
> > The issue appear when its the same file, but a second cluster that is
> > not immediately after it, imagine clusters `500 -> 503`, this will give
> > us 2 mappings one has the range `500..501` and another `503..504`, both
> > point to the same file, but different offsets.
> > 
> > When we don't open the file since the path is the same, we won't assign
> > `s->current_mapping` and thus accessing way out of bound of the file.
> > 
> > From our example above, after `open_file` (that didn't open anything) we
> > will get the offset into the file with
> > `s->cluster_size*(cluster_num-s->current_mapping->begin)`, which will
> > give us `0x2000 * (504-500)`, which is out of bound for this mapping and
> > will produce some issues.
> > 
> > Signed-off-by: Amjad Alsharafi <amjadsharafi10@gmail.com>
> > ---
> >  block/vvfat.c | 23 ++++++++++++++++-------
> >  1 file changed, 16 insertions(+), 7 deletions(-)
> > 
> > diff --git a/block/vvfat.c b/block/vvfat.c
> > index b63ac5d045..fc570d0610 100644
> > --- a/block/vvfat.c
> > +++ b/block/vvfat.c
> > @@ -1360,15 +1360,24 @@ static int open_file(BDRVVVFATState* s,mapping_t* mapping)
> >  {
> >      if(!mapping)
> >          return -1;
> > +    int new_path = 1;
> >      if(!s->current_mapping ||
> > -            strcmp(s->current_mapping->path,mapping->path)) {
> > -        /* open file */
> > -        int fd = qemu_open_old(mapping->path,
> > +            s->current_mapping->info.file.offset
> > +                != mapping->info.file.offset ||
> 
> I'm wondering if this couldn't just be s->current_mapping != mapping?

Actually, you are totally right. Not sure what made me go for this.

I tried also to test with only checking if the path changed, but it
fails on some tests. So the offset is important.
For that reason, checking just the mapping ptr is better since we won't
have 2 mappings with same file and offset.

I'll then use this change. Thanks

> 
> > +            (new_path = strcmp(s->current_mapping->path, mapping->path))) {
> 
> If both the path and the offset change, we still want to set new_path, I
> think. And if we didn't already have a mapping, we also need to open the
> file.
> 
> Actually, setting a variable inside the condition makes it kind of hard
> to read, so if s->current_mapping != mapping works, we can do the check
> only in the conditon below:
> 
> > +        if (new_path) {
> 
> if (!s->current_mapping ||
>     strcmp(s->current_mapping->path, mapping->path))
> 
> > +            /* open file */
> > +            int fd = qemu_open_old(mapping->path,
> >                                 O_RDONLY | O_BINARY | O_LARGEFILE);
> > -        if(fd<0)
> > -            return -1;
> > -        vvfat_close_current_file(s);
> > -        s->current_fd = fd;
> > +            if (fd < 0) {
> > +                return -1;
> > +            }
> > +            vvfat_close_current_file(s);
> > +
> > +            s->current_fd = fd;
> > +        }
> > +        assert(s->current_fd);
> >          s->current_mapping = mapping;
> >      }
> 
> Kevin
>
Amjad Alsharafi July 19, 2024, 12:29 a.m. UTC | #3
On Jul 19 2024, at 8:20 am, Amjad Alsharafi <amjadsharafi10@gmail.com> wrote:

> On Thu, Jul 18, 2024 at 05:20:36PM +0200, Kevin Wolf wrote:
>> Am 12.06.2024 um 14:43 hat Amjad Alsharafi geschrieben:
>> > When reading with `read_cluster` we get the `mapping` with
>> > `find_mapping_for_cluster` and then we call `open_file` for this
>> > mapping.
>> > The issue appear when its the same file, but a second cluster that is
>> > not immediately after it, imagine clusters `500 -> 503`, this will give
>> > us 2 mappings one has the range `500..501` and another `503..504`, both
>> > point to the same file, but different offsets.
>> > 
>> > When we don't open the file since the path is the same, we won't assign
>> > `s->current_mapping` and thus accessing way out of bound of the file.
>> > 
>> > From our example above, after `open_file` (that didn't open
>> anything) we
>> > will get the offset into the file with
>> > `s->cluster_size*(cluster_num-s->current_mapping->begin)`, which will
>> > give us `0x2000 * (504-500)`, which is out of bound for this
>> mapping and
>> > will produce some issues.
>> > 
>> > Signed-off-by: Amjad Alsharafi <amjadsharafi10@gmail.com>
>> > ---
>> >  block/vvfat.c | 23 ++++++++++++++++-------
>> >  1 file changed, 16 insertions(+), 7 deletions(-)
>> > 
>> > diff --git a/block/vvfat.c b/block/vvfat.c
>> > index b63ac5d045..fc570d0610 100644
>> > --- a/block/vvfat.c
>> > +++ b/block/vvfat.c
>> > @@ -1360,15 +1360,24 @@ static int open_file(BDRVVVFATState*
>> s,mapping_t* mapping)
>> >  {
>> >      if(!mapping)
>> >          return -1;
>> > +    int new_path = 1;
>> >      if(!s->current_mapping ||
>> > -            strcmp(s->current_mapping->path,mapping->path)) {
>> > -        /* open file */
>> > -        int fd = qemu_open_old(mapping->path,
>> > +            s->current_mapping->info.file.offset
>> > +                != mapping->info.file.offset ||
>> 
>> I'm wondering if this couldn't just be s->current_mapping != mapping?
> 
> Actually, you are totally right. Not sure what made me go for this.
> 
> I tried also to test with only checking if the path changed, but it
> fails on some tests. So the offset is important.
> For that reason, checking just the mapping ptr is better since we won't
> have 2 mappings with same file and offset.
> 
> I'll then use this change. Thanks

Should I send a new patch? since most commits are reviewed now

> 
>> 
>> > +            (new_path = strcmp(s->current_mapping->path,
>> mapping->path))) {
>> 
>> If both the path and the offset change, we still want to set
>> new_path, I
>> think. And if we didn't already have a mapping, we also need to open the
>> file.
>> 
>> Actually, setting a variable inside the condition makes it kind of hard
>> to read, so if s->current_mapping != mapping works, we can do the check
>> only in the conditon below:
>> 
>> > +        if (new_path) {
>> 
>> if (!s->current_mapping ||
>>     strcmp(s->current_mapping->path, mapping->path))
>> 
>> > +            /* open file */
>> > +            int fd = qemu_open_old(mapping->path,
>> >                                 O_RDONLY | O_BINARY | O_LARGEFILE);
>> > -        if(fd<0)
>> > -            return -1;
>> > -        vvfat_close_current_file(s);
>> > -        s->current_fd = fd;
>> > +            if (fd < 0) {
>> > +                return -1;
>> > +            }
>> > +            vvfat_close_current_file(s);
>> > +
>> > +            s->current_fd = fd;
>> > +        }
>> > +        assert(s->current_fd);
>> >          s->current_mapping = mapping;
>> >      }
>> 
>> Kevin
>> 
>
Kevin Wolf July 19, 2024, 8:22 a.m. UTC | #4
Am 19.07.2024 um 02:29 hat Amjad Alsharafi geschrieben:
> 
> 
> On Jul 19 2024, at 8:20 am, Amjad Alsharafi <amjadsharafi10@gmail.com> wrote:
> 
> > On Thu, Jul 18, 2024 at 05:20:36PM +0200, Kevin Wolf wrote:
> >> Am 12.06.2024 um 14:43 hat Amjad Alsharafi geschrieben:
> >> > When reading with `read_cluster` we get the `mapping` with
> >> > `find_mapping_for_cluster` and then we call `open_file` for this
> >> > mapping.
> >> > The issue appear when its the same file, but a second cluster that is
> >> > not immediately after it, imagine clusters `500 -> 503`, this will give
> >> > us 2 mappings one has the range `500..501` and another `503..504`, both
> >> > point to the same file, but different offsets.
> >> > 
> >> > When we don't open the file since the path is the same, we won't assign
> >> > `s->current_mapping` and thus accessing way out of bound of the file.
> >> > 
> >> > From our example above, after `open_file` (that didn't open
> >> anything) we
> >> > will get the offset into the file with
> >> > `s->cluster_size*(cluster_num-s->current_mapping->begin)`, which will
> >> > give us `0x2000 * (504-500)`, which is out of bound for this
> >> mapping and
> >> > will produce some issues.
> >> > 
> >> > Signed-off-by: Amjad Alsharafi <amjadsharafi10@gmail.com>
> >> > ---
> >> >  block/vvfat.c | 23 ++++++++++++++++-------
> >> >  1 file changed, 16 insertions(+), 7 deletions(-)
> >> > 
> >> > diff --git a/block/vvfat.c b/block/vvfat.c
> >> > index b63ac5d045..fc570d0610 100644
> >> > --- a/block/vvfat.c
> >> > +++ b/block/vvfat.c
> >> > @@ -1360,15 +1360,24 @@ static int open_file(BDRVVVFATState*
> >> s,mapping_t* mapping)
> >> >  {
> >> >      if(!mapping)
> >> >          return -1;
> >> > +    int new_path = 1;
> >> >      if(!s->current_mapping ||
> >> > -            strcmp(s->current_mapping->path,mapping->path)) {
> >> > -        /* open file */
> >> > -        int fd = qemu_open_old(mapping->path,
> >> > +            s->current_mapping->info.file.offset
> >> > +                != mapping->info.file.offset ||
> >> 
> >> I'm wondering if this couldn't just be s->current_mapping != mapping?
> > 
> > Actually, you are totally right. Not sure what made me go for this.
> > 
> > I tried also to test with only checking if the path changed, but it
> > fails on some tests. So the offset is important.
> > For that reason, checking just the mapping ptr is better since we won't
> > have 2 mappings with same file and offset.
> > 
> > I'll then use this change. Thanks
> 
> Should I send a new patch? since most commits are reviewed now

Yes, please do. I think I reviewed the whole series.

Kevin

> > 
> >> 
> >> > +            (new_path = strcmp(s->current_mapping->path,
> >> mapping->path))) {
> >> 
> >> If both the path and the offset change, we still want to set
> >> new_path, I
> >> think. And if we didn't already have a mapping, we also need to open the
> >> file.
> >> 
> >> Actually, setting a variable inside the condition makes it kind of hard
> >> to read, so if s->current_mapping != mapping works, we can do the check
> >> only in the conditon below:
> >> 
> >> > +        if (new_path) {
> >> 
> >> if (!s->current_mapping ||
> >>     strcmp(s->current_mapping->path, mapping->path))
> >> 
> >> > +            /* open file */
> >> > +            int fd = qemu_open_old(mapping->path,
> >> >                                 O_RDONLY | O_BINARY | O_LARGEFILE);
> >> > -        if(fd<0)
> >> > -            return -1;
> >> > -        vvfat_close_current_file(s);
> >> > -        s->current_fd = fd;
> >> > +            if (fd < 0) {
> >> > +                return -1;
> >> > +            }
> >> > +            vvfat_close_current_file(s);
> >> > +
> >> > +            s->current_fd = fd;
> >> > +        }
> >> > +        assert(s->current_fd);
> >> >          s->current_mapping = mapping;
> >> >      }
> >> 
> >> Kevin
> >> 
> > 
>
diff mbox series

Patch

diff --git a/block/vvfat.c b/block/vvfat.c
index b63ac5d045..fc570d0610 100644
--- a/block/vvfat.c
+++ b/block/vvfat.c
@@ -1360,15 +1360,24 @@  static int open_file(BDRVVVFATState* s,mapping_t* mapping)
 {
     if(!mapping)
         return -1;
+    int new_path = 1;
     if(!s->current_mapping ||
-            strcmp(s->current_mapping->path,mapping->path)) {
-        /* open file */
-        int fd = qemu_open_old(mapping->path,
+            s->current_mapping->info.file.offset
+                != mapping->info.file.offset ||
+            (new_path = strcmp(s->current_mapping->path, mapping->path))) {
+
+        if (new_path) {
+            /* open file */
+            int fd = qemu_open_old(mapping->path,
                                O_RDONLY | O_BINARY | O_LARGEFILE);
-        if(fd<0)
-            return -1;
-        vvfat_close_current_file(s);
-        s->current_fd = fd;
+            if (fd < 0) {
+                return -1;
+            }
+            vvfat_close_current_file(s);
+
+            s->current_fd = fd;
+        }
+        assert(s->current_fd);
         s->current_mapping = mapping;
     }
     return 0;