-3

Consider following code:

cv::Mat currentFrame; // some proper frame with allocated memory

std::vector<uint8_t> storage;

{
    cv::Mat m1 = currentFrame.clone();
    cv::Mat m2 = m1;
    const auto *m1ptr = reinterpret_cast<const uint8_t *>(&m1);
    storage.insert(storage.end(), m1ptr, m1ptr + sizeof(cv::Mat));
    m1.addref();
}

{
    cv::Mat m3;
    uint8_t *dstPtr = reinterpret_cast<uint8_t *>(&m3);
    std::copy_n(storage.begin(), sizeof(cv::Mat), dstPtr);
}

In the first scope I am working with a cv::Mat image, doing some copying to increase the refcount, and finally bitwise-serializing just the cv::Mat header (96 bytes) into the vector storage. Notice m1.addref(), where I increase the refcount by 1, so to avoid memory deallocation, when m1 and m2 get deleted, and refcount would drop to 0. Between the scopes, there is no actual cv::Mat pointing to allocated place in the memory, but the copy of such cv::Mat exists in the storage.

Then I try to restore it, by deserializing it from the storage. m3 is now a copy of already gone m1, it points to still allocated memory block, and refcount=1. So I would expect everything to work. But when we reach the end of the scope and destructor of m3 is called, I get invalid pointer exception. What is wrong with this design?

Evg
  • 25,259
  • 5
  • 41
  • 83
Piotr G
  • 959
  • 1
  • 7
  • 25
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/251419/discussion-on-question-by-piotr-g-can-cvmat-be-safely-recreated-from-its-bitwi). – Cody Gray - on strike Jan 26 '23 at 23:15

1 Answers1

0

C++ objects are not just collections of bytes. You can't bitwise copy an arbitrary object into another object and pretend that you've recreated the original one. You can do it only for trivial types, and cv::Mat is not such a type. All tricks related to keeping matrix elements buffer from deallocation are irrelevant, because cv::Mat could have and actually does have other implementation details that prevent correct bitwise move.

Here is a simple example of a type that follows those details of cv::Mat:

struct S {
    int* a;    // = cv::Mat::step.p
    int  b;    // = cv::Mat::step.buf
 
    // other irrelevant data members including matrix elements buffer
 
    S() {
        a = &b;
    }
 
    ~S() {
        if (a != &b)
            delete a;
    }
};

std::vector<char> storage;
{
    S s1;
    auto ptr = reinterpret_cast<char*>(&s1);
    storage.insert(storage.end(), ptr, ptr + sizeof(S));
}
{
    S s2;
    char* ptr = reinterpret_cast<char*>(&s2);
    std::copy_n(storage.begin(), sizeof(S), ptr);
}

It should be pretty obvious what's wrong with bitwise overwriting s2 with bitwise-serialized s1: you set s2.a to an address of s1.b. If you're lucky to have s1 and s2 at the same address, nothing bad will happen. If they are at different addresses, you'll delete a variable on stack. That's how UB looks: sometimes your code appears to work, sometimes it crashes.

Here is a complete demo you can play with: https://godbolt.org/z/5ezcaWv7x

Evg
  • 25,259
  • 5
  • 41
  • 83