memcpyでアドレスオーバーラップさせると・・・

注意: この記事は1年以上前に掲載されたものです。情報が古い場合がありますのでお気を付け下さい。

memcpyで配列のアドレスをオーバーラップした場合、意図せぬ動作を引き起こすことがある。それは非常に厄介な問題を孕んでおり、通常では見つけることも困難になることも予想される。

memcpyでアドレスオーバーラップを起こしているコードの例

例えば、以下のようなコードがある。

この場合、「1234123456」と出力されることを期待するはずが、実際に出力されるのは「1234123412」となってしまうこともある。これは、memcpyではアドレスがオーバーラップした場合の動作が確定していないからである。

AddressSanitizerで検知して、強制終了できるようにする

なお、clangではAddressSanitizerというツールを使うことで、上記のようなコードが実行された時にエラーを発生させることができる。

例えば、上記のコードをfoo.cで保存したことを仮定して、「-fsanitize=address -fno-omit-frame-pointer」を加えた上でコンパイルして実行した場合、エラーメッセージを吐いて強制終了するようになる。

以下はその例である。

これによって、memcpyのアドレスオーバーラップが発生していることがわかる。

この場合は、memmoveを使う

上記のことがわかったことで、代替措置はないのか?と思う人はいるだろう。当然のことながら、代替措置はある。それが、memcpyの代わりに、memmoveを使うことである。memmoveではmemcpyとは違い、オーバーラップしたとしても(別の問題を起こしていない限りは)正常に動作するようになっている。

例えば、先ほどのコードをmemmoveに置き換えたコードを以下に記載する。

上記のコードを先ほどと同じようにコンパイルして実行してみる。

この場合は、AddressSanitizerを有効にしても正常に動作した。

これは、memcpyとは違い、memmoveではアドレスがオーバーラップしたとしても正常に動作するようになっているからである。アドレスがオーバーラップする処理を行う場合は、memcpyではなく、memmoveを使うのが正しいということになる。

最後に

今回はmemcpyのアドレスオーバーラップを例に挙げたが、C言語ではこう言ったダークサイドが多数あるため、意図した動作になるのか、その命令を使うのが適切かどうかなどちゃんと確認した上で使っていきたい。

なお、今回はmemcpyのアドレスオーバーラップ問題対策としてAddressSanitizerを使ったが、AddressSanitizerはそれ以外にもメモリーの使い方でバッファーオーバーランを起こしていないかどうかなどの確認に使えるので、必要に応じて使っていきたいところである。特に再現性の低いバグの対処には有効に働くだろう。

ウェブマスター。本ブログでITを中心にいろいろな情報や意見などを提供しています。ご用の方はコメントかコンタクトフォームにて。

スポンサーリンク

フォローする