プログラミング

【C言語組み込み】スタックオーバーフローと発生したときの対応

スタックオーバーフローとは何か、スタックオーバーフローが発生したときにどういった動作になるのか、スタックオーバーフローが発生したときにどうやって解析・デバッグすればよいか、について書いていきます。

スタックについての解説については以下を参照してください。

スタックオーバーフローとは?

スタックオーバーフローについて、具体例をあげながら現象を見ていきましょう。

まず、以下の図のように、

  • タスクAのスタック領域を0x2000から128byte確保
  • タスクBのスタック領域を0x2080から128byte確保
  • タスクAでは、スタック領域を60byte使用中
  • タスクBでは、スタック領域を120byte使用中

という状況を想定します。

この状態から、タスクBでさらに30byteのスタックを確保したとします。
すると、以下の図のように、タスクBのスタック領域の限度を超えて、タスクAのスタック領域に侵食し、タスクAのスタック領域の値を上書きしてしまいます。これがスタックオーバーフローです。

スタックオーバーフローが発生した時点ではまだプログラムは正常に動作しています。このあと、タスクAに実行権が移ってスタックをpopし、上書きされたデータをpopしたときに問題が発生します。

保存されていた一時変数が上書きされるだけであれば演算結果がおかしくなるだけです。(もちろん、それも大問題ですが・・・)
しかし、前回説明した通り、ここには関数コールが完了した後に元に戻るためのアドレスが格納されています。このアドレスが上書きされているため、戻りたいアドレスには戻ることができず、おかしなアドレスに飛ばされてプログラムが暴走します。

プログラムが全く正しく動作しなくなるので、たいていの場合はWDTによってプログラムがリセットされます。(デバッグ中はWDTを無効にすることが多いので、リセットせず暴走したままとなります。)

したがって、スタックオーバーフローが発生した場合は、プログラムが勝手にリブートするといった動作になります。

スタックオーバーフローの解析・デバッグ方法

スタックオーバーフローが発生したと分かっている場合の対処方法は簡単です。

  • スタックのサイズを大きくする
  • 大きなデータをスタックに入れている場合は静的な変数に変更して、スタック使用量を減らす

のいずれかの方法をとれば解決することができます。

スタックオーバーフローの厄介なところは、

「プログラムがリブートするんだけど、原因が何かわからない!!」

となることです。

スタックオーバーフローの厄介なポイントをまとめると

  • リブートする/プログラムが暴走する
  • 発生するときとしないときがある
  • プログラムが暴走する箇所が毎回違うことがある
  • ある関数を実行したらいきなり暴走する
  • 実際に問題があるタスクとは別のタスクで異常が発生する

といった点です。

プログラムが暴走する場合、デバッグをしていてもbreakでプログラムを止めることができなくなるので、そもそも問題が発生する箇所を確認することが難しいです。

そのうえ、スタックオーバーフローが発生したあとに該当の部分を通らないと現象が再現しないという問題もあります。スタックオーバーフローが発生するタスクが違うので、タイミングによっては毎回違う箇所で問題が発生するケースもあり、非常に厄介です。

また、関数から戻ってこなくなる、といった動作になることが多いので、なぜそんな現象が発生するのか理解できず、すごく不思議な現象に見えてしまうことがあります。

以上を踏まえて、デバッグの方針を以下にまとめます。

  • リセットがあったときはスタックオーバーフローを疑う
  • 現象が発生したときの動作を再現する
  • 暴走する箇所が特定できたら、そのタスクの下にスタック領域を持つタスクを疑う
  • 変更をしたあとにリブートが発生するようになった場合は、変更箇所を疑う

リセットがあったときはスタックオーバーフローを疑う

リセットを発生させる不具合の要因はいくつか考えられますが、スタックオーバーフローである可能性も十分にあるので、候補の1つであることを念頭に置いて以下の調査を実施すると、解決が早まるかもしれません。

現象が発生したときの動作を再現する

スタックオーバーフローが発生するのは、一番スタックを多く使う特定のパスのみということもよくあるので、特定の操作をしたときのみリセットが発生する、というパターンもよくあります。

動作を再現するというのは、不具合解析の基本中の基本ではありますが、やはり条件をきっちりそろえて問題が発生したときと同じ動作をさせるというのが大切になります。

暴走する箇所が特定できたら、そのタスクの下にスタック領域を持つタスクを疑う

問題が発生する箇所を見つけたらその周辺に問題の原因があると思いがちですが、今まで記載してきた通りスタックオーバーフローの場合は別のタスクに原因があります。

ですので、場所の特定ができたらスタック領域の配置順を確認し、そのタスクの後ろのアドレスにスタック領域を持つタスクのスタックを調査してみるのが良いでしょう。

わからないと思っていた不具合原因が一気に解決する可能性があります。

変更をしたあとにリブートが発生するようになった場合は、変更箇所を疑う

当たり前のことではありますが、変更直後に発生するようになった現象は変更箇所があやしいです。

1つ上と同じことではありますが、問題が発生する場所が変更箇所と違っていたからと言って変更箇所の問題がないとは言えないので、まずは変更した場所のスタックを確認してみましょう。

まとめ

以上、スタックオーバーフローの説明と発生したときの解析方法について解説しました。

スタックオーバーフローが発生したときの不具合解析は難しいですが、スタックオーバーフローの発生条件や発生したときの動作、解析方法を知っておくだけでも視野が広がり、短時間で解析ができるようになると思います。

もちろん、発生しないように対策しておくのが一番良いと思いますが、発生したとしても上記を参考に解析を頑張っていただきたいと思います。

COMMENT

メールアドレスが公開されることはありません。

CAPTCHA