Skip to content

Instantly share code, notes, and snippets.

@bcantrill
Created January 11, 2019 18:41
Show Gist options
  • Save bcantrill/7b30914541fe6c3306fc174992d497ec to your computer and use it in GitHub Desktop.
Save bcantrill/7b30914541fe6c3306fc174992d497ec to your computer and use it in GitHub Desktop.
Punishment doesn't fit the crime?
#define NULL ((void *)0)
static char *arr[2] = { "nasal", "demons" };
long
func()
{
int i;
for (i = 0; i <= 2; i++) {
if (arr[i] == NULL && i == 0)
return (0xbad);
}
return (0xfad);
}
@bcantrill
Copy link
Author

bcantrill commented Jan 11, 2019

Compiling -O2 on GCC 7:

% gcc --version
gcc (GCC) 7.3.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
% gcc -O2 -c ub.c
% dis ub.o
disassembly for ub.o


section .text
func()
    func:     55                 pushl  %ebp
    func+0x1: b8 ad 0b 00 00     movl   $0xbad,%eax
    func+0x6: 89 e5              movl   %esp,%ebp
    func+0x8: 5d                 popl   %ebp
    func+0x9: c3                 ret    

Seems a tad harsh!

@voutilad
Copy link

I don't know what's going on here but this is fun:

ubuntu:tmp$ gcc --version
gcc (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

ubuntu:tmp$ gcc -O1 -c ub.c && objdump -d ub.o 

ub.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <func>:
   0:   b8 ad 0f 00 00          mov    $0xfad,%eax
   5:   c3                      retq   
ubuntu:tmp$ gcc -O2 -c ub.c && objdump -d ub.o 

ub.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <func>:
   0:   b8 ad 0b 00 00          mov    $0xbad,%eax
   5:   c3                      retq   

@rileyberton
Copy link

GCC 6.3 or earlier doesn't even produce output under -O2. Heh.

https://godbolt.org/z/s3O6JH

@bcantrill
Copy link
Author

So, adding -fno-aggressive-loop-optimizations makes this code run correctly -- a flag that itself was added because the aggressive loop optimization broke SPEC CPU 2006. As for the GCC code itself, it is remarkably frank about this optimization.

@sebres
Copy link

sebres commented Jan 14, 2019

More weird example (https://godbolt.org/z/VM3nMo):

static char *arr[2] = { "nasal", "demons" };

long
func_n()
{
    int i;
    for (i = 0; i <= 2; i++) {
        if (!arr[i])
            return (1);
    }
    return (0);
}
long
func_p()
{
    int i;
    for (i = 0; i <= 2; i++) {
        if (arr[i])
            return (1);
    }
    return (0);
}

long
func_control(int control)
{
    int i;
    for (i = 0; i <= 2; i++) {
        if ( (!control && !arr[i]) || (control && arr[i]) )
            return (1);
    }
    return (0);
}

results into:

func_n:
  mov eax, 1
  ret
func_p:
  mov eax, 1
  ret
func_control:
  mov edx, 3
  mov eax, 1
  test edi, edi
  jne .L4
.L9:
  sub edx, 1
  jne .L9
  xor eax, eax
.L4:
  ret

As one could see the UB is abruptly "conditional" (in func_control) if one extend it with control-switch.

And as already said, what on this UB is unexpected for developer (sorry for tautology):

  • this does not produce the warning array subscript 2 is above array bounds of 'char *[2]' [-Warray-bounds].
    Even not with -Wall or -Wextra;
  • small amends (like variable control condition in func_control) change the UB drastically;
  • it is extremely unstable between gcc-versions (and of course the compile-flags).
    Clear, it is an UB, but why this could not be a bit "stable" UB, remaining the same result between versions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment