Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C++11: segfault in debug mode for arrays #1105

Open
Mingun opened this issue Apr 9, 2024 · 1 comment
Open

C++11: segfault in debug mode for arrays #1105

Mingun opened this issue Apr 9, 2024 · 1 comment

Comments

@Mingun
Copy link

Mingun commented Apr 9, 2024

Currently C++ generator when uses smart pointers generates incorrect code that uses unique_ptr after it was moved into the vector:

void debug_array_user_t::_read() {
    m_one_cat = std::unique_ptr<cat_t>(new cat_t(m__io, this, m__root));
    m_one_cat->_read();
    m_array_of_cats = std::unique_ptr<std::vector<std::unique_ptr<cat_t>>>(new std::vector<std::unique_ptr<cat_t>>());
    const int l_array_of_cats = 3;
    for (int i = 0; i < l_array_of_cats; i++) {
        std::unique_ptr<cat_t> _t_array_of_cats = std::unique_ptr<cat_t>(new cat_t(m__io, this, m__root));
        m_array_of_cats->push_back(std::move(_t_array_of_cats));
        _t_array_of_cats->_read(); // <<<<<<< BIG PROBLEM ^^^ object is moved out
    }
}

That is the reason why DebugArrayUser test on https://ci.kaitai.io/ is segfaulted on C++11 with message memory access violation at address: 0x8: no mapping at fault address.

This is the common problem: in debug mode reading is performed after placing new object into container, but in no debug mode it is readed in constructor, i. e. before placing into container. That is very unpleasant situation, because debug mode ideally should not change behavior.

@Mingun
Copy link
Author

Mingun commented Apr 9, 2024

I think, the best solution is to move read just after creating object, but then this can create a memory leak in C++98 target if _read() of an array item will throw. Currently such object will be deleted in _clean_up() method:

// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

#include "debug_array_user.h"

debug_array_user_t::debug_array_user_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, debug_array_user_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root ? p__root : this;
    m_one_cat = 0;
    m_array_of_cats = 0;
}

void debug_array_user_t::_read() {
    m_one_cat = new cat_t(m__io, this, m__root);
    m_one_cat->_read();
    m_array_of_cats = new std::vector<cat_t*>();
    const int l_array_of_cats = 3;
    for (int i = 0; i < l_array_of_cats; i++) {
        cat_t* _t_array_of_cats = new cat_t(m__io, this, m__root);
        m_array_of_cats->push_back(_t_array_of_cats);
        _t_array_of_cats->_read();
    }
}

debug_array_user_t::~debug_array_user_t() {
    _clean_up();
}

void debug_array_user_t::_clean_up() {
    if (m_one_cat) {
        delete m_one_cat; m_one_cat = 0;
    }
    if (m_array_of_cats) {
        for (std::vector<cat_t*>::iterator it = m_array_of_cats->begin(); it != m_array_of_cats->end(); ++it) {
            delete *it;
        }
        delete m_array_of_cats; m_array_of_cats = 0;
    }
}

debug_array_user_t::cat_t::cat_t(kaitai::kstream* p__io, debug_array_user_t* p__parent, debug_array_user_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
}

void debug_array_user_t::cat_t::_read() {
    m_meow = m__io->read_u1();
}

debug_array_user_t::cat_t::~cat_t() {
    _clean_up();
}

void debug_array_user_t::cat_t::_clean_up() {
}

Because in C++98 target we do not want to use any smart pointers because they can absence, we can, however, create a custom, very limited smart pointer type, which will only exist to delete memory allocated for object if its _read() method throw. Then the debug_array_user_t::_read() would be implemented as:

void debug_array_user_t::_read() {
    m_one_cat = new cat_t(m__io, this, m__root);
    m_one_cat->_read();
    m_array_of_cats = new std::vector<cat_t*>();
    const int l_array_of_cats = 3;
    for (int i = 0; i < l_array_of_cats; i++) {
        safe_read_helper<cat_t> _t_array_of_cats_helper = safe_read_helper<cat_t>(new cat_t(m__io, this, m__root));
        cat_t* _t_array_of_cats = _t_array_of_cats_helper.read();
        m_array_of_cats->push_back(_t_array_of_cats);
    }
}

The helper is very simple and limited:

/// Will work only with concrete types
/// Live only on stack. Destroyed just after calling `read()`
template<class T>
class safe_read_helper {
  T* m_ptr;
public:
  safe_read_helper(T* ptr) : m_ptr(ptr) {}
  ~safe_read_helper() { delete this->m_ptr; }

  T* read() {
    T* ptr = this->m_ptr;
    ptr->_read();
    this->m_ptr = 0;
    return ptr;
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant