バスエラーとは何ですか?


254

「バスエラー」メッセージの意味と、segfaultとの違い


5
両方の簡単な説明を追加したいと思います。セグメンテーション違反は、許可されていないメモリにアクセスしようとしていることを意味します(たとえば、プログラムの一部ではありません)。ただし、バスエラーの場合、通常、存在しないメモリにアクセスしようとしている(12Gのアドレスにアクセスしようとしたが、8Gのメモリしかない)か、使用可能なメモリの制限を超えていることを意味します。
xdevs23

これはどのプラットフォームで見ましたか?PC?マック?x86?32/64?
Peter Mortensen、

回答:


243

最近のx86ではバスエラーはまれであり、プロセッサが要求されたメモリアクセスを試行できない場合に発生します。通常は次のとおりです。

  • アライメント要件を満たさないアドレスでプロセッサ命令を使用する。

セグメンテーション違反は、プロセスに属していないメモリにアクセスするときに発生します。これらは非常に一般的であり、通常は次の結果です。

  • 割り当て解除されたものへのポインタを使用する。
  • 初期化されていない偽のポインタを使用しています。
  • nullポインタを使用します。
  • バッファオーバーフロー。

PS:より正確には、これは問題を引き起こすポインタ自体を操作しているのではなく、ポイントしているメモリにアクセスしています(逆参照)。


106
それらは珍しいことではありません。How to Learn C the Hard Wayのエクササイズ9で、すでに問題1に遭遇しています...
11684

24
バスのエラーのもう1つの原因は(とにかくLinuxの場合)、オペレーティングシステムが物理メモリ(たとえば、メモリ不足の状態、または巨大ページメモリを使用しているときに巨大ページが不足している)で仮想ページをバックアップできない場合です。通常、mmap(およびmalloc)のみ仮想アドレス空間を予約し、カーネルがオンデマンドで物理メモリを割り当てます(いわゆるソフトページフォールト)。十分な大きさのmallocを作成し、十分な量のmallocに書き込むと、バスエラーが発生します。
Eloff、2015

1
私にとっては、含まれているパーティション/var/cacheは単にいっぱいでしたaskubuntu.com/a/915520/493379
c33s

2
私の場合、この方法は、static_castvoid *格納コールバック(メソッドへのオブジェクトと他の1つの属性点)、そのオブジェクトにパラメータを。次に、コールバックが呼び出されます。ただし、渡されvoid *たものは完全に異なるものだったため、メソッド呼び出しによってバスエラーが発生しました。
Christopher K.

@bltxdバスエラーの性質を知っていますか。つまり、リングバス上のメッセージには、リング上のストップがそれによって送信されたメッセージを受け入れるメカニズムがいくつかありますが、リングを一周し、受け入れられなかったことを示唆しているためです。ラインフィルバッファーがエラーステータスを返し、それがリタイアするとパイプラインをフラッシュして正しい例外マイクロルーチンを呼び出すと思います。これは基本的に、メモリコントローラーがその範囲内のすべてのアドレスを受け入れる必要があるため、BARなどが変更された場合、内部で行う必要があることを示唆しています
Lewis Kelsey

84

segfaultが、アクセスを許可されていないメモリにアクセスしています。読み取り専用であり、権限がありません...

バスエラーが発生している可能性のないメモリにアクセスしようとしています。システムにとって意味のないアドレスを使用したか、その操作に間違った種類のアドレスを使用しました。


14

mmap 最小限のPOSIX 7の例

「バスエラー」は、カーネルSIGBUSがプロセスに送信するときに発生します。

ftruncate忘れられたためにそれを生成する最小限の例:

#include <fcntl.h> /* O_ constants */
#include <unistd.h> /* ftruncate */
#include <sys/mman.h> /* mmap */

int main() {
    int fd;
    int *map;
    int size = sizeof(int);
    char *name = "/a";

    shm_unlink(name);
    fd = shm_open(name, O_RDWR | O_CREAT, (mode_t)0600);
    /* THIS is the cause of the problem. */
    /*ftruncate(fd, size);*/
    map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    /* This is what generates the SIGBUS. */
    *map = 0;
}

次で実行:

gcc -std=c99 main.c -lrt
./a.out

Ubuntu 14.04でテスト済み。

POSIX は次のように説明 SIGBUSします。

メモリオブジェクトの未定義部分へのアクセス。

MMAPの仕様では、その言葉:

paで始まり、オブジェクトの終わりに続いてlenバイトの間ページ全体に続くアドレス範囲内の参照は、SIGBUSシグナルの配信になります。

そしてshm_open それはサイズ0のオブジェクトを生成すると言います:

共有メモリオブジェクトのサイズはゼロです。

そこで*map = 0、割り当てられたオブジェクトの終わりを超えて触れています。

ARMv8 aarch64での非境界整列スタックメモリアクセス

これは、バスエラーとは何ですか?SPARCの場合ですが、ここではより再現性の高い例を示します。

必要なのは、独立したaarch64プログラムだけです。

