回答編集履歴

1

回答を修正

2020/02/04 13:53

投稿

rubato6809
rubato6809

スコア1382

test CHANGED
@@ -1,6 +1,30 @@
1
- 経験的に言うと(具体的なアドレスなどはともかく)原因も違いも明確です。例外が発生するかしないかの違いは、コピーのソース側となっている
1
+ 経験的に言うと(具体的なアドレスなどはともかく)原因も違いも明確です。例外が発生するかしないかの違いは、コピーのソース側となっている配列 char szString2[512]; の配置場所(メモリアドレス)に依ります。もう少し言うと、この512バイトのメモリに続く**残り512バイトのメモリ領域の中に、アクセス許可されている(=保護されていない)メモリと、許可されていない(=保護されている)メモリの境界があった**という事です。境界が残り512バイトのメモリ領域の中に無ければクラッシュしません。
2
2
 
3
- 配列 char szString2[512]; の配置場所(メモリアドレス)に依ります。もう少し言うと、この512バイトのメモリに続く**残り512バイトのメモリ領域の、どこかがアクセスが許可されていない領域の先頭番地だった**という事です。
3
+
4
+
5
+ > クラッシュするケースでは保護されているメモリ空間に不正にアクセスしたから、クラッシュした
6
+
7
+
8
+
9
+ は正しい理解です。念の為に繰り返しますが、アクセス許可されているメモリとは、保護されていないメモリ、ですよ。
10
+
11
+
12
+
13
+ 書き込む先のメモリ領域にバッファオーバーランして書き込んだら、他の用途に使っているメモリ(他の変数)を上書きするので、その後、プログラムが異常動作する事は容易に想像できると思います。
14
+
15
+ 一方、ご質問の状況は読み出し側のバッファオーバーランです。通常、コピー元のメモリ領域は、読みだす分にはオーバーランアクセスしたって(メモリを書き変えないのだから)問題を起こすことは無いものです。従って、問題なく動作することは少なくありません。というか、読み出しだけならオーバーランアクセスしても何も問題を起こさないでしょう。
16
+
17
+
18
+
19
+ ところがクラッシュするケースがある、それは例外(**メモリアクセス例外**、OSやCPUによって具体的な呼び名は違うかもしれない)が起こったことを意味します。そうなる理由はただひとつ、残り512バイトのメモリ領域の、ある番地から、アクセスが許可されていない・保護されたメモリだった、ということに尽きます。
20
+
21
+
22
+
23
+ プログラムが動作する時、動作に必要なメモリを、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を制御することで、各プロセスがアクセスできるメモリに制限をかけています。アクセス制限されたメモリ領域をアクセスしようとすれば(プログラムの異常動作であると見做して)例外を発生させ、プログラムを停止するようにしています。それがクラッシュするという事です。
24
+
25
+
26
+
27
+ そのアクセスを許可したメモリ領域の中に szString1[1024] も szString2[512]も配置されますが、szString2[512] に続く、残りの512バイトがアクセス可能なメモリである保証はありません。アクセスできたなら、それは運が良かったわけです。szString2[512]をメモリのどこに配置するか、なぜ境界の近くに配置されたのか、は**コンパイラと、OSの、両方の事情の合わせ技**のようなもので決まります。お使いのOSとコンパイラ次第なので具体的な説明はできかねますが。
4
28
 
5
29
 
6
30
 
@@ -34,28 +58,16 @@
34
58
 
35
59
  }
36
60
 
37
-
38
-
39
61
  ```
40
62
 
41
- 書き込む先のメモリ領域にバッファオーバーランして書き込んだら、他の用途に使ってメモリを上書きするので、その後、プログラム異常動作する事は容易に想像できると思います。
63
+ 「境界」を超えた時点で「temp = *s; // コピー元のメモリを読む」とメモリアクセス命令
42
64
 
43
- 一方、ご質問の状況は読み出し側のバッファオーバーランです。通常、コピー元のメモリ領域は、読みだす分にはオーバーランアクセスしたって(上書きではないのだから)具体的な問題起こすこは無いものです。従って、問題なく動作することは少なくありせん。
65
+ > 「保護されているメモリ読み取り(中略)操作行おうした・・・」
44
66
 
45
67
 
46
68
 
47
- ところがクラッシュするケースがある、それは例外(**メモリアクセス例外**?OSやCPUによって具体的な呼び名は違うかもしれない)が起こったことを意味します。そうなる理由はただひとつ、残り512バイトのメモリ領域の、ある番地から、アクセスが許可されていないメモリだった、ということに尽きます。
48
-
49
- メモリアクセス例外は、上に示したコードで「temp = *s; // コピー元メモリを読む」というメモリアクセス命令起きます。
69
+ 即ち不正アクセスになり、例外を起こしたのです。
50
70
 
51
71
 
52
72
 
53
- そのプログラムが動作する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を制御することで、各プロセスアクセスできるメモリに制限をかけています。この制限さたメモリを超えた領域をアクセスすれば(プログラムの異常動作あると見て)例外を発生させ、プログラムを停止すようにしています。それがクラッシュすいうです。
54
-
55
-
56
-
57
- そのアクセスを許可したメモリ領域の中に szString1[1024] も szString2[512]も配置されますが、szString2[512] に続く、残りの512バイトもアクセス可能なメモリである保証はありません。アクセスできたなら、それは運が良かったわけです。szString2[512]をメモリのどこに配置するかは、**コンパイラと、OSの、両方の事情の合わせ技**のようなもので決まります。
58
-
59
-
60
-
61
- こういうことを実感するには、各変数、配列などの**メモリアドレスを確認する**などして、具体的なメモリ上の配置を調べてみるとよいです。さらに、荒っぽい話に聞こえるかもしれませんが、わざとバッファーオーバーランアクセスを起こしてして、どこで例外が起こるか試してみる、なんていうのも良い経験になります。
73
+ こうしたことを実感するには各変数、配列どの**メモリアドレを確認する**どして、具体的なメモリ上の配置を調べてみるよいです。また変数配置、コンパイラ(実際はンカ)の仕様を調べると共に、コンパイル時に**リンケージマプ情報出力させ調べ**ことができます。さらに、荒っぽい話に聞えるかもしませんが、わざとバッファーオーバーランアクセスを起こしてみて、どこで例外が起こか試して、なんていう調べ方もきます。