Bug 25308 - Multiple null pointer dereferencs in bfd module due to not checking return value of bfd_malloc
Summary: Multiple null pointer dereferencs in bfd module due to not checking return va...
Status: RESOLVED FIXED
Alias: None
Product: binutils
Classification: Unclassified
Component: binutils (show other bugs)
Version: unspecified
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2019-12-22 03:28 UTC by Nguyễn Đức Mạnh
Modified: 2020-01-03 14:44 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:


Attachments
Crashes due to not checking return value of bfd_malloc (8.79 KB, application/x-zip-compressed)
2019-12-22 03:28 UTC, Nguyễn Đức Mạnh
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Nguyễn Đức Mạnh 2019-12-22 03:28:30 UTC
Created attachment 12141 [details]
Crashes due to not checking return value of bfd_malloc

There are multiple null pointer dereferences in bfd module due to calling bfd_malloc without checking if the return pointer is null.

## Analysis
Here is the list of vulnerable issues:
### Issue 1
In bfd/pef.c:bfd_pef_scan_start_address(), loaderbuf is malloced without checking if the return pointer is null. Later, loaderbuf is feeded to bfd_read => Null pointer derefence.
--------------------------------------
  loaderbuf = bfd_malloc (loaderlen);
  if (bfd_seek (abfd, loadersec->filepos, SEEK_SET) < 0)
    goto error;
  if (bfd_bread ((void *) loaderbuf, loaderlen, abfd) != loaderlen)
    goto error;
--------------------------------------
### Issue 2, 3
In bfd/pef.c:bfd_pef_parse_function_stubs(), libraries and imports are malloced without checking the return values. Later, they are passed to bfd_pef_parse_imported_library and bfd_pef_parse_imported_symbol:
--------------------------------------
  libraries = bfd_malloc
    (header.imported_library_count * sizeof (bfd_pef_imported_library));
  imports = bfd_malloc
    (header.total_imported_symbol_count * sizeof (bfd_pef_imported_symbol));
...
      ret = bfd_pef_parse_imported_library
	(abfd, loaderbuf + 56 + (i * 24), 24, &libraries[i]);
...
      ret = (bfd_pef_parse_imported_symbol
	     (abfd,
	      loaderbuf + 56 + (header.imported_library_count * 24) + (i * 4),
	      4, &imports[i]));
--------------------------------------
### Issue 4, 5
In bfd, pef.c:bfd_pef_parse_symbols(), codebuf and loadersec are malloced without checking the return values. Later, they are passed to bfd_read:
--------------------------------------
  codesec = bfd_get_section_by_name (abfd, "code");
  if (codesec != NULL)
    {
      codelen = codesec->size;
      codebuf = bfd_malloc (codelen);
      if (bfd_seek (abfd, codesec->filepos, SEEK_SET) < 0)
	goto end;
      if (bfd_bread ((void *) codebuf, codelen, abfd) != codelen)
	goto end;
    }

  loadersec = bfd_get_section_by_name (abfd, "loader");
  if (loadersec != NULL)
    {
      loaderlen = loadersec->size;
      loaderbuf = bfd_malloc (loaderlen);
      if (bfd_seek (abfd, loadersec->filepos, SEEK_SET) < 0)
	goto end;
      if (bfd_bread ((void *) loaderbuf, loaderlen, abfd) != loaderlen)
	goto end;
    }
---------------------------------------
### Issue 6
In bfd/pef.c:bfd_pef_print_loader_section(), loaderbuf is malloced without checking the return value. Later, loaderbuf is passed to bfd_bread:
---------------------------------------
  loaderbuf = bfd_malloc (loaderlen);

  if (bfd_seek (abfd, loadersec->filepos, SEEK_SET) < 0
      || bfd_bread ((void *) loaderbuf, loaderlen, abfd) != loaderlen
      || loaderlen < 56
      || bfd_pef_parse_loader_header (abfd, loaderbuf, 56, &header) < 0)
    {
      free (loaderbuf);
      return -1;
    }
---------------------------------------
### Issue 7
In bfd/mach-o.c:bfd_mach_o_core_fetch_environment(), dsym_filename is passed to sprintf:
---------------------------------------
  dsym_filename = (char *)bfd_malloc (strlen (base_bfd->filename)
				       + strlen (dsym_subdir) + 1
				       + strlen (base_basename) + 1);
  sprintf (dsym_filename, "%s%s/%s",
	   base_bfd->filename, dsym_subdir, base_basename);

