0

Reposting with full code, as suggested from others. Just updated the main function with hard coded arguments that causes segmentation fault.

Changing the width and height to something else lets the program run fine, for example 500 and 433 respectively.

Main file:

#include <cstdint>
#include <iostream>
#include <sstream>
#include <vector>
#include <cmath>
#include "sierpinski.h"

Triangle::Triangle() {
  this->pixels = nullptr;
  this->top = Coordinate();
  this->left = Coordinate();
  this->right = Coordinate();
  this->fg_color = 0;
  this->area = 0.0;
}

Triangle::Triangle(uint32_t* pixels, Coordinate top, Coordinate left, Coordinate right, uint32_t fg_color) {
  this->pixels = pixels;
  this->top = top;
  this->left = left;
  this->right = right;
  this->fg_color = fg_color;
  this->area = calculateArea(top, left, right);
}

void Triangle::DrawLines(Coordinate top, Coordinate left, Coordinate right) {
  int width = getWidth(this->left, this->right);
  //left to top
  std::vector<Coordinate> line = left.Bresenham(top);
  for (unsigned int i = 0; i < line.size(); i++) {
    this->pixels[width * line[i].getY() + line[i].getX()] = this->fg_color;
  }
  line.clear();
  // left to right
  line = left.Bresenham(right);
  for (unsigned int i = 0; i < line.size(); i++) {
    this->pixels[width * line[i].getY() + line[i].getX()] = this->fg_color;
  }
  line.clear();
  // top to right
  line = top.Bresenham(right);
  for (unsigned int i = 0; i < line.size(); i++) {
    this->pixels[width * line[i].getY() + line[i].getX()] = this->fg_color;
  }
}

void Triangle::Sierpinski(Coordinate top, Coordinate left, Coordinate right, const double min_area) {
  if (calculateArea(top, left, right) < min_area) {
    return;
  }
  DrawLines(top, left, right);

  Triangle triangle(this->pixels, top, left, right, this->fg_color);

  Sierpinski(top, top.Midpoint(left), top.Midpoint(right), min_area);
  Sierpinski(top.Midpoint(left), left, left.Midpoint(right), min_area);
  Sierpinski(top.Midpoint(right), left.Midpoint(right), right, min_area);
}

uint32_t* Triangle::getPixels() {
  return this->pixels;
}

uint32_t Triangle::getFg_color() {
  return this->fg_color;
}

double Triangle::getArea() {
  return this->area;
}

double Triangle::getWidth(Coordinate left, Coordinate right) {
  return left.distance(right);
}

double Triangle::calculateArea(Coordinate top, Coordinate left, Coordinate right) {
  return std::abs(top.getX() * (left.getY() - right.getY()) + left.getX() * (right.getY() - top.getY()) + right.getX() * (top.getY() - left.getY())) / 2.0;
}

Coordinate::Coordinate() {
  this->x = 0;
  this->y = 0;
}

Coordinate::Coordinate(int x, int y) {
  this->x = x;
  this->y = y;
}

Coordinate::Coordinate(const Coordinate &other) {
  this->x = other.x;
  this->y = other.y;
}

int Coordinate::getX() {
  return this->x;
}

int Coordinate::getY() {
  return this->y;
}

double Coordinate::distance(Coordinate other) {
  return std::sqrt(std::pow((this->x - other.x), 2) + std::pow((this->y - other.y), 2));
}

Coordinate Coordinate::Midpoint(Coordinate other) {
  double midX = (this->x + other.getX()) / 2;
  double midY = (this->y + other.getY()) / 2;
  return Coordinate(midX, midY);
}

int Coordinate::gcd(int a, int b) {
  if (b == 0)
    return a;
  return gcd(b, a%b);
}

std::vector<Coordinate> Coordinate::Bresenham(Coordinate other) {
  int x1 = this->x, y1 = this->y;
  int const x2 = other.x, y2 = other.y;
  std::vector<Coordinate> points;

  int delta_x(x2 - x1);
  signed char const ix((delta_x > 0) - (delta_x < 0));
  delta_x = std::abs(delta_x) << 1;

  int delta_y(y2 - y1);
  signed char const iy((delta_y > 0) - (delta_y < 0));
  delta_y = std::abs(delta_y) << 1;

  Coordinate newPoint1(x1, y1);
  points.push_back(newPoint1);

  if (delta_x >= delta_y) {
    int error(delta_y - (delta_x >> 1));
      while (x1 != x2) {
        if ((error > 0) || (!error && (ix > 0))) {
          error -= delta_x;
          y1 += iy;
        }
        error += delta_y;
        x1 += ix;

        Coordinate newPoint2(x1, y1);
        points.push_back(newPoint2);
     }
  } else {
      int error(delta_x - (delta_y >> 1));

      while (y1 != y2) {
        if ((error > 0) || (!error && (iy > 0))) {
          error -= delta_y;
          x1 += ix;
        }
        error += delta_x;
        y1 += iy;

        Coordinate newPoint3(x1, y1);
        points.push_back(newPoint3);
      }
  }
  return points;
}

