Thread: Crash setting std::ifstream buffer

  1. #1
    Informer -Adrian's Avatar
    Join Date
    Jan 2013
    Posts
    830

    Crash setting std::ifstream buffer

    When I run the following code:
    Code:
    #include <fstream>
    
    class Reader
    {
    public:
        Reader();
    private:
        std::ifstream ifs;
        char readBuffer[64 * 1024];
    };
    
    int main()
    {
        Reader r;
    }
    
    Reader::Reader()
    {
        ifs.rdbuf()->pubsetbuf(readBuffer, sizeof(readBuffer));
    }
    My program crashes:
    Code:
    find: `./testing' terminated by signal 11
    Signal 11 referring to a segfault I think.

    Rearranging the members, so that readBuffer comes first, appears to fix the problem. Any ideas what might be going on?

  2. #2
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    Any ideas what might be going on?
    O_o

    Do you know that `std::ios::.rdbuf()` return a pointer?

    *shrug*

    I can't think right now of a better way to give a hint.
    “Salem Was Wrong!” -- Pedant Necromancer
    “Four isn't random!” -- Gibbering Mouther

  3. #3
    Registered User
    Join Date
    May 2010
    Posts
    4,607
    Any ideas what might be going on?
    From some documentation for C++ class initialisation

    Initialization order

    The order of member initializers in the list is irrelevant: the actual order of initialization is as follows:
    1) If the constructor is for the most-derived class, virtual base classes are initialized in the order in which they appear in depth-first left-to-right traversal of the base class declarations (left-to-right refers to the appearance in base-specifier lists)
    2) Then, direct base classes are initialized in left-to-right order as they appear in this class's base-specifier list
    3) Then, non-static data members are initialized in order of declaration in the class definition.
    4) Finally, the body of the constructor is executed

    (Note: if initialization order was controlled by the appearance in the member initializer lists of different constructors, then the destructor wouldn't be able to ensure that the order of destruction is the reverse of the order of construction)
    Since you use a magic number in the declaration of your buffer the buffer should be initialized before you try to use the sizeof operator.

    Jim

  4. #4
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    >> ... your buffer the buffer should be initialized before you try to use the sizeof operator.
    Learn something new everyday.
    [edit]Well, as Soma says below, it should be init'd before the construct runs...[/edit]

    gg

  5. #5
    Informer -Adrian's Avatar
    Join Date
    Jan 2013
    Posts
    830
    Quote Originally Posted by jimblumberg View Post
    From some documentation for (...)
    3) Then, non-static data members are initialized in order of declaration in the class definition.
    Thanks for the information. Since I grew up with initializer-lists, I was under the false impression that the body of a constructor would be executed after all members were initialized. Of course looking at how initialization was done before, that idea makes no sense.

  6. #6
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    Since you use a magic number in the declaration of your buffer the buffer should be initialized before you try to use the sizeof operator.
    O_o

    The buffer is initialized before `sizeof` is used.

    4) Finally, the body of the constructor is executed
    The `ifs.rdbuf()->pubsetbuf(readBuffer, sizeof(readBuffer))` statement within the constructor can't be executed before the array is initialized.

    *shrug*

    Of course, the initialization doesn't have any effect on the `sizeof` operator in any event.

    The `sizeof` calculation doesn't happen during program execution but during compilation.

    I also can't figure out where the "magic number" aspect becomes relevant.

    [Edit]
    Of course looking at how initialization was done before, that idea makes no sense.
    What?
    [/Edit]

    Soma

    Code:
    #include <fstream>
    #include <iostream>
    
    class Reader
    {
    public:
        Reader();
    //private:
        std::ifstream ifs;
        char readBuffer[64 * 1024];
    };
    
    const Reader * check(); // no implementation
    
    int main()
    {
        //Reader r;
        std::cout << sizeof(check()->readBuffer) << '\n';
    }
    
    Reader::Reader()
    {
        ifs.rdbuf()->pubsetbuf(readBuffer, sizeof(readBuffer));
    }
    [Edit]
    Code:
    #include <fstream>
    #include <iostream>
    
    struct DONTREALLYUSE
    {
        DONTREALLYUSE()
        {
            std::cout << "!\n";
        }
    };
    
    class Reader
    {
    public:
        Reader();
    //private:
        std::ifstream ifs;
        //char readBuffer[64 * 1024];
        DONTREALLYUSE readBuffer[8];
    };
    
    const Reader * check(); // no implementation
    
    int main()
    {
        Reader r;
        //std::cout << sizeof(check()->readBuffer) << '\n';
    }
    
    Reader::Reader()
    {
        //ifs.rdbuf()->pubsetbuf(readBuffer, sizeof(readBuffer));
    }
    [/Edit]
    Last edited by phantomotap; 09-08-2015 at 07:21 AM.
    “Salem Was Wrong!” -- Pedant Necromancer
    “Four isn't random!” -- Gibbering Mouther

  7. #7
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    So what's going on here?

    Adrian - what platform/toolchain/compiler options?

    gg

  8. #8
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    So what's going on here?
    O_o

    I think we are probably missing the actual problematic code.

    Code:
    // You shouldn't use such code. The example is intentionally broken.
    {
        filebuf * sBuffer;
        {
            Reader s;
            sBuffer = s.ifs.rdbuf();
        }
        sBuffer->pubsetbuf(readBuffer, sizeof(readBuffer));
    }
    My guess was/is that the internal `filebuf` is somehow being misused after the `Reader` instance has died.

    Soma
    “Salem Was Wrong!” -- Pedant Necromancer
    “Four isn't random!” -- Gibbering Mouther

  9. #9
    Informer -Adrian's Avatar
    Join Date
    Jan 2013
    Posts
    830
    Quote Originally Posted by phantomotap View Post
    Of course, the initialization doesn't have any effect on the `sizeof` operator in any event.

    The `sizeof` calculation doesn't happen during program execution but during compilation.

    I also can't figure out where the "magic number" aspect becomes relevant.
    The first thing I tried was using the same numeric literal instead of sizeof, but it crashed all the same. I should have mentioned that.

    Quote Originally Posted by phantomotap View Post
    What?
    Disregard that, for some reason I thought initialization (via assignment) was done exclusively in the body in older C++ standards.

    Quote Originally Posted by Codeplug View Post
    Adrian - what platform/toolchain/compiler options
    GCC 5.1.1 on Linux. No specific flags. I think it might be the compiler.

    I can make IDEOne crash, but an older version of GCC it offers doesn't.

  10. #10
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    I wonder if a pubsetbuf(null, 0) in the destructor is enough to bypass the issue.

    gg

  11. #11
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    GCC 5.1.1 on Linux. No specific flags. I think it might be the compiler.
    O_o

    Unlikely.

    I recall some ABI issues, but I think you'd be seeing a lot more problems.

    I can make IDEOne crash, but an older version of GCC it offers doesn't.
    I can only tell you that the code should be fine with most shipping "libstdc++" versions.

    [Edit]
    I wanted to elaborate on the standard library implementation requirements. The call `->pubsetbuf(0,0)` is the only arguments with semantics required by the standard. You'll find that the behavior with any other arguments is implementation defined.

    I also need to retract my statement. I think a simple buffer should be fine, but I haven't actually used the interface myself in ages so I honestly have no idea if "should" is anywhere near being correct.
    [/Edit]

    I wonder if a pubsetbuf(null, 0) in the destructor is enough to bypass the issue.
    I know that some historical implementations took ownership of the buffer which could cause `delete` being used twice or, in the case here, `delete` being used with memory not allocated with the `new` operator. I don't think "libstdc++" ever used `delete` or anything.

    Soma
    Last edited by phantomotap; 09-08-2015 at 09:17 AM.
    “Salem Was Wrong!” -- Pedant Necromancer
    “Four isn't random!” -- Gibbering Mouther

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Runtime crash on ifstream destructor.
    By Adamkromm in forum C++ Programming
    Replies: 4
    Last Post: 01-24-2013, 08:23 AM
  2. Class for both dynamic allocated buffer and static buffer
    By TotalTurd in forum C++ Programming
    Replies: 3
    Last Post: 01-07-2012, 09:09 PM
  3. Lame null append cause buffer to crash
    By cmoo in forum C Programming
    Replies: 8
    Last Post: 12-29-2008, 03:27 AM
  4. fgets(buffer,sizeof(buffer),stdin);
    By linuxdude in forum C Programming
    Replies: 2
    Last Post: 10-28-2003, 10:41 AM
  5. ifstream
    By Klinerr1 in forum C++ Programming
    Replies: 2
    Last Post: 07-22-2002, 11:18 PM