This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
[PATCH] avoid undefined behavior due to oversized shifts
- From: Nickolai Zeldovich <nickolai at csail dot mit dot edu>
- To: binutils at sourceware dot org
- Cc: nickolai at csail dot mit dot edu
- Date: Thu, 3 Jan 2013 02:38:14 -0500 (EST)
- Subject: [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