Это лучшее из того, что я могу придумать в короткие сроки:
Исходный код:
#include <stdio.h>
int undefined(int *a, short *b)
{
*a = 1;
b[0] = 0;
b[1] = 0;
return *a;
}
int main()
{
int x;
short *y = (short*) &x;
int z = undefined(&x, y);
printf("%d\n", z);
return 0;
}
Результирующая сборка с использованием gcc 8.3 -O3
undefined(int*, short*):
mov DWORD PTR [rdi], 1
mov eax, 1
mov DWORD PTR [rsi], 0
ret
.LC0:
.string "%d\n"
main:
sub rsp, 8
mov esi, 1
mov edi, OFFSET FLAT:.LC0
xor eax, eax
call printf
xor eax, eax
add rsp, 8
ret
Посмотри в действии: https://godbolt.org/z/E0XDYt
В частности, он опирается на неопределенное поведение, вызванное приведением адреса int
к short*
, действие, которое нарушает правило строгого наложения имен и, следовательно, вызывает неопределенное поведение.
Начните со сборки undefined()
. Это предполагает, что, поскольку a
и b
являются разными типами, они не могут перекрываться, поэтому он оптимизирует return *a;
в mov eax,1
, даже если он на самом деле вернет ноль, если выберет значение из памяти. Что и происходит при выключенной оптимизации, так что это одна из тех действительно коварных проблем, которая проявляется только в оптимизированной сборке релиза, а не при попытке отладки его с неоптимизированной сборкой отладки.
Однако обратите внимание, как код в main()
пытается и делает его правильно: он встроен, а затем оптимизирует вызов до undefined()
и вместо этого принимает 0
в z
, когда он делает xor eax,eax
чуть выше звонка на printf
. Поэтому он игнорирует то, что он вычислил как возвращаемое значение несколькими строками выше, и вместо этого использует другое значение.
В общем, очень сильно сломанная программа. Именно то, что вы рискуете с неопределенным поведением.