CVE-2017-9763: Improper Restriction of Operations within the Bounds of a Memory Buffer (CWE-119)
value: The grub_ext2_read_block function in fs/ext2.c in GNU GRUB before 2013-11-12, as used in shlr/grub/fs/ext2.c in radare2 1.5.0, allows remote attackers to cause a denial of service (excessive stack use and application crash) via a crafted binary file, related to use of a variable-size stack array.
CWE description: The product performs operations on a memory buffer, but it reads from or writes to a memory location outside the buffer's intended boundary. This may result in read or write operations on unexpected memory locations that could be linked to other variables, data structures, or internal program data.
CVSS v3 base score: 7.5
Published: 2017-06-19T16:29Z
Repository: https://github.com/radare/radare2
Language: C
Files changed: libr/core/cmd_mount.c, shlr/grub/fs/ext2.c, shlr/grub/fs/fshelp.c, shlr/grub/grubfs.c, sys/rebuild.sh
Commit date: 2017-06-12 09:57:30 +0200
Commit message: Fix #7723 - crash in ext2 GRUB code because of variable size array in stack

Diff: diff --git a/libr/core/cmd_mount.c b/libr/core/cmd_mount.c
index ab9648787fc..5c66b60a365 100644
--- a/libr/core/cmd_mount.c
+++ b/libr/core/cmd_mount.c
@@ -33,28 +33,31 @@ static int cmd_mount(void *data, const char *_input) {
 	input = oinput = strdup (_input);
 
 	switch (*input) {
 	case ' ':
 		input++;
-		if (input[0]==' ')
+		if (input[0]==' ') {
 			input++;
+		}
 		ptr = strchr (input, ' ');
 		if (ptr) {
 			*ptr = 0;
 			ptr++;
 			ptr2 = strchr (ptr, ' ');
 			if (ptr2) {
 				*ptr2 = 0;
 				off = r_num_math (core->num, ptr2+1);
 			}
-			if (!r_fs_mount (core->fs, ptr, input, off))
+			if (!r_fs_mount (core->fs, ptr, input, off)) {
 				eprintf ("Cannot mount %s\n", input);
+			}
 		} else {
-			if (!(ptr = r_fs_name (core->fs, core->offset)))
+			if (!(ptr = r_fs_name (core->fs, core->offset))) {
 				eprintf ("Unknown filesystem type\n");
-			else if (!r_fs_mount (core->fs, ptr, input, core->offset))
+			} else if (!r_fs_mount (core->fs, ptr, input, core->offset)) {
 				eprintf ("Cannot mount %s\n", input);
+			}
 			free (ptr);
 		}
 		break;
 	case '-':
 		r_fs_umount (core->fs, input+1);
diff --git a/shlr/grub/fs/ext2.c b/shlr/grub/fs/ext2.c
index 7567d7075c5..dfd2a5e11e0 100644
--- a/shlr/grub/fs/ext2.c
+++ b/shlr/grub/fs/ext2.c
@@ -40,10 +40,11 @@
 #define FILETYPE_INO_MASK	0170000
 #define FILETYPE_INO_REG	0100000
 #define FILETYPE_INO_DIRECTORY	0040000
 #define FILETYPE_INO_SYMLINK	0120000
 
+#include <stdlib.h>
 #include <grub/err.h>
 #include <grub/file.h>
 #include <grub/mm.h>
 #include <grub/misc.h>
 #include <grub/disk.h>
@@ -365,12 +366,13 @@ grub_ext4_find_leaf (struct grub_ext2_data *data, char *buf,
 
       block = grub_le_to_cpu16 (index[i].leaf_hi);
       block = (block << 32) + grub_le_to_cpu32 (index[i].leaf);
       if (grub_disk_read (data->disk,
                           block << LOG2_EXT2_BLOCK_SIZE (data),
-                          0, EXT2_BLOCK_SIZE(data), buf))
+                          0, EXT2_BLOCK_SIZE(data), buf)) {
         return 0;
+      }
 
       ext_block = (struct grub_ext4_extent_header *) buf;
     }
 }
 
