C言語を扱う中で、バスエラー(BUSE. SIGBUS)について調べたのでメモを残しておきます。

本記事のコードは、WSL2のUbuntuで動作させた結果を掲載しています。

一応、以下に検証した動作環境を。

環境・バージョンなど

  • Windows 10 Pro 20H2 (OSビルド: 19042.782)
  • Windows Subsystem for Linux Update 5.4.72
  • Ubuntu 20.04.2 LTS

バスエラーは物理メモリやハードウェアバスに関するエラー、、、らしい

バスエラー(BUSE)は、CPUが物理メモリにアクセスしたときに発生するエラーだとのこと。存在しないデバイスや、制限されているデバイスへのアクセスで発生する、との記載も見かけました。

むむ、難しい。確保してないメモリに触ったときはSEGVが発生するけど、そうじゃないメモリのエラーで発生するということですかね。

メモリアライメント違反とかでもおこるようです。

発生させてみる

とりあえず、実際に発生させてみましょう。思いついたものから試してみます。

スタック領域の文字列の範囲外を書き換え

ヒープ領域ではなくスタック領域のメモリの範囲外へのアクセスだとどうなるでしょうか。スタック領域が物理メモリなのかどうかわからないですが。。。

int	main(void)
{
	char str[3] = "abc";

	str[3] = 'd';
	return (0);
}
$ gcc test_buse.c -o a.out
$ ./a.out
*** stack smashing detected ***: terminated
Aborted

思ってたのと違う実行結果でした。これも物理メモリではなかったということですかね。

ていうか、スタック領域のメモリの外側に触れると、ちゃんとその旨表示するんですね。知らなかった。

アライメント不整合のメモリアクセス

次は、調べてみたときに見かけた「メモリアライメント違反」の場合を試してみます。

#include <stdio.h>
#include <stdlib.h>

int	main(void)
{
	long *l;
	char *c;

	l = (long *)malloc(sizeof(long) * 2);

	/* ポインタ変更前 */
	printf("l(%p): %ld\n", l, *l);

	/* longのポインタをcharのポインタに代入して、1バイトだけ動かしてから戻す */
	c = (char *)l;
	c++;
	l = (long *)c;

	/* 変更前から1バイトずれて、アライメント違反になっているはず */
	printf("l(%p): %ld\n", l, *l);

	return (0);
}
$ gcc test_buse2.c -o a.out
$ ./a.out
l(0x562c8fdef2a0): 0
l(0x562c8fdef2a1): 0

あれっ、エラーにならず正常終了してしまいました…明らかに不整合なのに…

もうちょっと調べてみたところ、アライメント違反のチェックフラグがデフォルトでオフになっているようです。

EFLAGSのACをオンにすると検知してくれます。

#include <stdio.h>
#include <stdlib.h>

int	main(void)
{
	/* asmでEFLAGSを変更 */
	asm( "pushf\n\torl $0x40000,(%rsp)\n\tpopf");

	long *l;
	char *c;

	l = (long *)malloc(sizeof(long) * 2);

	/* ポインタ変更前 */
	printf("l(%p): %ld\n", l, *l);

	/* longのポインタをcharのポインタに代入して、1バイトだけ動かしてから戻す */
	c = (char *)l;
	c++;
	l = (long *)c;

	/* 変更前から1バイトずれて、アライメント違反になっているはず */
	printf("l(%p): %ld\n", l, *l);

	return (0);
}
$ gcc test_buse3.c -o a.out
$ ./a.out
Bus error

めでたくバスエラーが発生しました。

アセンブリわかんないな~…近いうち勉強しないと…

バスエラー、めったに見る機会がなさそう

昔は、アライメント違反で普通に発生してたんですかね…

いまはデフォルトでチェック機能がオフになっているので、意図的に検知させないとめったに遭遇しないんじゃないでしょうか。

けっきょく、物理メモリとかハードウェアバスのところまでは理解が及びませんでしたが…

どうやら前提知識が不足しているっぽいので、いまは深追いせずにペンディングにしときます。

説明できるようになったら、また記事追加しようと思います。

以上、あなたのお役に立てれば幸いです。