This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] avoid undefined behavior due to oversized shifts


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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]