C言語の問題点
C言語は1972年頃に開発された高級言語の第二世代とも言えるプログラミング言語です。
CはOS記述言語とも言われているようにWindows, Unix, LinuxというOSはCでできています。
現在PHP, Python, Java, JavaScriptという新しい言語が普及していますが実はC言語で記述されているという一回りするとC言語の一強独占状態です。
C言語は素晴らしい言語ですがセキュリティ上「踏み込み過ぎた」箇所があります。
現在あるセキュリティ問題はここにあります。
少なくともメモリオーバーラン、バッファオーバラン、メモリオーバーフロー、バッファオーバーフローこれらは全て同じ現象を指します。
ここではメモリオーバーランで統一します。これらC言語の仕様設計ミスで発生している事を説明します。
併せて解決方法も説明します。
C言語正確にはC言語本体+標準ライブラリの全体においてセキュリティホールは2つ存在します。
- 標準ライブラリの欠陥(設計ミス)。具体的にはヌルストップです。
- C言語の欠陥、正確には”Call by Value”の安易な全面採用。
現在のC言語は一般的にはどちらも同じ所に開いておりセキュリティホールが発生しています。
この問題はどちらか一方を塞げば解決します。要は貫通しなければ問題は起きません。

AZ-Cを採用すれば例外を除き普通にセキュリティホールは消え去ります。
しかしセキュリティ上、設計ミスは可能な限り除去すべきです。
過去に開発した膨大なソフトウェア資産はそのまま使用するのが賢明です。
ただし新規に作るソフトウェアはセキュリティを考えてしっかりとプログラミングすべきです。
ここでは安全なプログラミング技法を説明します。
1.ヌルストップ問題とは
C言語では文字列例えば”Hellow”とメモリ上に配置する時は次のようにします。最後は終わりを示す0x00(ゼロ)で締め括ります。
H | e | l | l | o | w | 0x00 |
これはC言語というよりはC言語用の標準ライブラリの設計ミス。あるいはC言語が作ろうとしたOS「Unix」の設計ミスと言えるものです。決してC言語そのものの欠陥ではないです。
これは1980年代、日本にC言語が上陸した時にプログラマ達は「これは明らかにC言語の設計ミスだね」と指摘しあっていたものです。
彼ら(私たち)の感覚では本来こうあるべきと感じていました。
【パスカルストリング】
0x06 | H | e | l | l | o | w |
いわゆるパスカルストリングと呼ばれる形式です。”Hellow”という文字列を定義するにしても先頭に文字長を指定する形式です。
先頭の「0x06」は6文字の文字列、ここでは”Hellow”が格納を意味しています。
次のような古典的なCの構文を書いてみると一目瞭然です。
void pass_cpy( char *pass )
{
char wk[24];
strcpy( wk, pass ); // passにもし23バイト以上の文字列が来たらメモリがパンクする。
・・・・
}
このような良くあるプログラムを書いた時、メモリオーバーランが発生します。
問題は渡って来た変数(あるいは引数)は実際にメモリを舐めないと長さが判らないためです。
パスカルストリングだとコピーの段階で事前に長さが判っているので安全です。
2. C言語の欠陥、”Call by Value”の問題点、AZ-Cによる解決
Cプログラミングの聖書とも言えるカーニハンとリッチーが書いた「C言語入門」1978年初版においてはC言語はその革新性としてCall by Valueの採用を歌っています。
ちなみにC言語以前の高級言語たとえばFORTRANはCall by Referenceという方式です。
両者の関係は次の関係にあります。
安全性 | 通常Call | 再帰Call | マルチスレッド | 備考 | |
Call by Reference | ◎ |
〇 | × | × | アプリ開発向き |
Call by Value | △ | 〇 | 〇 | 〇 | OS, ドライバ向き |
Call by Valueは再帰Call(リカーシブコール)、マルチスレッド呼び出しが可能という長所があります。
一般的なプログラムは再帰コール、マルチスレッドは稀に使用します。あるいは使用しても全体の数%ほど使いどころがあります。大多数は通常コールでプログラミングできます。
またUnix/Linuxにおいてはfork文というマルチタスク機能がありマルチスレッドは古いプログラムほど見かけないです。
C言語が採用したCall by Valueの危険性はそのメモリ配置にあります。
ここでは極端に省略して要点をまとめます。詳細は別記事で詳しく説明する予定です。
Call by Valueによるスタックメモリ上の配置を示します。このように変数領域の中に関数のリターンアドレスが配置しています。
これがCall by Valueを採用したコンパイラ言語の共通の脆弱性です。
関数のどこかでひとたび、メモリオーバーランが起きると0xFFFF方向にデータの書き換えが次々に伝染していきます。
問題は「次に実行するアドレス」が書き換えられることです。
古典的なウィルスはここに自分自身のアドレスで上書きしてExploit(制御件を乗っ取り)します。これがメモリオーバーランによるセキュリティホールです。
この問題はCall by Value固有の物です。
AntiZeroday-Cはメモリオーバーラン防止装置を追加実装してこの問題を防御します。
より詳しい説明は下記を次の資料を見てください。特にIPAの資料は専門家向けの具体的なハッキング手口を説明しています。
https://dnki.co.jp/w2/download/azc/bufferoverrun.pdf
政府団体IPAによる詳細な説明”[6-1.]バッファオーバーラン~その1・こうして起こる~”
https://www.ipa.go.jp/security/awareness/vendor/programmingv1/pdf/b06_01.pdf
この危険な現象をAZ-Cは次のメカニズムで遮断します。
Ubuntu Linux にてAZ-Cを使いバッファオーバーランの防止するデモ資料を公開します。
https://dnki.co.jp/w2/download/azc/demo_doc.pdf
3. より強固なC言語によるセキュアプログラミングのススメ
この問題はCall by Referenceではほとんど発生しません。幸いC言語にはCall by Reference的な記述が可能です。
AZ-Cの備えるメモリオーバーラン防止装置だけでなく普段よりC言語の特性を理解した安全なプログラミングを紹介します。
まずなぜCall by Referenceが安全なのか図で示します。
Call by Referenceには2つの安全性があります。
- 次に実行するアドレス情報は通常は破壊されない。
- 破壊されるメモリは普通は現在の関数 のデータ領域からでそれ以降は未使用の領域である。
Call by Referenceは利用するメモリ領域をスタックと変数格納領域(データヒープという)分離しているためセキュリティ上堅牢であると判ります。