---------------------------------------
### Issue 8
In bfd/elf-properties.c/_bfd_elf_convert_gnu_properties(), contents is malloced without checking the return value. Later, contents is passed to elf_write_gnu_properties:
---------------------------------------
  if (size > bfd_section_size (isec))
    {
      contents = (bfd_byte *) bfd_malloc (size);
      free (*ptr);
      *ptr = contents;
    }
  else
    contents = *ptr;

  *ptr_size = size;

  /* Generate the output .note.gnu.property section.  */
  elf_write_gnu_properties (ibfd, contents, list, size, 1 << align_shift);
---------------------------------------
### Issue 9
In bfd/elf32-arm.c:bfd_elf32_arm_vfp11_fix_veneer_locations(), tmp_name is malloced without checking the return value. Later, tmp_name is passed to sprintf:
---------------------------------------
  tmp_name = (char *) bfd_malloc ((bfd_size_type) strlen
				  (VFP11_ERRATUM_VENEER_ENTRY_NAME) + 10);
...
	      sprintf (tmp_name, VFP11_ERRATUM_VENEER_ENTRY_NAME,
		       errnode->u.b.veneer->u.v.id);
----------------------------------------
### Issue 10
In bfd/elf32-arm.c:bfd_elf32_arm_stm32l4xx_fix_veneer_locations(), tmp_name is malloced without checking the return value. Later, tmp_name is passed to sprintf:
----------------------------------------
tmp_name = (char *) bfd_malloc ((bfd_size_type) strlen
				  (STM32L4XX_ERRATUM_VENEER_ENTRY_NAME) + 10);
...
	      sprintf (tmp_name, STM32L4XX_ERRATUM_VENEER_ENTRY_NAME,
		       errnode->u.b.veneer->u.v.id);
----------------------------------------
### Issue 11
In bfd/elf32-arm.c:elf32_arm_filter_cmse_symbols(), cmse_name is malloced without checking the return value. Later, cmse_name is passed to snprintf:
----------------------------------------
  cmse_name = (char *) bfd_malloc (maxnamelen);
...
        snprintf (cmse_name, maxnamelen, "%s%s", CMSE_PREFIX, name);
----------------------------------------
### Issue 12
In bfd/elf32-arm.c, edited_contents is malloced without checking the return value. Later, it is used in many places, for example, copy_exidx_entry:
----------------------------------------
      bfd_vma add_to_offsets = 0;
...
		  copy_exidx_entry (output_bfd, edited_contents + out_index * 8,
				    contents + in_index * 8, add_to_offsets);

----------------------------------------

