memcpyバッファーUBでreinterpret_castを使用していますか?


8

コードを考える

struct A {};

auto obj = new A;
std::vector<unsigned char> buffer;
buffer.resize(sizeof(obj));
std::memcpy(buffer.data(), &obj, sizeof(obj));  // this copies the pointer, not the object!

// ...

auto ptr = *reinterpret_cast<A**>(buffer.data()); // is this UB?
delete ptr;

reinterpret_castこの場合の使用法はUBですか?memcpyはインスタンスの存続期間を開始しないため、厳密なエイリアシングルールに違反しstd::bit_castているため(そういうわけで、C ++ 20に追加されました)。

そして、キャストを別のキャストに置き換えるとmemcpy(ポインターを読み取るため)、プログラムは明確に定義されますか?


言語弁護士はさておき、それはただの間違いです。それが指し示す内容はbuffer.data()、おそらくへのポインタでありAbuffer.data()それ自体ではなく、へのポインタAです。
StoryTeller-Unslander Monica

3
によって割り当てられたメモリをバッキングストアにアラインメント保証はありstd::vectorますか?(私はその保証がそのアロケーターが保証するものであると思います。)
Eljay

1
厳密なエイリアシングも破ると思います。
一部のプログラマ、

1
@anastaciu Googleでの最初のヒット— stats.meta.stackexchange.com/q/5783/3512
Konrad Rudolph

1
質問をもう一度読んだ後は問題ありませんA*。(1)アライメント要件が壊れている可能性があり、(2)ここにバッファ内にオブジェクトがないため、それは確かにUB です。「戻り値:サイズのストレージの配列の最初の要素へのポインタ標準は、標準アロケータ::機能を割り当てについて述べてn * sizeof(T)適宜整列、型Tのオブジェクトに対して」。
n。「代名詞」m。

回答:


9

はい、このコードには未定義の動作があります。がA*指す場所にタイプのオブジェクトがありませんbuffer.data()。あなたがしたのは、そのようなポインタのオブジェクト表現をあなたのベクトル[basic.types] / 4にコピーすることだけでした。ポインタは簡単にコピーできる[basic.types] / 9なので、これらのバイトを実際の型のオブジェクトにコピーしてA*から、deleteその値をコピーすると、明確に定義された[basic.types] / 3になります。したがって、この

A* ptr;
std::memcpy(&ptr, buffer.data(), sizeof(ptr));
delete ptr;

大丈夫だろう。

元の例では未定義の動作を呼び出すのはキャスト自体ではなく、キャストA*を介して取得したポインターが存在する場所に存在しない型のオブジェクトの値を読み取ろうとするその後の試みであることに注意してください。ポインタが指す場所に存在するのは、タイプのオブジェクトのシーケンスだけですunsigned char。タイプA*は、タイプunsigned char [basic.lval] / 8のオブジェクトの格納された値にアクセスするために使用できるタイプではありません…


2
私はこの答えは正しいと思いますが、最後の文は少し意外です、本質的に質問が本質的に質問に実行する既存のコードのかなりの量があり(事実上すべてのカスタムコンテナー実装、最初は)、現在は本当に良いものはありませんこれを回避する方法もあります。
コンラッドルドルフ

1
A* a_ptr; std::memcpy(&a_ptr, buffer.data(), sizeof(a_ptr));ポインタをバッファから抽出します。reinterpret_castティモの質問ではなく。
エルジェイ

言語弁護士はさておき、なぜこれが実際にUBなのですか?厳密なエイリアシングが思い浮かびますが、それ以外に何かありますか?
divinas

@MichaelKenzelそこにはunsigned char、おそらく複数のタイプのオブジェクトがあります。
n。「代名詞」m。

1
@MichaelKenzelもちろんUBなので、防御はできません。しかし、(現在の)C ++ではオブジェクトの有効期間を開始するのは非常に難しく、大量の(そうでなければ高品質!)コードは適切に実行できません。特にp0593r2§2.3だけでなく、§2.2も参照してください。そして、あなたが圧縮について言及しているので、それは実際に私の会社が行っていることであり、私たちのコードベースは少なくともこれらの問題に注意を払っていないとだけ言いましょう(確かに、その多くは慣用的なCから派生しているためです)。
コンラッドルドルフ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.