Skip to content

Instantly share code, notes, and snippets.

@bcantrill bcantrill/ub.c
Created Jan 11, 2019

Embed
What would you like to do?
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

This comment has been minimized.

Copy link
Owner Author

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

This comment has been minimized.

Copy link

commented Jan 11, 2019

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

This comment has been minimized.

Copy link

commented Jan 11, 2019

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

https://godbolt.org/z/s3O6JH

@bcantrill

This comment has been minimized.

Copy link
Owner Author

commented Jan 11, 2019

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

This comment has been minimized.

Copy link

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
You can’t perform that action at this time.