## PoC
There are crash PoCs for issue 1 and issue 2. They are in the attachment. Binutils-gdb is built in 32-bit version so that it is easier for bfd_malloc to return null.
---------------Version------------------
Tested with version 39aa149769fd05fb6fade43bd41c1d7b6d63d06b of github.com/bminor/binutils-gdb
---------Crash log issue 1--------------
root@manh-ubuntu16:~/fuzz/fuzz_binutils# valgrind binutils-gdb-gcc-32/binutils/objdump  -x crash-objdump-bfd_pef_scan_start_address 
==1089== Memcheck, a memory error detector
==1089== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==1089== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==1089== Command: binutils-gdb-gcc-32/binutils/objdump -x crash-objdump-bfd_pef_scan_start_address
==1089== 
==1089== Invalid write of size 1
==1089==    at 0x403337C: __GI_mempcpy (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==1089==    by 0x40D1364: _IO_file_xsgetn (fileops.c:1392)
==1089==    by 0x40D333D: _IO_sgetn (genops.c:467)
==1089==    by 0x40C68F6: fread (iofread.c:38)
==1089==    by 0x86A6739: cache_bread_1 (cache.c:319)
==1089==    by 0x86A6864: cache_bread (cache.c:358)
==1089==    by 0x820ED2E: bfd_bread (bfdio.c:211)
==1089==    by 0x8634E6B: bfd_pef_scan_start_address (pef.c:483)
==1089==    by 0x8635167: bfd_pef_scan (pef.c:556)
==1089==    by 0x8635397: bfd_pef_object_p (pef.c:605)
==1089==    by 0x8212246: bfd_check_format_matches (format.c:322)
==1089==    by 0x8052A83: display_object_bfd (objdump.c:4163)
==1089==  Address 0x0 is not stack'd, malloc'd or (recently) free'd
==1089== 
==1089== 
==1089== Process terminating with default action of signal 11 (SIGSEGV)
==1089==  Access not within mapped region at address 0x0
==1089==    at 0x403337C: __GI_mempcpy (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==1089==    by 0x40D1364: _IO_file_xsgetn (fileops.c:1392)
==1089==    by 0x40D333D: _IO_sgetn (genops.c:467)
==1089==    by 0x40C68F6: fread (iofread.c:38)
==1089==    by 0x86A6739: cache_bread_1 (cache.c:319)
==1089==    by 0x86A6864: cache_bread (cache.c:358)
==1089==    by 0x820ED2E: bfd_bread (bfdio.c:211)
==1089==    by 0x8634E6B: bfd_pef_scan_start_address (pef.c:483)
==1089==    by 0x8635167: bfd_pef_scan (pef.c:556)
==1089==    by 0x8635397: bfd_pef_object_p (pef.c:605)
==1089==    by 0x8212246: bfd_check_format_matches (format.c:322)
==1089==    by 0x8052A83: display_object_bfd (objdump.c:4163)
==1089==  If you believe this happened as a result of a stack
==1089==  overflow in your program's main thread (unlikely but
==1089==  possible), you can try to increase the size of the
==1089==  main thread stack using the --main-stacksize= flag.
==1089==  The main thread stack size used in this run was 8388608.
==1089== 
==1089== HEAP SUMMARY:
==1089==     in use at exit: 10,821 bytes in 8 blocks
==1089==   total heap usage: 37 allocs, 29 frees, 19,532 bytes allocated
==1089== 
==1089== LEAK SUMMARY:
==1089==    definitely lost: 0 bytes in 0 blocks
==1089==    indirectly lost: 0 bytes in 0 blocks
==1089==      possibly lost: 0 bytes in 0 blocks
==1089==    still reachable: 10,821 bytes in 8 blocks
==1089==         suppressed: 0 bytes in 0 blocks
==1089== Rerun with --leak-check=full to see details of leaked memory
==1089== 
==1089== For counts of detected and suppressed errors, rerun with: -v
==1089== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
Segmentation fault (core dumped)
---------------------------------------------
----------Crash log for issue 2--------------
root@manh-ubuntu16:~/fuzz/fuzz_binutils# valgrind binutils-gdb-gcc-32/binutils/objdump  -x crash-objdump-bfd_pef_parse_function_stubs 
==1117== Memcheck, a memory error detector
==1117== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==1117== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==1117== Command: binutils-gdb-gcc-32/binutils/objdump -x crash-objdump-bfd_pef_parse_function_stubs
==1117== 

crash-objdump-bfd_pef_parse_function_stubs:     file format pef
crash-objdump-bfd_pef_parse_function_stubs
architecture: powerpc:common64, flags 0x000001ff:
HAS_RELOC, EXEC_P, HAS_LINENO, HAS_DEBUG, HAS_SYMS, HAS_LOCALS, DYNAMIC, WP_TEXT, D_PAGED
start address 0x0000000000000000

==1117== Invalid write of size 4
==1117==    at 0x863489F: bfd_pef_parse_imported_library (pef.c:354)
==1117==    by 0x8635928: bfd_pef_parse_function_stubs (pef.c:761)
==1117==    by 0x8635F8B: bfd_pef_parse_symbols (pef.c:929)
==1117==    by 0x8636011: bfd_pef_count_symbols (pef.c:951)
==1117==    by 0x8636027: bfd_pef_get_symtab_upper_bound (pef.c:957)
==1117==    by 0x804AE3A: slurp_symtab (objdump.c:705)
==1117==    by 0x8052667: dump_bfd (objdump.c:4037)
==1117==    by 0x8052A97: display_object_bfd (objdump.c:4165)
==1117==    by 0x8052D1F: display_any_bfd (objdump.c:4255)
==1117==    by 0x8052D8C: display_file (objdump.c:4276)
==1117==    by 0x80537A5: main (objdump.c:4603)
==1117==  Address 0x0 is not stack'd, malloc'd or (recently) free'd
==1117== 
==1117== 
==1117== Process terminating with default action of signal 11 (SIGSEGV)
==1117==  Access not within mapped region at address 0x0
==1117==    at 0x863489F: bfd_pef_parse_imported_library (pef.c:354)
==1117==    by 0x8635928: bfd_pef_parse_function_stubs (pef.c:761)
==1117==    by 0x8635F8B: bfd_pef_parse_symbols (pef.c:929)
==1117==    by 0x8636011: bfd_pef_count_symbols (pef.c:951)
==1117==    by 0x8636027: bfd_pef_get_symtab_upper_bound (pef.c:957)
==1117==    by 0x804AE3A: slurp_symtab (objdump.c:705)
==1117==    by 0x8052667: dump_bfd (objdump.c:4037)
==1117==    by 0x8052A97: display_object_bfd (objdump.c:4165)
==1117==    by 0x8052D1F: display_any_bfd (objdump.c:4255)
==1117==    by 0x8052D8C: display_file (objdump.c:4276)
==1117==    by 0x80537A5: main (objdump.c:4603)
==1117==  If you believe this happened as a result of a stack
==1117==  overflow in your program's main thread (unlikely but
==1117==  possible), you can try to increase the size of the
==1117==  main thread stack using the --main-stacksize= flag.
==1117==  The main thread stack size used in this run was 8388608.
==1117== 
==1117== HEAP SUMMARY:
==1117==     in use at exit: 18,179 bytes in 10 blocks
==1117==   total heap usage: 67 allocs, 57 frees, 60,491 bytes allocated
==1117== 
==1117== LEAK SUMMARY:
==1117==    definitely lost: 0 bytes in 0 blocks
==1117==    indirectly lost: 0 bytes in 0 blocks
==1117==      possibly lost: 0 bytes in 0 blocks
==1117==    still reachable: 18,179 bytes in 10 blocks
==1117==         suppressed: 0 bytes in 0 blocks
==1117== Rerun with --leak-check=full to see details of leaked memory
==1117== 
==1117== For counts of detected and suppressed errors, rerun with: -v
==1117== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
Segmentation fault (core dumped)
---------------------------------------------

