c++pointersgccgcc8

Why declaring a return value for a function that doesn't return nothing leads to run-time crash in gcc8 only


In the following code a function is declared/defined as int setYear(int year_h){year = year_h;} (instead of void setYear(...), leading to run-time crash in gcc 8 AND with the flag -O[X] only.

Specific questions:

  1. What have changed in gcc 8, as it worked in gcc 7 ?
  2. Which flags can I use (if any) to generate a compilation error (rather than a warning) in gcc8 ?

main.cpp:

#include <iostream>

using namespace std;

int year = 2000;
int setYear(int year_h){year = year_h;}

int main()
{
    cout << "Hello World!" << endl;
    setYear(2019);
    cout << "Year: " << year << endl;
    return 0;
}

Run-time crash with:

g++-8 -O2  -o main main.cpp
./main
Hello World!
Hello World!
Segmentation fault (core dumped)

Works with:

g++-7 -O2  -o main main.cpp

or

g++-8 -o main main.cpp

EDIT: The question Omitting return statement in C++ answers my second question, but not the first one (on the difference between gcc 7 and gcc 8).


Solution

  • Starting with GCC 8, your setYear function simply doesn't have the RET instruction when your source is compiled with -Og (at higher levels the function is inlined, making understanding what's going on harder), and the main where the function is called also lacks any continuation.

    See for comparison the original code at Compiler Explorer:

    <...>
    setYear(int):
            mov     DWORD PTR year[rip], edi
    .LC0:
            .string "Hello World!"
    main:
    <...>
    

    And the code where return type int has been changed to void (link):

    <...>
    setYear(int):
            mov     DWORD PTR year[rip], edi
            ret
    .LC0:
            .string "Hello World!"
    .LC1:
            .string "Year: "
    main:
    <...>
    

    This omission alone is enough for execution flow to bump into main (the .strings are declared in another section), executing it again instead of returning to the point of call. Apparenly, gcc doesn't consider it worth adding the RET instruction when there's no return statement in a function other than main returning non-void.

    Of course, in many cases the problem is easily detectable by the compiler at the compilation stage. I'd recommend using -Werror=return-type option, which makes this an error, unconditionally (unlike -Werror in general). It's very rare when you'd want to avoid this option, and it's extremely useful.