int main() {
  int width = 55;
  int height = 40;
  uint32_t bg_color = 4095617261;
  uint32_t fg_color = 2988200782;
  double min_area = 1.08;

  std::vector<uint32_t> pixels(width * height, bg_color);
  Coordinate top, left, right;
  top = Coordinate(width / 2, 0);
  left = Coordinate(0, height);
  right = Coordinate(width, height);

  Triangle triangle = Triangle(pixels.data(), top, left, right, fg_color);
  triangle.Sierpinski(top, left, right, min_area);
  std::cout.write(reinterpret_cast<char *>(pixels.data()), width * height * 4);
}

Header file:

#ifndef SIERPINSKI_H
#define SIERPINSKI_H
#include <vector>

class Coordinate {
 public:
    Coordinate();
    Coordinate(int x, int y);
    Coordinate(const Coordinate &other);
    int getX();
    int getY();
    double distance(Coordinate);
    Coordinate Midpoint(Coordinate other);
    int gcd(int a, int b);
    std::vector<Coordinate> Bresenham(Coordinate other);
    int countPoints(Coordinate other);
    std::vector<Coordinate> getPoints(Coordinate other);
 private:
    int x;
    int y;
};

class Triangle {
 public:
    Triangle();
    Triangle(uint32_t* pixels, Coordinate top, Coordinate left, Coordinate right, uint32_t fg_color);
    void DrawLines(Coordinate top, Coordinate left, Coordinate right);
    void Draw(double min_area);
    void Sierpinski(Coordinate top, Coordinate left, Coordinate right, double min_area);
    double calculateArea(Coordinate top, Coordinate left, Coordinate right);
    uint32_t* getPixels();
    uint32_t getFg_color();
    double getArea();
    Coordinate top, left, right;
    double getWidth(Coordinate left, Coordinate right);
 private:
    uint32_t* pixels;
    uint32_t fg_color;
    double area;
};

#endif

After using gdb, it seemed the error was occuring in my Bresenham function at the line:

points.push_back(newPoint3);

But other users stated that seg fault may not be occurring exactly where gdb says it is. If anyone can provide further insight it would be greatly appreciated.