.global _start
_start:
asm_main_after_prologue:
    /* misalign the stack out of 16-bit boundary */
    add sp, sp, #-4
    /* access the stack */
    ldr w0, [sp]

    /* exit syscall in case SIGBUS does not happen */
    mov x0, 0
    mov x8, 93
    svc 0

そのプログラムは、ThunderX2サーバーマシンでUbuntu 18.04 aarch64、Linuxカーネル4.15.0でSIGBUSを発生させます

残念ながら、QEMU v4.0.0のユーザーモードでは再現できません。理由はわかりません。

障害はオプションであり、SCTLR_ELx.SAおよびSCTLR_EL1.SA0フィールドによって制御されているようです。ここで関連ドキュメントを少しまとめました


11

アプリケーションがデータバス上でデータの不整合を示すと、カーネルがSIGBUSを生成すると思います。ほとんどのプロセッサーのほとんどの[?]現代のコンパイラーは、プログラマーのためにデータをパディング/アラインするので、(少なくとも)ヨアのアラインメントの問題は軽減されたため、最近ではSIGBUSはあまり頻繁に見られません(AFAIK)。

From:ここから


1
あなたのコードでやっている厄介なトリックに依存します。ポインターの計算を行うような愚かなことを行ってから問題モードにアクセスするために型キャストする場合(つまり、uint8_t配列を設定し、配列のポインターに1、2、または3を追加してから型キャストする)、BUSエラー/アライメントトラップをトリガーできます。短い、int、またはlongにして、問題のある結果にアクセスしてみてください。)X86システムでは、実際のパフォーマンスは低下しますが、ほとんどの場合これを実行できます。 一部の ARMv7システムではこれを行うことができますが、ほとんどのARM、MIPS、Powerなどがそれをうならせます。
Svartalf 2014

6

何らかの理由でコードページをページインできない場合にも、SIGBUSを取得できます。


7
私は、プロセス実行中の.soファイルを更新するとき、これはしばしば起こる
poordeveloper

あなたがしようとした場合に発生するもう一つの理由はあるmmapのサイズよりも大きいファイル/dev/shm
ilija139

3

OS XでCをプログラミングしているときに発生したバスエラーの具体例:

#include <string.h>
#include <stdio.h>

int main(void)
{
    char buffer[120];
    fgets(buffer, sizeof buffer, stdin);
    strcat("foo", buffer);
    return 0;
}

ドキュメントを覚えていない場合はstrcat、最初の引数を変更して、ドキュメントが最初の引数に2番目の引数を追加します(引数を反転すると、正常に動作します)。Linuxでは、これはセグメンテーション違反を引き起こします(予想どおり)が、OS Xではバスエラーが発生します。どうして?本当に分からない。


おそらくスタックオーバーフロー保護はバスエラーを発生させます。
ジョシュア

1
"foo"はメモリの読み取り専用セグメントに格納されているため、書き込むことはできません。これはスタックオーバーフロー保護ではなく、メモリ書き込み保護のみです(プログラムがそれ自体を書き換えることができる場合、これはセキュリティホールです)。
Mark Lakata、2016年

3

バスエラーの典型的な例の1つは、SPARC(少なくとも一部のSPARC、おそらくこれは変更されている)などの特定のアーキテクチャーで発生し、不整合なアクセスを行った場合です。例えば:

unsigned char data[6];
(unsigned int *) (data + 2) = 0xdeadf00d;

このスニペットは、32ビット整数値0xdeadf00dを(ほとんどの場合)適切に配置されていないアドレスに書き込もうとし、この点で「ピッキー」なアーキテクチャでバスエラーを生成します。インテルのx86は道、によって、あるいないようなアーキテクチャ、それが(もっとゆっくり、それを実行するにもかかわらず)のアクセスを可能にします。


1
場合によっては、データがありました[8]。これは32ビットアーキテクチャでは4の倍数になりました。それで、それは整列されます。それでもエラーは発生しますか?また、ポインタのデータ型変換は悪い考えですが、説明してください。それは壊れやすいアーキテクチャで不整合エラーを引き起こしますか?詳しく説明してください。
2013年

へえ。ポインターの計算を行ったポインターに対して型変換を行うので、型変換はそれほど多くありません。上記のコードをよく見てください。コンパイラーは慎重にデータのポインターをdwordで調整しました。その後、参照をTWOでオフセットし、dword以外の境界となるものへのアクセスをdwordで調整する必要がある非常に必要なものに型キャストすることで、コンパイラーをすべて台無しにします。
Svartalf 2014

「壊れやすい」というのは、私がこのすべてに使用する言葉ではありません。X86マシンとコードでは、しばらくの間、愚かなことをしている人がいます。これもその1つです。この種の問題が発生した場合は、コードを再考してください。そもそも、X86ではパフォーマンスがあまりよくありません。
Svartalf 2014

@Svartalf:x86では、非整列ポインターへのワードアクセスは整列ポインターへのワードアクセスよりも確かに遅くなりますが、少なくとも歴史的には、無条件にバイトからアセンブリする単純なコードよりも速く、試行するコードよりも単純です。さまざまなサイズの操作の最適な組み合わせを使用する。特定のプラットフォームで最適なアプローチをコンパイラーが使用できるように、C規格に一連の小さな整数/文字との間で大きな整数型をパック/アンパックする手段が含まれることを望みます。
スーパーキャット2015

@Supercat:問題はこれです-X86ではそれを回避できます。ARM、MIPS、Powerなどでこれを試すと、厄介なことが起こります。Arch V7未満のARMでは、コードでアライメントエラーが発生します。V7では、ランタイムが設定されている場合、深刻なパフォーマンスヒットで処理できます。あなたは単にこれをしたくないだけです。率直に言って、それは悪い習慣です。:D
Svartalf 2015

2

それはあなたのOS、CPU、コンパイラ、そしておそらく他の要因に依存します。

一般的には、CPUバスがコマンドを完了できなかったか、競合が発生したことを意味しますが、実行されている環境とコードに応じて、さまざまなことが起こります。

-アダム


2

通常、非境界整列アクセスを意味します。

物理的に存在しないメモリにアクセスしようとすると、バスエラーも発生しますが、MMUを搭載したプロセッサとバグのないOSを使用している場合は、非バグがないため、このエラーは表示されません。 -プロセスのアドレス空間にマッピングされた既存のメモリ。


2
私のi7には確かにMMUがありますが、OS XでCを学習しているときにまだ初期化されていません(初期化されていないポインターをに渡すscanf)。それは、OS X Mavericksにバグがあるということですか?バグのないOSでの動作は何でしょうか?
Calvin Huang

2

ルートディレクトリが100%のときにバスエラーが発生しました。


1

Mac OS Xでバスエラーが発生した理由は、スタックに約1Mbを割り当てようとしたためです。これは1つのスレッドでうまく機能しましたが、openMPを使用すると、Mac OS Xの非メインスレッドのスタックサイズが非常に制限されているため、これがバスエラーを引き起こします。


1

上記のすべての回答に同意します。BUSエラーに関する私の2セントは次のとおりです。

BUSエラーは、プログラムのコード内の命令から発生する必要はありません。これは、バイナリを実行しているときに発生し、実行中にバイナリが変更されます(ビルドによって上書きされたり、削除されたりするなど)。

これが原因かどうかの確認これが原因かどうかを確認する簡単な方法は、同じバイナリの実行中のインスタンスを起動してビルドを実行することです。SIGBUSビルドが完了してバイナリ(両方のインスタンスが現在実行しているもの)を置き換えた直後に、実行中の両方のインスタンスがエラーでクラッシュしました。

根本的な理由: これは、OSがメモリページをスワップし、場合によってはバイナリが完全にメモリに読み込まれないことがあり、OSが同じバイナリから次のページをフェッチしようとしたが、最後からバイナリが変更されたためにこれらのクラッシュが発生するためですそれを読んで。


これは、私の経験ではバスエラーの最も一般的な原因です。
イタイチ

0

上記のblxtdの回答に加えて、プロセスが特定の「変数」のメモリにアクセスできない場合にもバスエラーが発生します。

for (j = 0; i < n; j++) {
    for (i =0; i < m; i++) {
        a[n+1][j] += a[i][j];
    }
}

お知らせ「不注意の」使用法、変数「I」「forループ初?これが、この場合のバスエラーの原因です。


m> = nの場合、既存のiの値に応じて、外側のループが1回実行されるか、まったく実行されません。m <nの場合は、配列の境界を使い果たし、バスエラーではなくセグメンテーション違反が発生する可能性が高くなるまで、jインデックスを増やして無期限に実行されます。このコードがコンパイルされる場合、変数 'i'自体のメモリへのアクセスに問題はありません。申し訳ありませんが、この答えは間違っています。
イタイチ

0

ARMv7プロセッサで、最適化されていないときにセグメンテーション違反を発生させるコードを記述できるが、-O2(さらに最適化)でコンパイルするとバスエラーが発生するという難しい方法を見つけました。

Ubuntu 64ビットのGCC ARM gnueabihfクロスコンパイラを使用しています。


これはどのように質問に答えますか?
Peter Mortensen、

-1

バスエラーを引き起こす典型的なバッファオーバーフローは、

{
    char buf[255];
    sprintf(buf,"%s:%s\n", ifname, message);
}

ここで、二重引用符( "")内の文字列のサイズがbufサイズより大きい場合、バスエラーが発生します。


1
ええと...これが当てはまった場合は、Windowsや他のマシンで常に読んだスタック破壊攻撃の代わりに、BUSエラーの懸念があるでしょう。BUSエラーは、アドレスが無効なためにマシンが単純にアクセスできない「メモリ」にアクセスしようとしたことが原因で発生します。(したがって、「バスエラー」という用語です。)これは、プロセッサがバスラインにアドレスを配置できない限り、無効なアライメントなど、多くの障害が原因である可能性があります。
Svartalf 2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.