Bug 21440 - Malicious PE with invalid extended relocation can cause binutils/objdumo 2.28 to allocate any-size big memory
Summary: Malicious PE with invalid extended relocation can cause binutils/objdumo 2.28...
Alias: None
Product: binutils
Classification: Unclassified
Component: binutils (show other bugs)
Version: 2.28
: P2 critical
Target Milestone: ---
Assignee: Not yet assigned to anyone
Depends on:
Reported: 2017-04-27 06:10 UTC by jgj212
Modified: 2017-05-02 10:58 UTC (History)
1 user (show)

See Also:
Last reconfirmed:

Malicious PE with invalid extended relocation sample (2.72 KB, application/x-msdownload)
2017-04-27 06:10 UTC, jgj212

Note You need to log in before you can comment on or make changes to this bug.
Description jgj212 2017-04-27 06:10:21 UTC
Created attachment 10029 [details]
Malicious PE with invalid extended relocation sample

$objdump -x $FILE

With x option, objdump will allocate memory to store relocation data, this is done in the following function:
static void
dump_relocs_in_section (bfd *abfd,
			asection *section,
			void *dummy ATTRIBUTE_UNUSED)
	relsize = bfd_get_reloc_upper_bound (abfd, section); //can be controlled
	relpp = (arelent **) xmalloc (relsize);  //allocate memory
  	relcount = bfd_canonicalize_reloc (abfd, section, relpp, syms);  //read reloc data

the relsize is computed as :
(asect->reloc_count + 1) * sizeof (arelent *)    // coff_get_reloc_upper_bound(…)

In the coff standard, reoc_count is WORD, which is 16bit wide. sizeof (arelent *) is constant and small. So normally, the result is small. But when PE section contain extended relocation, everything will go bad. 

PE section header is inited in the following function:
static void
coff_set_alignment_hook (bfd * abfd ATTRIBUTE_UNUSED,
			 asection * section,
			 void * scnhdr)
	/* Check for extended relocs.  */
	if (hdr->s_flags & IMAGE_SCN_LNK_NRELOC_OVFL)
struct external_reloc dst;
      struct internal_reloc n;
      file_ptr oldpos = bfd_tell (abfd);
      bfd_size_type relsz = bfd_coff_relsz (abfd);

      if (bfd_seek (abfd, (file_ptr) hdr->s_relptr, 0) != 0) //seek to the first reloc data
      if (bfd_bread (& dst, relsz, abfd) != relsz) //read the first reloc data

      coff_swap_reloc_in (abfd, &dst, &n);//fill struct
      if (bfd_seek (abfd, oldpos, 0) != 0)

      section->reloc_count = hdr->s_nreloc = n.r_vaddr - 1;  //can be controlled
      section->rel_filepos += relsz;

From the above, when pe section contain extended relocation, reloc_count is assigned from n.r_vaddr.  n is a instance of struct internal_reloc as follow:
struct internal_reloc
  bfd_vma r_vaddr;		/* Virtual address of reference */
  long r_symndx;		/* Index into symbol table	*/
  unsigned short r_type;	/* Relocation type		*/
  unsigned char r_size;		/* Used by RS/6000 and ECOFF	*/
  unsigned char r_extern;	/* Used by ECOFF		*/
  unsigned long r_offset;	/* Used by Alpha ECOFF, SPARC, others */

r_vaddr's type is unsigned long, it can be 32bit and 64bit, according to the os-archive. it is from the dst, which is a instance of struct external_reloc as follow:
struct external_reloc
  char r_vaddr[4];
  char r_symndx[4];
  char r_type[2];

So n.r_vaddr is really 32bit wide,  section->reloc_count is also 32bit wide.
(asect->reloc_count + 1) * sizeof (arelent *) can be very large.

To control the memory size, it just need to modify the r_vaddr field of first reloc data, if can be from 0 to 0Xffffffff.  

This could cause memory exhaustion to dos.
Comment 1 jgj212 2017-04-27 10:20:00 UTC
Credit:The bug was discovered by ADLab of Venustech
Comment 2 Sourceware Commits 2017-05-02 10:56:08 UTC
The master branch has been updated by Nick Clifton <nickc@sourceware.org>:


commit 39ff1b79f687b65f4144ddb379f22587003443fb
Author: Nick Clifton <nickc@redhat.com>
Date:   Tue May 2 11:54:53 2017 +0100

    Prevent memory exhaustion from a corrupt PE binary with an overlarge number of relocs.
    	PR 21440
    	* objdump.c (dump_relocs_in_section): Check for an excessive
    	number of relocs before attempting to dump them.
Comment 3 Nick Clifton 2017-05-02 10:58:19 UTC
Thanks for reporting this bug.

I have applied a patch to add a check for the reloc size being larger than
the size of the file, and hence invalid, before an attempt is made to allocate
memory for the relocs.  I think that this should resolve the problem.