CVE-2017-9763 (published 2017-06-19T16:29Z)

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.

Severity: MEDIUM
CVSS v2 base score: 5.0 (AV:N/AC:L/Au:N/C:N/I:N/A:P)
CVSS v3 base score: 7.5, HIGH (CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)

Weakness information:
cwe_id: CWE-119
cwe_name: Improper Restriction of Operations within the Bounds of a Memory Buffer
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.
extended_description: 
            Certain languages allow direct addressing of memory locations and do not automatically ensure that these locations are valid for the memory buffer that is being referenced. This can cause read or write operations to be performed on memory locations that may be associated with other variables, data structures, or internal program data.
         
         

Fix information:
repo_url: https://github.com/radare/radare2
commit_details: repo_url: https://github.com/radare/radare2
author: pancake
author_date: 2017-06-12 09:57:30+02:00
msg: Fix #7723 - crash in ext2 GRUB code because of variable size array in stack
repository: repo_url: https://github.com/radare/radare2
repo_name: radareorg/radare2
description: UNIX-like reverse engineering framework and command-line toolset
repo_language: C
owner: radare
file_changes: filename: cmd_mount.c
change_type: ModificationType.MODIFY
diff: @@ -35,8 +35,9 @@ static int cmd_mount(void *data, const char *_input) {
 	switch (*input) {
 	case ' ':
 		input++;
-		if (input[0]==' ')
+		if (input[0]==' ') {
 			input++;
+		}
 		ptr = strchr (input, ' ');
 		if (ptr) {
 			*ptr = 0;
@@ -46,13 +47,15 @@ static int cmd_mount(void *data, const char *_input) {
 				*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;

programming_language: C
filename: ext2.c
change_type: ModificationType.MODIFY
diff: @@ -42,6 +42,7 @@
 #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>
@@ -367,8 +368,9 @@ grub_ext4_find_leaf (struct grub_ext2_data *data, char *buf,
       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;
     }
@@ -385,11 +387,10 @@ grub_ext2_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
 
   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;
@@ -400,6 +401,7 @@ grub_ext2_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
       if (! leaf)
         {
           grub_error (GRUB_ERR_BAD_FS, "invalid extent");
+	  free (buf);
           return -1;
         }
 
@@ -413,14 +415,16 @@ 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;
             }
@@ -428,8 +432,10 @@ grub_ext2_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
       else
         {
           grub_error (GRUB_ERR_BAD_FS, "something wrong with extent");
+  	    free (buf);
           return -1;
         }
+free (buf);
     }
   /* Direct blocks.  */
   if (fileblock < INDIRECT_BLOCKS) {
@@ -440,15 +446,17 @@ grub_ext2_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
       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);
@@ -463,22 +471,25 @@ grub_ext2_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
       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);

programming_language: C
filename: fshelp.c
change_type: ModificationType.MODIFY
diff: @@ -91,11 +91,7 @@ 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;
@@ -113,6 +109,7 @@ find_file (const char *currpath, grub_fshelp_node_t currroot,
   if (! *name)
     {
       *currfound = currnode;
+free (fpath);
       return 0;
     }
 
@@ -135,6 +132,7 @@ find_file (const char *currpath, grub_fshelp_node_t currroot,
       if (type != GRUB_FSHELP_DIR)
 	{
 	  free_node (currnode, c);
+free (fpath);
 	  return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
 	}
 
@@ -146,8 +144,10 @@ find_file (const char *currpath, grub_fshelp_node_t currroot,
       found = c->iterate_dir (currnode, iterate, &cc);
       if (! found)
 	{
-	  if (grub_errno)
+	  if (grub_errno) {
+free (fpath);
 	    return grub_errno;
+}
 
 	  break;
 	}
@@ -162,6 +162,7 @@ find_file (const char *currpath, grub_fshelp_node_t currroot,
 	    {
 	      free_node (currnode, c);
 	      free_node (oldnode, c);
+free (fpath);
 	      return grub_error (GRUB_ERR_SYMLINK_LOOP,
 				 "too deep nesting of symlinks");
 	    }
@@ -172,6 +173,7 @@ find_file (const char *currpath, grub_fshelp_node_t currroot,
 	  if (!symlink)
 	    {
 	      free_node (oldnode, c);
+free (fpath);
 	      return grub_errno;
 	    }
 
@@ -190,6 +192,7 @@ find_file (const char *currpath, grub_fshelp_node_t currroot,
 	  if (grub_errno)
 	    {
 	      free_node (oldnode, c);
+free (fpath);
 	      return grub_errno;
 	    }
 	}
@@ -201,12 +204,14 @@ find_file (const char *currpath, grub_fshelp_node_t currroot,
 	{
 	  *currfound = currnode;
 	  c->foundtype = type;
+free (fpath);
 	  return 0;
 	}
 
       name = next;
     }
 
+free (fpath);
   return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
 }
 

programming_language: C
filename: grubfs.c
change_type: ModificationType.MODIFY
diff: @@ -1,4 +1,4 @@
-/* radare - LGPL - Copyright 2011 pancake<nopcode.org> */
+/* radare - LGPL - Copyright 2011-2017 pancake<nopcode.org> */
 
 #include <r_io.h>
 #include <r_fs.h>
@@ -17,20 +17,20 @@ static void* empty (int sz) {
 }
 
 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) {
@@ -58,8 +58,9 @@ grub_disk_t grubfs_disk (void *data) {
 
 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);

programming_language: C
filename: rebuild.sh
change_type: ModificationType.MODIFY
diff: @@ -72,7 +72,7 @@ RebuildGdb() {
 }
 
 case "$1" in
-fs)     RebuildFs; ;;
+grub|fs)RebuildFs; ;;
 bin)    RebuildBin ; ;;
 gdb)    RebuildGdb ; ;;
 sdb)    RebuildSdb ; ;;

programming_language: Shell