心悦陈
  • 1
  • 2
  • Recommendation: replace the command line arguments with values that you know will expose the bug. It's really annoying when someone answers the question with a solution based on a different bug found with different inputs. – user4581301 Nov 16 '19 at 03:14
  • Sidenote: sierpinski.h uses `uint32_t` but does not include ``. It's not an error at the moment because of the include order of sierpinski.h in main.cpp, but this could come back to bite you later. – user4581301 Nov 16 '19 at 03:17
  • Thanks for the sidenote, also I included a case in my original post that exposes the bug right? I'm confused about your first comment. – 心悦陈 Nov 16 '19 at 03:18
  • I see the inputs, but why have inputs at all? Hard code them into the program so that there is no room for error. – user4581301 Nov 16 '19 at 03:18
  • Oh I got it, thanks for the tip. I didn't think of doing that, that would make it easier for people. – 心悦陈 Nov 16 '19 at 03:19
  • 2
    Also unrelated: There is no need for a copy constructor in `Coordinate`. There's noting in it that the default copy constructor can't handle. See the [Rule of Zero](https://en.cppreference.com/w/cpp/language/rule_of_three). Shouldn't change a thing WRT the bug. – user4581301 Nov 16 '19 at 03:26

1 Answers1

3

The crash happens here:

#0  0x00007ffff7b12c46 in malloc () from /lib64/libc.so.6
#1  0x00007ffff7e5e059 in operator new(unsigned long) () from /lib64/libstdc++.so.6
#2  0x00000000004035ac in __gnu_cxx::new_allocator<Coordinate>::allocate (this=0x7fffffffd3f0, __n=2) at /usr/include/c++/9/ext/new_allocator.h:114
#3  0x000000000040335b in std::allocator_traits<std::allocator<Coordinate> >::allocate (__a=..., __n=2) at /usr/include/c++/9/bits/alloc_traits.h:444
#4  0x0000000000402fe2 in std::_Vector_base<Coordinate, std::allocator<Coordinate> >::_M_allocate (this=0x7fffffffd3f0, __n=2) at /usr/include/c++/9/bits/stl_vector.h:343
#5  0x00000000004028be in std::vector<Coordinate, std::allocator<Coordinate> >::_M_realloc_insert<Coordinate const&> (this=0x7fffffffd3f0, __position={x = 0, y = 0}, __args#0=...) at /usr/include/c++/9/bits/vector.tcc:440
#6  0x00000000004024a0 in std::vector<Coordinate, std::allocator<Coordinate> >::push_back (this=0x7fffffffd3f0, __x=...) at /usr/include/c++/9/bits/stl_vector.h:1195
#7  0x0000000000401f48 in Coordinate::Bresenham (this=0x7fffffffd4c8, other=...) at t.cc:205
#8  0x0000000000401610 in Triangle::DrawLines (this=0x7fffffffd580, top=..., left=..., right=...) at t.cc:86
#9  0x0000000000401823 in Triangle::Sierpinski (this=0x7fffffffd580, top=..., left=..., right=..., min_area=1.0800000000000001) at t.cc:96
#10 0x0000000000402154 in main () at t.cc:225

Any time you have a crash in malloc, you should immediately suspect heap corruption.

Here is what address sanitizer (just add -fsanitize=address to your compile and link command lines) says:

=================================================================
==5470==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x625000002360 at pc 0x000000401e84 bp 0x7fffffffcdb0 sp 0x7fffffffcda0
WRITE of size 4 at 0x625000002360 thread T0
    #0 0x401e83 in Triangle::DrawLines(Coordinate, Coordinate, Coordinate) /tmp/t.cc:76
    #1 0x4028c5 in Triangle::Sierpinski(Coordinate, Coordinate, Coordinate, double) /tmp/t.cc:96
    #2 0x4043df in main /tmp/t.cc:225
    #3 0x7ffff707ef32 in __libc_start_main (/lib64/libc.so.6+0x23f32)
    #4 0x40126d in _start (/tmp/a.out+0x40126d)

0x625000002360 is located 0 bytes to the right of 8800-byte region [0x625000000100,0x625000002360)
allocated by thread T0 here:
    #0 0x7ffff768a9d7 in operator new(unsigned long) (/lib64/libasan.so.5+0x10f9d7)
    #1 0x406f82 in __gnu_cxx::new_allocator<unsigned int>::allocate(unsigned long, void const*) /usr/include/c++/9/ext/new_allocator.h:114
    #2 0x406e67 in std::allocator_traits<std::allocator<unsigned int> >::allocate(std::allocator<unsigned int>&, unsigned long) /usr/include/c++/9/bits/alloc_traits.h:444
    #3 0x406b8f in std::_Vector_base<unsigned int, std::allocator<unsigned int> >::_M_allocate(unsigned long) /usr/include/c++/9/bits/stl_vector.h:343
    #4 0x40663e in std::_Vector_base<unsigned int, std::allocator<unsigned int> >::_M_create_storage(unsigned long) /usr/include/c++/9/bits/stl_vector.h:358
    #5 0x405a5c in std::_Vector_base<unsigned int, std::allocator<unsigned int> >::_Vector_base(unsigned long, std::allocator<unsigned int> const&) /usr/include/c++/9/bits/stl_vector.h:302
    #6 0x404bf0 in std::vector<unsigned int, std::allocator<unsigned int> >::vector(unsigned long, unsigned int const&, std::allocator<unsigned int> const&) /usr/include/c++/9/bits/stl_vector.h:521
    #7 0x403faa in main /tmp/t.cc:218
    #8 0x7ffff707ef32 in __libc_start_main (/lib64/libc.so.6+0x23f32)

SUMMARY: AddressSanitizer: heap-buffer-overflow /tmp/t.cc:76 in Triangle::DrawLines(Coordinate, Coordinate, Coordinate)

So indeed you have heap corruption (overflow of allocated heap buffer).

Hopefully this is enough info for you to find a logic bug in your program.

P.S.

It looks like you are allocating a rectangle of height * width pixels, then try to draw a line from the height * widths pixel. You likely have an off-by-one bug -- valid pixel indices are in the range [0 .. width-1] * [0 .. height-1].

When I reserve (width+1) * (height+1) space in the vector, the problem goes away.

Employed Russian
  • 199,314
  • 34
  • 295
  • 362
  • Asan is a good choice. To enable it ensure `CXXFLAGS` includes `-fsanitize=address`. Also see [How to use AddressSanitizer in GCC?](https://stackoverflow.com/q/37970758/608639) – jww Nov 16 '19 at 04:13