Thread: Bug in gcc

  1. #1
    11DE784A SirPrattlepod's Avatar
    Join Date
    Aug 2013
    Posts
    485

    Bug in gcc

    I found a bug in gcc. Reproducible using:

    Code:
    #include <stdio.h>
    
    const char *usage(int test)
    {
        char s[15];
        
        sprintf(s, "%d\n", test);
        return s;
    }
    
    void main(void)
    {
        int a;
        
        a += 100;
        printf("a is %s\n", usage(a));
    }
    Expected result is "a is 100" but instead it prints "a is"

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    What bug?

    You're returning a pointer to a local variable.

    Saying const doesn't change the lifetime of that variable.

    void main is also wrong.

    Try enabling some warnings to tell you that the things you think are bugs are wrong.
    Code:
    $ gcc -Wall bar.c
    bar.c: In function ‘usage’:
    bar.c:8:5: warning: function returns address of local variable [enabled by default]
    bar.c: At top level:
    bar.c:11:6: warning: return type of ‘main’ is not ‘int’ [-Wmain]
    bar.c: In function ‘main’:
    bar.c:15:7: warning: ‘a’ is used uninitialized in this function [-Wuninitialized]
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  3. #3
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Ah, yes. The bug is in the mind of the programmer (writing code with undefined behaviour), but programmer wrongly attributes fault to compiler.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  4. #4
    11DE784A SirPrattlepod's Avatar
    Join Date
    Aug 2013
    Posts
    485
    Quote Originally Posted by Salem View Post
    void main is also wrong.
    Really?! The standard says

    5.1.2.2.3
    [...] If the return type is not compatible with int, the termination status returned to the host environment is unspecified.
    I'm ok with it being unspecified because it doesn't matter. In fact the annotation even gives an example!

    You are therefore free to declare main() as required by your program

    void main (void)
    I don't see the problem

  5. #5
    Hurry Slowly vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,788
    a is uninitialized, so a += 100 is undefined
    All problems in computer science can be solved by another level of indirection,
    except for the problem of too many layers of indirection.
    – David J. Wheeler

  6. #6
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    > Really?! The standard says
    Really, you need to do better at cherry-picking your evidence.
    Quote Originally Posted by c99
    5.1.2.2.1 Program startup
    1 The function called at program startup is named main. The implementation declares no
    prototype for this function. It shall be defined with a return type of int and with no
    parameters:
    int main(void) { /* ... */ }
    or with two parameters (referred to here as argc and argv, though any names may be
    used, as they are local to the function in which they are declared):
    int main(int argc, char *argv[]) { /* ... */ }
    or equivalent;8) or in some other implementation-defined manner.

    2 If they are declared, the parameters to the main function shall obey the following
    constraints:
    — The value of argc shall be nonnegative.
    — argv[argc] shall be a null pointer.
    — If the value of argc is greater than zero, the array members argv[0] through
    argv[argc-1] inclusive shall contain pointers to strings, which are given
    implementation-defined values by the host environment prior to program startup. The
    intent is to supply to the program information determined prior to program startup
    from elsewhere in the hosted environment. If the host environment is not capable of
    supplying strings with letters in both uppercase and lowercase, the implementation
    shall ensure that the strings are received in lowercase.
    — If the value of argc is greater than zero, the string pointed to by argv[0]
    represents the program name; argv[0][0] shall be the null character if the
    program name is not available from the host environment. If the value of argc is
    greater than one, the strings pointed to by argv[1] through argv[argc-1]
    represent the program parameters.
    — The parameters argc and argv and the strings pointed to by the argv array shall
    be modifiable by the program, and retain their last-stored values between program
    startup and program termination.

    5.1.2.2.2 Program execution
    1 In a hosted environment, a program may use all the functions, macros, type definitions,
    and objects described in the library clause (clause 7).

    5.1.2.2.3 Program termination
    1 If the return type of the main function is a type compatible with int, a return from the
    initial call to the main function is equivalent to calling the exit function with the value
    returned by the main function as its argument;9) reaching the } that terminates the main
    function returns a value of 0. If the return type is not compatible with int, the
    termination status returned to the host environment is unspecified.

    7) The intent is that an implementation should identify the nature of, and where possible localize, each
    violation. Of course, an implementation is free to produce any number of diagnostics as long as a
    valid program is still correctly translated. It may also successfully translate an invalid program.
    8) Thus, int can be replaced by a typedef name defined as int, or the type of argv can be written as
    char ** argv, and so on.
    9) In accordance with 6.2.4, objects with automatic storage duration declared in main will no longer
    have storage guaranteed to be reserved in the former case even where they would in the latter.
    The ONLY place where void main is permissible (providing the implementation allows it) is in a free-standing implementation.
    If you don't know what that is, you're using a hosted implementation and int main is the only way to go.

    > I'm ok with it being unspecified because it doesn't matter. In fact the annotation even gives an example!
    I can't find an example of void main in Index of /jtc1/sc22/wg14/www/docs/n869
    Perhaps you would like to share a paragraph/page number with the rest of us.

    > > I'm ok with it being unspecified because it doesn't matter.
    Yeah right, and how much credibility does that have coming from someone who can't even return a pointer from a function, or who can't be bothered to enable warnings to find out that much.

    Do the world a favour and never write a program for someone else, if you think UB is harmless.
    void main(void) - the Wrong Thing
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  7. #7
    11DE784A SirPrattlepod's Avatar
    Join Date
    Aug 2013
    Posts
    485
    Quote Originally Posted by Salem View Post
    Yeah right, and how much credibility does that have coming from someone who can't even return a pointer from a function, or who can't be bothered to enable warnings to find out that much.
    Excuse me? I return pointer perfectly. This is most certainly a gcc bug.

  8. #8
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Yeah, the bug has to do with returning a pointer to a local variable. It is not a bug in gcc. Personally, I would be more cautious before declaring that I have found a bug in a reputable compiler. Such bugs certainly do exist, but I think it would more likely be some bug that I missed in my own code.

    Quote Originally Posted by SirPrattlepod
    I'm ok with it being unspecified because it doesn't matter.
    It can matter since the host environment might use the value returned in some way.

    Quote Originally Posted by SirPrattlepod
    In fact the annotation even gives an example!
    You are therefore free to declare main() as required by your program

    void main (void)
    I cannot find such a note in the text of C99 or the text of my draft copy of C11. However, as an example of what an implementation might define as an alternative, I think it is plausible, but whether or not it is actually allowed depends on whether the implementation documents it as an option.

    I've read some article that argues that this possibility for the return type main to be void with respect to a hosted implementation is a "bug" in the C standard, but as it does not appear "fixed" in C11, I suppose the committee either didn't know about it or figured that it does not matter. That said, when posting in this forum, I suggest that you stick to the versions of main for hosted implementations that are guaranteed by the standard.

    Quote Originally Posted by Salem
    The ONLY place where void main is permissible (providing the implementation allows it) is in a free-standing implementation.
    Refer to: void main() is not legal in C++ but is legal in C. I think it is more debatable than the author sets it out to be, but I tend to agree with the author's parsing of that paragraph.

    EDIT:
    Quote Originally Posted by SirPrattlepod
    Excuse me? I return pointer perfectly. This is most certainly a gcc bug.
    No, this is most certainly a bug in your example code. The variable named s is a non-static local variable. It has automatic storage duration (refer to C99 Clause 6.2.4), i.e., its lifetime is limited to the function. After the return is done, the pointer points to an object for which storage is no longer guaranteed to be reserved. As such, if the storage is say, then zero filled, that would not be a compiler bug.
    Last edited by laserlight; 09-07-2013 at 05:42 AM.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  9. #9
    Registered User
    Join Date
    Mar 2010
    Posts
    583
    What a bizarre thread. Even if you'd never posted before, I'd still raise my eyebrows at someone so supposedly bad at C referring to the standard. As it is.... I guess I'll play along.

    Forget about the annotation -- there's a page of criticisms/bugs here: www.lysator.liu.se/c/schildt.html

    Quote Originally Posted by SirPrattlepod
    I'm ok with it being unspecified because it doesn't matter.
    The standard allows for a freestanding environment [5.1.2.1], without an OS, where the starting point in the code can be absolutely anything, including void main(void). It also describes a hosted environment [5.1.2.2] - with an OS, which is where it says main should return int.

    The value returned from main() is the exit code of the process, which will be passed to exit(). If you return nothing (void) and the system expects you to have returned something, it'll just behave as though you have. If the compiler was supposed to put a return value in a particular register, the code after main() will just read whatever junk is in that register.
    If you look down at [7.22.4.4] "The exit function" you'll see a whole pile of "implementation defined". Which means the OS/libraries can really do anything they like with the exit code -- for example, take some action if the program terminated abnormally. Anything else that receives the exit code (e.g. if the program was invoked from a script) might also take action based on it.

    It's the responsibility of a diligent programmer to make sure their program behaves as the OS/libraries expect. Since void main(void) is a fairly common sight, some compilers specifically allow for it (e.g. MSVC, described in the "Microsoft Specific" part: main: Program Startup). GCC does not and warns about it. In C++ it's illegal. Other compilers will have other rules, but the best thing to do is comply with the standard, and freak out at the prospect of any "unspecified" or "undefined" behaviour!

  10. #10
    SAMARAS std10093's Avatar
    Join Date
    Jan 2011
    Location
    Nice, France
    Posts
    2,694
    Behind every error that gets attached to a machine, at least two human errors are behind this, including the error of accusing the machine.

    //gcc is so world - widely used, making the chance of a programmer finding a bug really small.
    Code - functions and small libraries I use


    It’s 2014 and I still use printf() for debugging.


    "Programs must be written for people to read, and only incidentally for machines to execute. " —Harold Abelson

  11. #11
    11DE784A SirPrattlepod's Avatar
    Join Date
    Aug 2013
    Posts
    485
    Quote Originally Posted by smokeyangel View Post
    <snipped> It's the responsibility of a diligent programmer to make sure their program behaves as the OS/libraries expect.
    Which is what I did. I thought. I'm kind of confused now, but I still think it's right.

  12. #12
    Registered User
    Join Date
    Mar 2010
    Posts
    583
    Quote Originally Posted by SirPrattlepod View Post
    Which is what I did. I thought. I'm kind of confused now, but I still think it's right.
    You say you're using gcc?

    Standards - Using the GNU Compiler Collection (GCC)

    Quote Originally Posted by gcc docs
    The standard also defines two environments for programs, a freestanding environment, required of all implementations and which may not have library facilities beyond those required of freestanding implementations, where the handling of program startup and termination are implementation-defined, and a hosted environment, which is not required, in which all the library facilities are provided and startup is through a function int main (void) or int main (int, char *[]).

  13. #13
    11DE784A SirPrattlepod's Avatar
    Join Date
    Aug 2013
    Posts
    485
    .... and a hosted environment, which is not required, ...

  14. #14
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by SirPrattlepod
    .... and a hosted environment, which is not required, ...
    Yes, but your example program has #include <stdio.h>. In a freestanding environment, the standard library need not provide <stdio.h>.

    EDIT:
    But really, there's no point arguing this. Unless you are posting a program that is obviously meant for a freestanding environment, just stick to the versions of main for hosted implementations that are guaranteed by the standard. It is just easier that way as no one can dispute it, so you can concentrate on what you set out to ask about rather than digress to discuss void main.
    Last edited by laserlight; 09-07-2013 at 06:30 AM.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  15. #15
    SAMARAS std10093's Avatar
    Join Date
    Jan 2011
    Location
    Nice, France
    Posts
    2,694
    For sure it is not.
    Let's see the code one more time. I am not going to comment the void main().
    Code:
    #include <stdio.h>
     
    /* Function usage starts to execute. (when it gets called from main).
     * It needs some space/ memory/ world to live into.
     * It allocates its own space, where herself and everything inside her will live.
     */
    const char *usage(int test)
    {
        /* We declare a local array of 15 chars */
        char s[15];
         
        sprintf(s, "%d\n", test);
        
        /* We return the pointer to the local array s */
        /* gcc says: (with -Wall flag enabled)
           main.c: In function `usage':
           main.c:22: warning: function returns address of local variable
         */
        return s;
    }
    /* Function ended its execution, thus ended its lifetime. As a consequence, her
     * space/memory/world is no longer nescairy and it gets deallocated. As a result,
     * everything inside this world dies, except of static and dynamically allocated
     * variables (we do not have any of them here). We have a local variable in the
     * function, array s, which dies (goes out of scope).
     */
    
     
    int main(void)
    {
        int a; /* Declare a variable named a, with no initial value */
         
        /* We add 100 to a. But a has not a value. Thus we add 100 in 
           something we do not know its value and as a result, this 
           line of code means a = 100 + unknownNumber;             */
        a += 100;
        
        /* We print where the pointer points to. The pointer is returned
           by function usage and when the function was executing, this
           pointer, names s, was pointing to a local variable(a local
           array to be exact). But the following line is equivalent to 
           this: const char* sMain = usage(a);
                 printf("a is %s\n", sMain);
         sMain now, points where s was pointing to. s was pointing to
         a local variable, which went out of scope (died), when the 
         function stopped executing. As a result, there is nothing 
         there. So, sMain points to nothing.
        */ 
        printf("a is %s\n", usage(a));
        
        return 0;
    }
    Code - functions and small libraries I use


    It’s 2014 and I still use printf() for debugging.


    "Programs must be written for people to read, and only incidentally for machines to execute. " —Harold Abelson

Popular pages Recent additions subscribe to a feed