teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

1

回答を修正

2020/02/04 13:53

投稿

rubato6809
rubato6809

スコア1382

answer CHANGED
@@ -1,6 +1,18 @@
1
- 経験的に言うと(具体的なアドレスなどはともかく)原因も違いも明確です。例外が発生するかしないかの違いは、コピーのソース側となっている
2
- 配列 char szString2[512]; の配置場所(メモリアドレス)に依ります。もう少し言うと、この512バイトのメモリに続く**残り512バイトのメモリ領域の、どこかがアクセスが許可されていない領域の先頭番地だった**という事です。
1
+ 経験的に言うと(具体的なアドレスなどはともかく)原因も違いも明確です。例外が発生するかしないかの違いは、コピーのソース側となっている配列 char szString2[512]; の配置場所(メモリアドレス)に依ります。もう少し言うと、この512バイトのメモリに続く**残り512バイトのメモリ領域の中に、アクセス許可されている(=保護されていない)メモリと、許可されていない(=保護されている)メモリの境界があった**という事です。境界が残り512バイトのメモリ領域の中に無ければクラッシュしません。
3
2
 
3
+ > クラッシュするケースでは保護されているメモリ空間に不正にアクセスしたから、クラッシュした
4
+
5
+ は正しい理解です。念の為に繰り返しますが、アクセス許可されているメモリとは、保護されていないメモリ、ですよ。
6
+
7
+ 書き込む先のメモリ領域にバッファオーバーランして書き込んだら、他の用途に使っているメモリ(他の変数)を上書きするので、その後、プログラムが異常動作する事は容易に想像できると思います。
8
+ 一方、ご質問の状況は読み出し側のバッファオーバーランです。通常、コピー元のメモリ領域は、読みだす分にはオーバーランアクセスしたって(メモリを書き変えないのだから)問題を起こすことは無いものです。従って、問題なく動作することは少なくありません。というか、読み出しだけならオーバーランアクセスしても何も問題を起こさないでしょう。
9
+
10
+ ところがクラッシュするケースがある、それは例外(**メモリアクセス例外**、OSやCPUによって具体的な呼び名は違うかもしれない)が起こったことを意味します。そうなる理由はただひとつ、残り512バイトのメモリ領域の、ある番地から、アクセスが許可されていない・保護されたメモリだった、ということに尽きます。
11
+
12
+ プログラムが動作する時、動作に必要なメモリを、OSはアクセス可能なメモリ領域として割当てます。現在のCPUは[メモリ管理ユニット](https://ja.wikipedia.org/wiki/%E3%83%A1%E3%83%A2%E3%83%AA%E7%AE%A1%E7%90%86%E3%83%A6%E3%83%8B%E3%83%83%E3%83%88)、略してMMUを内蔵しており、OSはMMUを制御することで、各プロセスがアクセスできるメモリに制限をかけています。アクセス制限されたメモリ領域をアクセスしようとすれば(プログラムの異常動作であると見做して)例外を発生させ、プログラムを停止するようにしています。それがクラッシュするという事です。
13
+
14
+ そのアクセスを許可したメモリ領域の中に szString1[1024] も szString2[512]も配置されますが、szString2[512] に続く、残りの512バイトがアクセス可能なメモリである保証はありません。アクセスできたなら、それは運が良かったわけです。szString2[512]をメモリのどこに配置するか、なぜ境界の近くに配置されたのか、は**コンパイラと、OSの、両方の事情の合わせ技**のようなもので決まります。お使いのOSとコンパイラ次第なので具体的な説明はできかねますが。
15
+
4
16
  memcpy() の中身はこんな感じです(愚直に書いてみた、ツッコミ無用笑)。
5
17
  ```C
6
18
  void *memcpy(void *buf1, const void *buf2, size_t n)
@@ -16,16 +28,10 @@
16
28
  }
17
29
  return buf1;
18
30
  }
19
-
20
31
  ```
21
- 書き込む先のメモリ領域にバッファオーバーランして書き込んだら、他の用途に使ってメモリを上書きするので、その後、プログラム異常動作する事は容易に想像できると思います。
32
+ 「境界」を超えた時点で「temp = *s; // コピー元のメモリを読む」とメモリアクセス命令
22
- 一方、ご質問の状況は読み出し側のバッファオーバーランです。通常、コピー元のメモリ領域は、読みだす分にはオーバーランアクセスしたって(上書きではないのだから)具体的な問題起こすこは無いものです。従って、問題なく動作することは少なくありせん。
33
+ > 「保護されているメモリ読み取り(中略)操作行おうした・・・」
23
34
 
24
- ところがクラッシュするケースがある、それは例外(**メモリアクセス例外**?OSやCPUによって具体的な呼び名は違うかもしれない)が起こったことを意味します。そうなる理由はただひとつ、残り512バイトのメモリ領域の、ある番地から、アクセスが許可されていないメモリだった、ということに尽きます。
25
- メモリアクセス例外は、上に示したコードで「temp = *s; // コピー元メモリを読む」というメモリアクセス命令起きます。
35
+ 即ち不正アクセスになり、例外を起こしたのです。
26
36
 
27
- そのプログラムが動作する時、OSは動作必要なメモリを、クセ可能なメモリ領域とし割当てます。現在CPU[メモリ管理ユニット](https://ja.wikipedia.org/wiki/%E3%83%A1%E3%83%A2%E3%83%AA%E7%AE%A1%E7%90%86%E3%83%A6%E3%83%8B%E3%83%83%E3%83%88)略してMMUを内蔵しており、OSMMU制御すロセスアクセスできるメモリに制限をかけています。この制限れたメモリを超た領域をアクセスすれば(プログラムの異常動作であると見例外を発生させ、プログラムを停止すようにしています。それがクラッシュすいうです。
28
-
29
- そのアクセスを許可したメモリ領域の中に szString1[1024] も szString2[512]も配置されますが、szString2[512] に続く、残りの512バイトもアクセス可能なメモリである保証はありません。アクセスできたなら、それは運が良かったわけです。szString2[512]をメモリのどこに配置するかは、**コンパイラと、OSの、両方の事情の合わせ技**のようなもので決まります。
30
-
31
- こういうことを実感するには、各変数、配列などの**メモリアドレスを確認する**などして、具体的なメモリ上の配置を調べてみるとよいです。さらに、荒っぽい話に聞こえるかもしれませんが、わざとバッファーオーバーランアクセスを起こしてして、どこで例外が起こるか試してみる、なんていうのも良い経験になります。
37
+ こうしたことを実感するには、各変数、配列どの**メモリアドレを確認する**どして、具体的なメモリ上の配置を調べみるとよいです。また変数配置は、コンパイラ(実際リンカ)の仕様調べると共にコンパイル時に**リンケージマッ情報を出力させて調べる**ことができます。さらに、荒っぽい話に聞こるかもしれませんが、わざとバッファーオーバーランアクセスを起こしみて、どこで例外が起こか試して、なんていう調べ方もきます。