0

Update: After compiling with -g and checking each line that is problematic, it now has managed to segfault in a system-level header file:

Program received signal SIGSEGV, Segmentation fault.
__gnu_cxx::new_allocator<char*>::construct<char*, char* const&> (this=0x7fffffffdec0, 
__p=0x5555557702f0, __args#0=<error reading variable>)
at /usr/include/c++/8.1.1/ext/new_allocator.h:136
136             { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }

This is something I simply have not seen before.

So I've got a very simple C++ program that at least attempts to write to and read from a file at the same time in order to make sure that there are absolutely no repeating values in a DAG whatsoever (variable names changed to protect personal trade secrets):

#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <vector>

struct parsedline {
    unsigned long long pre_edge;
    unsigned long long post_edge;
};

std::vector<parsedline> readfile(std::string name) {
    //create array of parsedlines for later use
    std::vector<parsedline> edges;

    //count lines and create array of lines
    int linecount = 0;
    std::vector<char*> lines;
    std::ifstream dag(name);
    std::string ul;

    while (std::getline(dag, ul)) {

        //convert to c_str immediately to avoid errors with atoi
        std::istringstream thisline;
        thisline.str(ul);
        lines[linecount] = const_cast<char*>(thisline.str().c_str());
        ++linecount;

    }

    //drop block definition in graphviz file representing DAG
    int max_readable_line = sizeof(lines) - 2;
    std::vector<char*> readable_lines;

    for (int i = 0; i < max_readable_line; i++) {

        //shift index to skip line 1
        int rlindex = i+1;
        readable_lines[i] = lines[rlindex];

    }

    //read integers from each line, store them in array of parsedline objects
    for (int j = 0; j < max_readable_line; j++) {

        int edge_begin, edge_end;
        if (std::sscanf(readable_lines[j], "   %d -> %d", &edge_begin, &edge_end)) {
            edges[j].pre_edge = edge_begin;
            edges[j].post_edge = edge_end;
        }

    }
    return edges;
}

int main (int argc, char **argv) {

    unsigned long long minperrank = atoi(argv[1]);
    unsigned long long maxperrank = atoi(argv[2]);
    unsigned long long minranks = atoi(argv[3]);
    unsigned long long maxranks = atoi(argv[4]);
    double edgechance = atof(argv[5]);
    std::string filedest(argv[6]);

    unsigned long long i,j,k,l,nodes = 0;
    std::srand(time(NULL));

    unsigned long long ranks = minranks + (std::rand() % (maxranks - minranks + 1));

    std::ofstream dagout(filedest, std::ofstream::out);
    std::vector<parsedline> lines = readfile(filedest);

    dagout << "digraph {" << std::endl;

    for(i = 0; i < ranks; i++) {
        unsigned long long newnodes = minperrank + (rand() % (maxperrank - minperrank + 1));

        for (j = 0; j < nodes; j++) {

            for (k = 0; k < newnodes; k++) {

                unsigned long long checkval = std::rand() % 100;

                if (checkval < edgechance) {

                    for (l = 0; l < sizeof(lines); l++) {

                        //each new member must be a nonce
                        unsigned long long pree = lines[l].pre_edge;
                        unsigned long long poste = lines[l].post_edge;
                        if (checkval != pree) {

                            if (checkval != poste) {

                                dagout << "   " << j << " -> " << k+nodes << ";" << std::endl;

                            }

                        }

                    }

                }

            }

        }

        nodes += newnodes;

    }

    dagout << "}" << std::endl;
    return 0;
}

This compiles just fine, but when I attempt to run it:

[realkstrawn93@archlinux Desktop]$ ./gendag 10 10 10 10 0.1 test.gv
Segmentation fault (core dumped)
[realkstrawn93@archlinux Desktop]$ 

Here's the gdb output ― it says it's in the readfile function, but doesn't specify exactly where in that function it is:

[realkstrawn93@archlinux Desktop]$ gdb gendag
GNU gdb (GDB) 8.1
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later 
<http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from gendag...(no debugging symbols found)...done.
(gdb) r 10 10 10 10 0.1 test.gv
Starting program: /home/realkstrawn93/Desktop/gendag 10 10 10 10 0.1 test.gv

Program received signal SIGSEGV, Segmentation fault.
0x0000555555555675 in readfile(std::__cxx11::basic_string<char, 
std::char_traits<char>, std::allocator<char> >) ()

I need to know where exactly in that function the segfault is.

realkstrawn93
  • 722
  • 1
  • 6
  • 13

2 Answers2

1
//count lines and create array of lines
int linecount = 0;
std::vector<char*> lines;  <--
std::ifstream dag(name);
std::string ul;

while (std::getline(dag, ul)) {

    //convert to c_str immediately to avoid errors with atoi
    std::istringstream thisline;
    thisline.str(ul);
    lines[linecount] = const_cast<char*>(thisline.str().c_str()); <--
    ++linecount;

}

You are accessing lines[linecount] before initializing it. Although the vector is initialized to be empty, its elements aren't initialized. Use

lines.push_back(const_cast<char*>(thisline.str().c_str()));

instead of the accessor operator ([]) if all you need is to append an element to the end of the vector.

Otherwise, if you must access the elements by their position, initialize first by passing an argument to the constructor like in

std::vector<char*> lines(max_lines);

which will initialize the vector to contain max_lines empty objects.

A segmentation fault happens when your program accesses memory that does not belong to it. Since vectors are always allocated on the heap, accessing any data beyond it's end will result in a segfault.

RenanB
  • 105
  • 1
  • 9
  • This is correct, but incomplete. Could you assist the asker in how they might properly initialize it, and what the trade-offs are between different ways to approach the initialization and use of the `vector`? – user1118321 Jul 18 '18 at 03:02
  • Found that out after compiling with -g. Trying your suggestion, thank you immensely for it – realkstrawn93 Jul 18 '18 at 03:06
  • Improved my answer with some information on initializing the vector and the reason why a segfault happens when you don't. – RenanB Jul 18 '18 at 03:08
  • Surprisingly the line that gdb said was the problem one was actually line 43 ― the loop through the lines to obtain the integers from them. – realkstrawn93 Jul 18 '18 at 03:10
  • 1
    Reason is the same, you access `readable_lines[i]` without initializing it. As I was following your program I only noticed the first occurrence of this problem. – RenanB Jul 18 '18 at 03:12
  • 1
    Sorry about the delay, had to build your code to verify. `thisline.str().c_str()` is of type `const char*`, while your vector is of type `char*`. This makes it unable to assign because it doesn't find a valid constructor. Just change the vectors to `std::vector lines` and it should work. – RenanB Jul 18 '18 at 03:28
  • 1
    Be aware, however, that `c_str()` is not usable if you ever intend to return the resulting `char*`. The pointer returned by that method is invalidated as soon as the string which created it goes out of scope. – RenanB Jul 18 '18 at 03:30
  • That’s what the “const_cast” is for. Definitely created a compiler error without that. – realkstrawn93 Jul 18 '18 at 03:35
  • 1
    One last thing, your use of `sizeof(lines)` to get the length of the vector is wrong. `sizeof` returns the size in bytes, which is not what you want. What you want is `lines.size()`. – RenanB Jul 18 '18 at 03:38
  • Did what you suggested and it did absolutely nothing re: the header fault. – realkstrawn93 Jul 18 '18 at 03:39
  • lines.size() also didn’t help. – realkstrawn93 Jul 18 '18 at 03:40
  • 1
    Don't forget to change the other instance, at `std::vector readable_lines;` I can build and run the code fine with no errors after those changes with both g++ and msbuild. – RenanB Jul 18 '18 at 03:42
  • Trying valgrind to see if that helps. – realkstrawn93 Jul 18 '18 at 03:43
  • It compiles completely flawlessly without any errors on my end; maybe -Wall -Wextra will help pinpoint some more issues **Update** Nope, unused argc is only problem that pinpoints – realkstrawn93 Jul 18 '18 at 03:46
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/176203/discussion-between-renanb-and-strawn-04). – RenanB Jul 18 '18 at 03:49
0

It turns out that I had to check whether or not the size of the array is 0 before proceeding. Otherwise, it's a null pointer dereference:

if (lines.size() == 0) {

    dagout << "   " << j << " -> " << k+nodes << ";" << std::endl;

} else {

    //each new member must be a nonce
    unsigned long long pree = lines.at(l).pre_edge;
    unsigned long long poste = lines.at(l).post_edge;
    if (std::rand() % 100 != pree) {

        if (std::rand() % 100 != poste) {

            dagout << "   " << j << " -> " << k+nodes << ";" << std::endl;

        }

    }

}

Now, however, I've got a bit of an infinite loop issue ― that's an altogether unrelated problem that I can solve on my own.

realkstrawn93
  • 722
  • 1
  • 6
  • 13