@@ -383,25 +385,25 @@ grub_ext2_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
   unsigned int blksz = EXT2_BLOCK_SIZE (data);
   int log2_blksz = LOG2_EXT2_BLOCK_SIZE (data);
 
   if (grub_le_to_cpu32(inode->flags) & EXT4_EXTENTS_FLAG)
     {
-#ifndef _MSC_VER
-	  char buf[EXT2_BLOCK_SIZE (data)];
-#else
-	  char * buf = grub_malloc (EXT2_BLOCK_SIZE(data));
-#endif
+	  char * buf = grub_malloc (EXT2_BLOCK_SIZE (data));
+          if (!buf) {
+              return -1;
+          }
       struct grub_ext4_extent_header *leaf;
       struct grub_ext4_extent *ext;
       int i;
 
       leaf = grub_ext4_find_leaf (data, buf,
 		  (struct grub_ext4_extent_header *) inode->blocks.dir_blocks,
 		  fileblock);
       if (! leaf)
         {
           grub_error (GRUB_ERR_BAD_FS, "invalid extent");
+	  free (buf);
           return -1;
         }
 
       ext = (struct grub_ext4_extent *) (leaf + 1);
       for (i = 0; i < grub_le_to_cpu16 (leaf->entries); i++)
@@ -411,46 +413,52 @@ grub_ext2_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
         }
 
       if (--i >= 0)
         {
           fileblock -= grub_le_to_cpu32 (ext[i].block);
-          if (fileblock >= grub_le_to_cpu16 (ext[i].len))
+          if (fileblock >= grub_le_to_cpu16 (ext[i].len)) {
+  	    free (buf);
             return 0;
-          else
+          } else
             {
               grub_disk_addr_t start;
 
               start = grub_le_to_cpu16 (ext[i].start_hi);
               start = (start << 32) + grub_le_to_cpu32 (ext[i].start);
+  	    free (buf);
 
               return fileblock + start;
             }
         }
       else
         {
           grub_error (GRUB_ERR_BAD_FS, "something wrong with extent");
+  	    free (buf);
           return -1;
         }
+free (buf);
     }
   /* Direct blocks.  */
   if (fileblock < INDIRECT_BLOCKS) {
     blknr = grub_le_to_cpu32 (inode->blocks.dir_blocks[fileblock]);
   /* Indirect.  */
   } else if (fileblock < INDIRECT_BLOCKS + blksz / 4)
     {
       grub_uint32_t *indir;
 
       indir = grub_malloc (blksz);
-      if (! indir)
+      if (! indir) {
 	return grub_errno;
+}
 
       if (grub_disk_read (data->disk,
 			  ((grub_disk_addr_t)
 			   grub_le_to_cpu32 (inode->blocks.indir_block))
 			  << log2_blksz,
-			  0, blksz, indir))
+			  0, blksz, indir)) {
 	return grub_errno;
+}
 
       blknr = grub_le_to_cpu32 (indir[fileblock - INDIRECT_BLOCKS]);
       grub_free (indir);
     }
   /* Double indirect.  */
@@ -461,26 +469,29 @@ grub_ext2_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
       unsigned int rblock = fileblock - (INDIRECT_BLOCKS
 					 + blksz / 4);
       grub_uint32_t *indir;
 
       indir = grub_malloc (blksz);
-      if (! indir)
+      if (! indir) {
 	return grub_errno;
+}
 
       if (grub_disk_read (data->disk,
 			  ((grub_disk_addr_t)
 			   grub_le_to_cpu32 (inode->blocks.double_indir_block))
 			  << log2_blksz,
-			  0, blksz, indir))
+			  0, blksz, indir)) {
 	return grub_errno;
+}
 
       if (grub_disk_read (data->disk,
 			  ((grub_disk_addr_t)
 			   grub_le_to_cpu32 (indir[rblock / perblock]))
 			  << log2_blksz,
-			  0, blksz, indir))
+			  0, blksz, indir)) {
 	return grub_errno;
+}
 
       blknr = grub_le_to_cpu32 (indir[rblock % perblock]);
             grub_free (indir);
     }
   /* triple indirect.  */