--
Thanks & Regards,
Nguyen Duc Manh
VinCSS (a member of Vingroup)
[M] (+84) 346136886
[E] v.manhnd@vincss.net
[W] www.vincss.net
Comment 1 cvs-commit@gcc.gnu.org 2020-01-03 14:42:32 UTC
The master branch has been updated by Nick Clifton <nickc@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=7a0fb7be96e0ce79e1ae429bc1ba913e5244d537

commit 7a0fb7be96e0ce79e1ae429bc1ba913e5244d537
Author: Nick Clifton <nickc@redhat.com>
Date:   Fri Jan 3 14:41:02 2020 +0000

    Fix potential illegal memory access failures in the BFD library by ensuring that the return value from bfd_malloc() is checked before it is used.
    
    	PR 25308
    	* elf-properties.c (_bfd_elf_convert_gnu_properties): Check the
    	return value from bfd_malloc.
    	* elf32-arm.c (bfd_elf32_arm_vfp11_fix_veneer_locations): Likewise.
    	(bfd_elf32_arm_stm32l4xx_fix_veneer_locations): Likewise.
    	(elf32_arm_filter_cmse_symbols): Likewise.
    	(elf32_arm_write_section): Likewise.
    	* mach-o.c (bfd_mach_o_core_fetch_environment): Likewise.
    	(bfd_mach_o_follow_dsym): Likewise.
    	* pef.c (bfd_pef_print_loader_section): Likewise.
    	(bfd_pef_scan_start_address): Likewise.
    	(bfd_pef_parse_function_stubs): Likewise.
    	(bfd_pef_parse_symbols): Likewise.
Comment 2 Nick Clifton 2020-01-03 14:44:22 UTC
Hi Nguyen,

  Thanks for reporting these problems.  I have checked in a patch to
  add tests of the return value from bfd_malloc in the places that
  you indicated.

Cheers
  Nick