マイコンでスタックオーバーフローがHard Faultになる仕組みをC言語とメモリ構造から解説

C言語関連

マイコン開発では、スタックオーバーフローによって突然Hard Faultが発生することがあります。しかし、スタック領域の伸びる方向やメモリ配置を考えると「本当に戻り先アドレスが上書きされるのか」と疑問に感じる方も少なくありません。この記事では、C言語で動作する組み込みマイコンを例に、スタックオーバーフローからHard Faultが発生するまでの流れを、スタックの仕組みやメモリ配置を含めて分かりやすく解説します。

マイコンのスタック領域とは何か

スタックとは、関数呼び出し時に一時的なデータを保存するためのメモリ領域です。C言語のプログラムでは、ローカル変数、関数の引数、戻り先アドレス、レジスタ退避情報などがスタックに保存されます。

例えば関数Aから関数Bを呼び出した場合、CPUは「関数Bが終了した後に戻る場所」を覚えておく必要があります。そのため、関数呼び出し時には戻り先アドレスがスタックへ保存されます。

ARM系マイコンなど多くの組み込みCPUでは、スタックはRAMの高アドレス側から低アドレス側へ伸びる方式が一般的です。ただし、重要なのはスタックがどちら向きに伸びるかではなく、スタック領域の範囲を超えて書き込みが発生すると、隣接する別領域を破壊する可能性があるという点です。

スタックオーバーフローで戻り先アドレスが壊れる理由

スタックオーバーフローが発生すると、スタックポインタが指している正常な領域を超えてデータを書き込むことになります。

例えば、ある関数のスタック上に以下のような配置があったとします。

アドレス 保存内容
高アドレス側 呼び出し元へ戻るアドレス
中間 保存レジスタ
低アドレス側 ローカル配列や変数

ここで大きすぎる配列への書き込みや、配列範囲外アクセスが発生すると、本来書き込むべきでないスタック領域までデータが侵入します。

例えば、char型配列を10バイトしか確保していないのに100バイト書き込むような処理を行うと、配列の後ろに配置されている保存データが破壊されます。その結果、戻り先アドレスが不正な値になる場合があります。

スタックは一方向なのになぜ上書きが起きるのか

スタックが一方向に伸びるという認識は正しいですが、それは「新しいデータを配置する方向」を示しているだけです。スタック以外のコードが、その範囲を守って書き込みを行う保証はありません。

つまり問題になるのは、スタック領域を新しく確保する動作ではなく、既に確保されたスタック上のデータに対して、プログラムが誤った書き込みを行うことです。

例えば、スタック領域が下方向へ伸びるCPUで、低アドレス側にある配列へ大量のデータを書き込むと、上位アドレス側にある保存情報まで到達することがあります。その結果、戻り先アドレスや保存レジスタが破壊されます。

Hard Faultが発生するまでの具体的な流れ

スタックオーバーフローからHard Faultが発生する流れは、一般的には以下のようになります。

  1. 関数呼び出しによってスタック領域が使用される
  2. 大きすぎるローカル配列や再帰処理などでスタック使用量が増える
  3. スタック領域の限界を超えてメモリ破壊が発生する
  4. 戻り先アドレスや保存されたCPU情報が壊れる
  5. 関数終了時にCPUが壊れたアドレスへジャンプしようとする
  6. 存在しないアドレスや実行禁止領域へアクセスする
  7. CPUの例外処理によってHard Faultが発生する

例えば、関数終了時に本来なら0x08001234番地へ戻るはずだったものが、スタック破壊によって0xFFFFFFFFのような不正値になった場合、CPUはそのアドレスを実行しようとして異常を検出します。

スタックオーバーフローの主な原因

組み込み開発でスタックオーバーフローを起こしやすい原因には、大きなローカル変数の確保があります。

例えば、以下のようなコードは注意が必要です。

char buffer[10000];

このような大きな配列を関数内で宣言すると、RAM容量が限られるマイコンでは簡単にスタックを圧迫します。

また、終了条件のない再帰関数や、多重割り込みによるスタック使用量の増加も原因になります。

スタックオーバーフローを防ぐ方法

スタックオーバーフローを防ぐには、まずスタック使用量を把握することが重要です。多くの開発環境では、スタック領域のサイズ設定や使用量確認機能があります。

大きな配列はスタックではなく、グローバル領域や静的領域へ配置することでスタック消費を減らせます。

また、配列アクセスでは境界チェックを行い、バッファサイズを超える書き込みを防ぐことも重要です。組み込みシステムでは小さなメモリ破壊が、後からHard Faultとして現れることがあります。

まとめ

スタックオーバーフローによってHard Faultが発生する原因は、スタックが一方向に伸びることではなく、プログラムの誤ったメモリアクセスによってスタック領域外へデータを書き込んでしまうことです。

スタック上にはローカル変数だけでなく、戻り先アドレスやCPUの保存情報も存在するため、範囲外アクセスによってそれらが破壊される可能性があります。

マイコン開発では、スタックサイズの管理、大きな配列の扱い、メモリ破壊の防止を意識することで、原因不明のHard Faultを大きく減らすことができます。

コメント

タイトルとURLをコピーしました