Ошибка очистки памяти: clang5 + msan + fwrite структур с заполненными байтами

Минимальный пример:

#include <fstream>

struct TFoo
{
bool Field1_ = false;
uint64_t Field2_ = 0;
};

int main() {
TFoo Foo_{};
const char* filename = "text.txt";
std::ofstream f(filename);

f.write((char*)(&Foo_), sizeof(Foo_));
}

clang с 5-ой версии в msan сообщает что-то вроде этого:

Uninitialized bytes in __interceptor_fwrite at offset 0 inside [0x720000000000, 15)
==71928==WARNING: MemorySanitizer: use-of-uninitialized-value
#0 0x2823aa  (/home/<hidden>/test-ofstream+0x2823aa)
#1 0x27830f  (/home/<hidden>/test-ofstream+0x27830f)
#2 0x272757  (/home/<hidden>/test-ofstream+0x272757)
#3 0x271388  (/home/<hidden>/test-ofstream+0x271388)
#4 0x270c96  (/home/<hidden>/test-ofstream+0x270c96)
#5 0x2709e2  (/home/<hidden>/test-ofstream+0x2709e2)
#6 0x26ff9e  (/home/<hidden>/test-ofstream+0x26ff9e)
#7 0x7fbc7238382f  (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)

Это происходит из-за заполнения байтов между Field1_ а также Field2_ не инициализируются.

Это нормально, MSAN прав.

Но если код содержит очень большие примеры такого кода (сохранение структур в двоичные файлы), есть ли какой-нибудь красивый способ массового улучшения кода?

(Упакованные структуры не являются решением для нас.)

2

Решение

Если вы можете изменить определение struct TFooВы можете добавить конструктор следующим образом:

struct TFoo {
bool Field1_;
uint64_t Fields_;
TFoo() { memset(this, 0, sizeof(*this)); }
TFoo(const TFoo &rhs) : TFoo() { Field1_ = rhs.Field1_; Field2_ = rhs.Field2_; }
};

Я думаю, что вы не можете использовать memset таким образом, в соответствии со стандартом, но он может работать с вашим компилятором. Увидеть Как я могу обнулить только байты заполнения класса?, где это решение обсуждается.

ОРИГИНАЛЬНЫЙ ОТВЕТ

Это пришло мне в голову, чтобы обнулить дополненные байты между Field1_ а также Field2_, Но, честно говоря, я не уверен, что он соответствует стандарту. Определенно, какая-то сериализация TFoo объекты были бы намного лучше, но если я правильно понял, это не вариант для вас, не так ли?

struct TFoo
{
bool Field1_ = false;
uint64_t Field2_ = 0;
};

struct TFooWrapper {
union {
TFoo tfoo;
char dummy[sizeof(TFoo)] = { 0 };
} u;
};

ОБНОВИТЬ

От http://en.cppreference.com/w/cpp/language/union: Не более одного варианта элемента может иметь инициализатор элемента по умолчанию. Поэтому приведенный выше код, скорее всего, неверен. Но вы можете, например, определить конструктор по умолчанию для TFooWrapper обнулить все байты.

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]