qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH v3 5/8] block: Fix potential Null pointer derefe


From: Max Reitz
Subject: Re: [Qemu-devel] [PATCH v3 5/8] block: Fix potential Null pointer dereferences in vvfat.c
Date: Fri, 12 Oct 2018 17:14:17 +0200
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.0

On 31.08.18 20:16, Liam Merwick wrote:
> The calls to bdrv_new_open_driver(), find_mapping_for_cluster(),
> and array_get_next() may return NULL but it isn't always checked for
> before dereferencing the value returned.
> 
> Signed-off-by: Liam Merwick <address@hidden>
> Reviewed-by: Darren Kenny <address@hidden>
> Reviewed-by: Mark Kanda <address@hidden>
> ---
>  block/vvfat.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 56 insertions(+)
> 
> diff --git a/block/vvfat.c b/block/vvfat.c
> index fc41841a5c3c..0f1f10a2f94b 100644
> --- a/block/vvfat.c
> +++ b/block/vvfat.c
> @@ -448,6 +448,9 @@ static direntry_t *create_long_filename(BDRVVVFATState 
> *s, const char *filename)
>  
>      for(i=0;i<number_of_entries;i++) {
>          entry=array_get_next(&(s->directory));
> +        if (entry == NULL) {
> +            continue;
> +        }

This is a bug in array_ensure_allocated().  It uses g_realloc() with a
non-zero size, so that function will never return NULL.  It will rather
abort().

Therefore, array_ensure_allocated() cannot fail.  Consequentially,
array_get_next() cannot fail.

>          entry->attributes=0xf;
>          entry->reserved[0]=0;
>          entry->begin=0;
> @@ -665,6 +668,9 @@ static inline void fat_set(BDRVVVFATState* s,unsigned int 
> cluster,uint32_t value
>      } else {
>          int offset = (cluster*3/2);
>          unsigned char* p = array_get(&(s->fat), offset);
> +        if (p == NULL) {
> +            return;
> +        }

This is only reached if array_get_next() was called before.  Therefore,
this cannot return NULL.

However, an assert(array->pointer); in array_get() can't hurt.

>          switch (cluster&1) {
>          case 0:
>                  p[0] = value&0xff;
> @@ -730,6 +736,9 @@ static inline direntry_t* 
> create_short_and_long_name(BDRVVVFATState* s,
>  
>      if(is_dot) {
>          entry=array_get_next(&(s->directory));
> +        if (entry == NULL) {
> +            return NULL;
> +        }

As above.

>          memset(entry->name, 0x20, sizeof(entry->name));
>          memcpy(entry->name,filename,strlen(filename));
>          return entry;
> @@ -844,6 +853,12 @@ static int read_directory(BDRVVVFATState* s, int 
> mapping_index)
>          /* create mapping for this file */
>          if(!is_dot && !is_dotdot && (S_ISDIR(st.st_mode) || st.st_size)) {
>              s->current_mapping = array_get_next(&(s->mapping));
> +            if (s->current_mapping == NULL) {
> +                fprintf(stderr, "Failed to create mapping for file\n");
> +                g_free(buffer);
> +                closedir(dir);
> +                return -2;
> +            }

As above.

>              s->current_mapping->begin=0;
>              s->current_mapping->end=st.st_size;
>              /*
> @@ -941,6 +956,9 @@ static int init_directories(BDRVVVFATState* s,
>      /* add volume label */
>      {
>          direntry_t* entry=array_get_next(&(s->directory));
> +        if (entry == NULL) {
> +            return -1;
> +        }

As above.

>          entry->attributes=0x28; /* archive | volume label */
>          memcpy(entry->name, s->volume_label, sizeof(entry->name));
>      }
> @@ -953,6 +971,9 @@ static int init_directories(BDRVVVFATState* s,
>      s->cluster_count=sector2cluster(s, s->sector_count);
>  
>      mapping = array_get_next(&(s->mapping));
> +    if (mapping == NULL) {
> +        return -1;
> +    }

As above.

>      mapping->begin = 0;
>      mapping->dir_index = 0;
>      mapping->info.dir.parent_mapping_index = -1;
> @@ -1630,6 +1651,9 @@ static void schedule_rename(BDRVVVFATState* s,
>          uint32_t cluster, char* new_path)
>  {
>      commit_t* commit = array_get_next(&(s->commits));
> +    if (commit == NULL) {
> +        return;
> +    }

As above.

>      commit->path = new_path;
>      commit->param.rename.cluster = cluster;
>      commit->action = ACTION_RENAME;
> @@ -1639,6 +1663,9 @@ static void schedule_writeout(BDRVVVFATState* s,
>          int dir_index, uint32_t modified_offset)
>  {
>      commit_t* commit = array_get_next(&(s->commits));
> +    if (commit == NULL) {
> +        return;
> +    }

As above.

>      commit->path = NULL;
>      commit->param.writeout.dir_index = dir_index;
>      commit->param.writeout.modified_offset = modified_offset;
> @@ -1649,6 +1676,9 @@ static void schedule_new_file(BDRVVVFATState* s,
>          char* path, uint32_t first_cluster)
>  {
>      commit_t* commit = array_get_next(&(s->commits));
> +    if (commit == NULL) {
> +        return;
> +    }

As above.

>      commit->path = path;
>      commit->param.new_file.first_cluster = first_cluster;
>      commit->action = ACTION_NEW_FILE;
> @@ -1657,6 +1687,9 @@ static void schedule_new_file(BDRVVVFATState* s,
>  static void schedule_mkdir(BDRVVVFATState* s, uint32_t cluster, char* path)
>  {
>      commit_t* commit = array_get_next(&(s->commits));
> +    if (commit == NULL) {
> +        return;
> +    }


As above.

>      commit->path = path;
>      commit->param.mkdir.cluster = cluster;
>      commit->action = ACTION_MKDIR;
> @@ -2261,6 +2294,9 @@ static mapping_t* insert_mapping(BDRVVVFATState* s,
>      }
>      if (index >= s->mapping.next || mapping->begin > begin) {
>          mapping = array_insert(&(s->mapping), index, 1);
> +        if (mapping == NULL) {
> +            return NULL;
> +        }

I haven't checked array_insert()'s code for buffer overflows, but just
like the other functions above, it cannot ever return NULL (unless
array->item_size were 0, which it never is), because g_realloc() never
returns NULL.

>          mapping->path = NULL;
>          adjust_mapping_indices(s, index, +1);
>      }
> @@ -2428,6 +2464,9 @@ static int commit_direntries(BDRVVVFATState* s,
>      direntry_t* direntry = array_get(&(s->directory), dir_index);
>      uint32_t first_cluster = dir_index == 0 ? 0 : 
> begin_of_direntry(direntry);
>      mapping_t* mapping = find_mapping_for_cluster(s, first_cluster);
> +    if (mapping == NULL) {
> +        return -1;
> +    }

This looks like a real issue.  I'm not sufficiently proficient in vvfat
to know whether this would warrant an assertion (it looks after the root
directory, which probably just should be there), so I'm OK with just
returning an error here.

However, qemu's codestyle forbids mixing statements with declarations,
so this needs to be moved below all of the declarations that follow still.

>      int factor = 0x10 * s->sectors_per_cluster;
>      int old_cluster_count, new_cluster_count;
> @@ -2494,6 +2533,9 @@ DLOG(fprintf(stderr, "commit_direntries for %s, 
> parent_mapping_index %d\n", mapp
>          direntry = array_get(&(s->directory), first_dir_index + i);
>          if (is_directory(direntry) && !is_dot(direntry)) {
>              mapping = find_mapping_for_cluster(s, first_cluster);
> +            if (mapping == NULL) {
> +                return -1;
> +            }

Looks OK.

>              assert(mapping->mode & MODE_DIRECTORY);
>              ret = commit_direntries(s, first_dir_index + i,
>                  array_index(&(s->mapping), mapping));
> @@ -2522,6 +2564,10 @@ static int commit_one_file(BDRVVVFATState* s,
>      assert(offset < size);
>      assert((offset % s->cluster_size) == 0);
>  
> +    if (mapping == NULL) {
> +        return -1;
> +    }
> +

Agreed.

(What's going on with the return values in this function?)

>      for (i = s->cluster_size; i < offset; i += s->cluster_size)
>          c = modified_fat_get(s, c);
>  
> @@ -2668,6 +2714,9 @@ static int handle_renames_and_mkdirs(BDRVVVFATState* s)
>          if (commit->action == ACTION_RENAME) {
>              mapping_t* mapping = find_mapping_for_cluster(s,
>                      commit->param.rename.cluster);
> +            if (mapping == NULL) {
> +                return -1;
> +            }
>              char* old_path = mapping->path;

Agreed, but needs to be moved below this declaration.

>  
>              assert(commit->path);
> @@ -2692,6 +2741,9 @@ static int handle_renames_and_mkdirs(BDRVVVFATState* s)
>                          if (is_file(d) || (is_directory(d) && !is_dot(d))) {
>                              mapping_t* m = find_mapping_for_cluster(s,
>                                      begin_of_direntry(d));
> +                            if (m == NULL) {
> +                                return -3;
> +                            }

I wouldn't try to follow the weird style (unless you understand the
reasoning behind it, which I don't). :-)

Just return either -1 or -EIO.

>                              int l = strlen(m->path);
>                              char* new_path = g_malloc(l + diff + 1);

And again, needs to be moved below the declarations, although we don't
want to do the g_malloc() before, of course.

>  
> @@ -3193,6 +3245,10 @@ static int enable_write_target(BlockDriverState *bs, 
> Error **errp)
>  
>      backing = bdrv_new_open_driver(&vvfat_write_target, NULL, 
> BDRV_O_ALLOW_RDWR,
>                                     &error_abort);
> +    if (!backing) {
> +        ret = -EINVAL;
> +        goto err;
> +    }

This cannot return NULL because we pass &error_abort.  Therefore, qemu
will abort before this function returns NULL.

Max

>      *(void**) backing->opaque = s;
>  
>      bdrv_set_backing_hd(s->bs, backing, &error_abort);
> 


Attachment: signature.asc
Description: OpenPGP digital signature


reply via email to

[Prev in Thread] Current Thread [Next in Thread]