diff --git a/shlr/grub/fs/fshelp.c b/shlr/grub/fs/fshelp.c
index 338f309997e..bbc540961cf 100644
--- a/shlr/grub/fs/fshelp.c
+++ b/shlr/grub/fs/fshelp.c
@@ -89,15 +89,11 @@ iterate (const char *filename,
 static grub_err_t
 find_file (const char *currpath, grub_fshelp_node_t currroot,
 	   grub_fshelp_node_t *currfound,
 	   struct grub_fshelp_find_file_closure *c)
 {
-#ifndef _MSC_VER
-	char fpath[grub_strlen (currpath) + 1];
-#else
 	char *fpath = grub_malloc (grub_strlen (currpath) + 1);
-#endif
   char *name = fpath;
   char *next;
   enum grub_fshelp_filetype type = GRUB_FSHELP_DIR;
   grub_fshelp_node_t currnode = currroot;
   grub_fshelp_node_t oldnode = currroot;
@@ -111,10 +107,11 @@ find_file (const char *currpath, grub_fshelp_node_t currroot,
     name++;
 
   if (! *name)
     {
       *currfound = currnode;
+free (fpath);
       return 0;
     }
 
   for (;;)
     {
@@ -133,10 +130,11 @@ find_file (const char *currpath, grub_fshelp_node_t currroot,
       /* At this point it is expected that the current node is a
 	 directory, check if this is true.  */
       if (type != GRUB_FSHELP_DIR)
 	{
 	  free_node (currnode, c);
+free (fpath);
 	  return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
 	}
 
       cc.name = name;
       cc.type = &type;
@@ -144,12 +142,14 @@ find_file (const char *currpath, grub_fshelp_node_t currroot,
       cc.currnode = &currnode;
       /* Iterate over the directory.  */
       found = c->iterate_dir (currnode, iterate, &cc);
       if (! found)
 	{
-	  if (grub_errno)
+	  if (grub_errno) {
+free (fpath);
 	    return grub_errno;
+}
 
 	  break;
 	}
 
       /* Read in the symlink and follow it.  */
@@ -160,20 +160,22 @@ find_file (const char *currpath, grub_fshelp_node_t currroot,
 	  /* Test if the symlink does not loop.  */
 	  if (++(c->symlinknest) == 8)
 	    {
 	      free_node (currnode, c);
 	      free_node (oldnode, c);
+free (fpath);
 	      return grub_error (GRUB_ERR_SYMLINK_LOOP,
 				 "too deep nesting of symlinks");
 	    }
 
 	  symlink = c->read_symlink (currnode);
 	  free_node (currnode, c);
 
 	  if (!symlink)
 	    {
 	      free_node (oldnode, c);
+free (fpath);
 	      return grub_errno;
 	    }
 
 	  /* The symlink is an absolute path, go back to the root inode.  */
 	  if (symlink[0] == '/')
@@ -188,10 +190,11 @@ find_file (const char *currpath, grub_fshelp_node_t currroot,
 	  grub_free (symlink);
 
 	  if (grub_errno)
 	    {
 	      free_node (oldnode, c);
+free (fpath);
 	      return grub_errno;
 	    }
 	}
 
       free_node (oldnode, c);
@@ -199,16 +202,18 @@ find_file (const char *currpath, grub_fshelp_node_t currroot,
       /* Found the node!  */
       if (! next || *next == '\0')
 	{
 	  *currfound = currnode;
 	  c->foundtype = type;
+free (fpath);
 	  return 0;
 	}
 
       name = next;
     }
 
+free (fpath);
   return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
 }
 
 /* Lookup the node PATH.  The node ROOTNODE describes the root of the
    directory tree.  The node found is returned in FOUNDNODE, which is
diff --git a/shlr/grub/grubfs.c b/shlr/grub/grubfs.c
index 699af4c1bea..ca5b6b01ad1 100644
--- a/shlr/grub/grubfs.c
+++ b/shlr/grub/grubfs.c
@@ -1,6 +1,6 @@
-/* radare - LGPL - Copyright 2011 pancake<nopcode.org> */
+/* radare - LGPL - Copyright 2011-2017 pancake<nopcode.org> */
 
 #include <r_io.h>
 #include <r_fs.h>
 #include "grubfs.h"
 #include <stdio.h>
@@ -15,24 +15,24 @@ static void* empty (int sz) {
 	if (p) memset (p, '\0', sz);
 	return p;
 }
 
 static grub_err_t read_foo (struct grub_disk *disk, grub_disk_addr_t sector, grub_size_t size, char *buf) {
-	if (disk != NULL) {
-		const int blocksize = 512; // unhardcode 512
-		int ret;
-		RIOBind *iob = disk->data;
-		if (bio) iob = bio;
-		//printf ("io %p\n", file->root->iob.io);
-		ret = iob->read_at (iob->io, delta+(blocksize*sector),
-			(ut8*)buf, size*blocksize);
-		if (ret == -1)
-			return 1;
-		//printf ("DISK PTR = %p\n", disk->data);
-		//printf ("\nBUF: %x %x %x %x\n", buf[0], buf[1], buf[2], buf[3]);
-	} else eprintf ("oops. no disk\n");
-	return 0; // 0 is ok
+	if (!disk) {
+		eprintf ("oops. no disk\n");
+		return 1;
+	}
+	const int blocksize = 512; // TODO unhardcode 512
+	RIOBind *iob = disk->data;
+	if (bio) {
+		iob = bio;
+	}
+	//printf ("io %p\n", file->root->iob.io);
+	if (iob->read_at (iob->io, delta+(blocksize*sector), (ut8*)buf, size*blocksize) == -1) {
+		return 1;
+	}
+	return 0;
 }
 
 GrubFS *grubfs_new (struct grub_fs *myfs, void *data) {
 	struct grub_file *file;
 	GrubFS *gfs = empty (sizeof (GrubFS));
@@ -56,12 +56,13 @@ grub_disk_t grubfs_disk (void *data) {
 	return disk;
 }
 
 void grubfs_free (GrubFS *gf) {
 	if (gf) {
-		if (gf->file && gf->file->device)
+		if (gf->file && gf->file->device) {
 			free (gf->file->device->disk);
+		}
 		//free (gf->file->device);
 		free (gf->file);
 		free (gf);
 	}
 }
diff --git a/sys/rebuild.sh b/sys/rebuild.sh
index 24f4dd246cc..4bd10fa4d0a 100755
--- a/sys/rebuild.sh
+++ b/sys/rebuild.sh
@@ -70,11 +70,11 @@ RebuildGdb() {
 	Rebuild libr/io
 	Rebuild libr/debug
 }
 
 case "$1" in
-fs)     RebuildFs; ;;
+grub|fs)RebuildFs; ;;
 bin)    RebuildBin ; ;;
 gdb)    RebuildGdb ; ;;
 sdb)    RebuildSdb ; ;;
 spp)    RebuildSpp ; ;;
 bin)    RebuildBin ; ;;

