[PATCH] avoid undefined behavior due to oversized shifts

Nickolai Zeldovich nickolai@csail.mit.edu
Thu Jan 3 07:38:00 GMT 2013


In C, shifting a value by more than its bitwidth is undefined behavior. 
The get_value() function in bfd/elflink.c (invoked as part of 
bfd_elf_perform_complex_relocation) sometimes shifts a bfd_vma value by 
more than its bitwidth.  Some compilers (e.g., clang) can produce a 
constant value in such cases, leading to unintended results:

   % cat foo.c
   unsigned long x;
   unsigned long bar(void);

   unsigned long foo(int v) {
     switch (v) {
     case 8:
       return x << (8*v) | bar();
     }
   }
   % clang foo.c -S -o - -O
   ...
   foo:                                    # @foo
   .Ltmp2:
   	.cfi_startproc
   # BB#0:
   	pushq	%rbp
   .Ltmp3:
   	.cfi_def_cfa_offset 16
   .Ltmp4:
   	.cfi_offset %rbp, -16
   	movq	%rsp, %rbp
   .Ltmp5:
   	.cfi_def_cfa_register %rbp
   	cmpl	$8, %edi
   	jne	.LBB0_2
   # BB#1:
   	callq	bar
   .LBB0_2:
   	movq	$-1, %rax
   	popq	%rbp
   	ret
   ...

The patch below fixes this issue by avoiding oversized shifts.

Nickolai.

---

--- binutils-2.23.51.0.8/bfd/elflink.c	2012-12-21 14:40:41.000000000 -0500
+++ binutils-2.23.51.0.8/bfd/elflink.c	2013-01-03 02:22:48.895604348 -0500
@@ -8023,23 +8023,28 @@

    for (; size; size -= chunksz, location += chunksz)
      {
+      if (chunksz < sizeof(x))
+	x <<= (8 * chunksz);
+      else
+	x = 0;
+
        switch (chunksz)
  	{
  	default:
  	case 0:
  	  abort ();
  	case 1:
-	  x = (x << (8 * chunksz)) | bfd_get_8 (input_bfd, location);
+	  x |= bfd_get_8 (input_bfd, location);
  	  break;
  	case 2:
-	  x = (x << (8 * chunksz)) | bfd_get_16 (input_bfd, location);
+	  x |= bfd_get_16 (input_bfd, location);
  	  break;
  	case 4:
-	  x = (x << (8 * chunksz)) | bfd_get_32 (input_bfd, location);
+	  x |= bfd_get_32 (input_bfd, location);
  	  break;
  	case 8:
  #ifdef BFD64
-	  x = (x << (8 * chunksz)) | bfd_get_64 (input_bfd, location);
+	  x |= bfd_get_64 (input_bfd, location);
  #else
  	  abort ();
  #endif



More information about the Binutils mailing list