Vulnerable code: 		if (input[0]==' ')
			if (!r_fs_mount (core->fs, ptr, input, off))
			if (!(ptr = r_fs_name (core->fs, core->offset)))
			else if (!r_fs_mount (core->fs, ptr, input, core->offset))
                          0, EXT2_BLOCK_SIZE(data), buf))
#ifndef _MSC_VER
	  char buf[EXT2_BLOCK_SIZE (data)];
#else
	  char * buf = grub_malloc (EXT2_BLOCK_SIZE(data));
#endif
          if (fileblock >= grub_le_to_cpu16 (ext[i].len))
          else
      if (! indir)
			  0, blksz, indir))
      if (! indir)
			  0, blksz, indir))
			  0, blksz, indir))
#ifndef _MSC_VER
	char fpath[grub_strlen (currpath) + 1];
#else
#endif
	  if (grub_errno)
/* radare - LGPL - Copyright 2011 pancake<nopcode.org> */
	if (disk != NULL) {
		const int blocksize = 512; // unhardcode 512
		int ret;
		RIOBind *iob = disk->data;
		if (bio) iob = bio;
		//printf ("io %p\n", file->root->iob.io);
		ret = iob->read_at (iob->io, delta+(blocksize*sector),
			(ut8*)buf, size*blocksize);
		if (ret == -1)
			return 1;
		//printf ("DISK PTR = %p\n", disk->data);
		//printf ("\nBUF: %x %x %x %x\n", buf[0], buf[1], buf[2], buf[3]);
	} else eprintf ("oops. no disk\n");
	return 0; // 0 is ok
		if (gf->file && gf->file->device)
fs)     RebuildFs; ;;
Fixed code: 		if (input[0]==' ') {
		}
			if (!r_fs_mount (core->fs, ptr, input, off)) {
			}
			if (!(ptr = r_fs_name (core->fs, core->offset))) {
			} else if (!r_fs_mount (core->fs, ptr, input, core->offset)) {
			}
#include <stdlib.h>
                          0, EXT2_BLOCK_SIZE(data), buf)) {
      }
	  char * buf = grub_malloc (EXT2_BLOCK_SIZE (data));
          if (!buf) {
              return -1;
          }
	  free (buf);
          if (fileblock >= grub_le_to_cpu16 (ext[i].len)) {
  	    free (buf);
          } else
  	    free (buf);
  	    free (buf);
free (buf);
      if (! indir) {
}
			  0, blksz, indir)) {
}
      if (! indir) {
}
			  0, blksz, indir)) {
}
			  0, blksz, indir)) {
}
free (fpath);
free (fpath);
	  if (grub_errno) {
free (fpath);
}
free (fpath);
free (fpath);
free (fpath);
free (fpath);
free (fpath);
/* radare - LGPL - Copyright 2011-2017 pancake<nopcode.org> */
	if (!disk) {
		eprintf ("oops. no disk\n");
		return 1;
	}
	const int blocksize = 512; // TODO unhardcode 512
	RIOBind *iob = disk->data;
	if (bio) {
		iob = bio;
	}
	//printf ("io %p\n", file->root->iob.io);
	if (iob->read_at (iob->io, delta+(blocksize*sector), (ut8*)buf, size*blocksize) == -1) {
		return 1;
	}
	return 0;
		if (gf->file && gf->file->device) {
		}
grub|fs)RebuildFs; ;;