7/29 (火)
[CPU] Itanium2 プロセッサのエラッタ?
前提
最近のアーキテクチャには CPU が指定のメモリ位置に到着したり、 指定のメモリにアクセスした時にトラップ/フォルトをあげるハード機構が存在する。 ここではまとめてデバッグレジスタ と呼ぶことにする。 デバッグレジスタ機構は、 主にデバッガから利用され、 例えば gdb だと awatch コマンドのために利用されている。
我らが IA-64 アーキテクチャは、
他のアーキテクチャと比べて結構リッチなデバッグレジスタを持っている。
Itanium2 プロセッサの場合、
命令実行を停止させるための
Instruction Break Register (IBR)が4組、
指定のメモリアクセスをした時にフォルトを発生させるための
Data Break Register (DBR)が4組も存在する
さらに面白いのは IA-64 のデバッグレジスタは DBR/IBR は 2 本のレジスタが一組で、 偶数レジスタは「アドレス指定レジスタ」と 奇数レジスタは「マスク指定レジスタ」となっている点だ。 検査アドレスがデバッグレジスタにヒットするとは、 以下が求められる。
- 検査アドレスの上位8ビットがアドレス指定レジスタの上位8ビットに一致する。
- 検査アドレスの下位56ビットがマスク指定レジスタの下位56ビットで論理積をとったものと、 アドレス指定レジスタの下位56ビットがマスク指定レジスタの下位56ビットをとったものに一致する。
bool match_dbr(uint64_t address, uint64_t dbr_address, uint64_t dbr_mask) { uint64_t mask = (dbr_mask | 0xFF00000000000000UL) return (address & mask) == (dbr_address & mask); }
IA-32 のデバッグレジスタなどの検査するバイト範囲が1,2,4バイトに絞られているのに対して、 IA-64 はマスクを使って自由に合成できる。 そのため2のべき乗の並んだ巨大配列の列要素だけを検査対象にするなどの柔軟な指定が可能だ。
ちなみにマスク指定レジスタのアドレスマスクに使用されない上位8ビットは、 属性情報を指定するのに利用される。
struct dbr_address { uint64_t r : 1; // 読み込みでブレークする uint64_t w : 1; // 書き込みでブレークする uint64_t ignore : 2; uint64_t pl : 4; // ブレークする特権レベル(PL0〜PL3)をビット指定 uint64_t mask : 56; };
Itanium2 のデバッグレジスタにはもう一つ美味しい点がある。 少なくともデータブレークの方はブレーク設定を有効にしたままプログラムを走行させても、 DBR の設定にヒットしない限り実行速度が落ちないという点だ。 これは Madison/Montecito で中規模テストプログラムの走行実験を行って、 観測可能な範囲で速度低下ないことを確かめている。 このためブレークレジスタを gdb などのデバッガにデバッグ用に使わせるのではなく、 自分のプログラムの中で利用したくなる (当然、カーネルを書き直すことが前提)。
エラッタ?
DBR の値の変更に対してデバッグレジスタ機構が正しく追従してこない現象を発見。 検査範囲外のメモリアクセスに対してデータデバッグブレークフォルトが発生することがある。
DBR は 0x0007ffff00000000 の1バイトをピンポンでアクセスした場合のみに デバッグフォルトが発生するように仕掛けているのに、 0x0010028000????? という明後日なアドレスでフォルトが発生する。 現在残っている DBR は、どう考えても 0x0010028000????? でブレークしない設定だ。
dbr[0] = 0x0007ffff00000000 dbr[1] = 0x48ffffffffffffff // r=0, w=1, pl3 のみ許可, mask=0x00ffffffffffffff dbr[2] = 0x0000000000000000 // あと dbr[7] まで 0x0
直前までは 0x0010028000????? にヒットする dbr があったのだが、 それはアドレスを書き潰して srlz.d を発行済み。
一方、 1組目の DBRのマスク指定レジスタを変更して、 「どの特権レベルでもブレークしない」と指定すると、 同じ状態でもデータブレークフォルトが発生しなくなる。
dbr[1] = 0x48ffffffffffffff // r=0, w=1, pl3 のみ許可, mask=0x00ffffffffffffff ↓ dbr[1] = 0x40ffffffffffffff // r=0, w=1, どの plでも許可しない, mask=0x00ffffffffffffff
Itanium Processor Specification には、 「IBR を書き直す場合にはいったん PSW.db=0 にしないとダメ」というエラッタが載っているのだが、 これも同種のエラッタなのかしら?
追記:2009/1/19
Itanium2 は PSW.db=1 の時は16バイト境界を越えるメモリアクセスを行なうと 常にデバッグフォルトがあがるという実装らしい。 トホホ。
7/27 (日)
[Linux] SIGSEGV シグナルをエミュレーションできないものかしら?
SIGSEGV シグナルハンドラを利用して プロセス内で独自のメモリ管理を導入しているプログラムは多いと思う。 というか、ここ数年はそういうプログラムしか書いたことがない。
問題はそういうプログラムだとテスト方法が面倒で、
「最初にページにアクセスした時」とか、
「ページが回収されて mumap された時」とかの状況を作り辛い。
そこでSIGSEGV シグナルハンドラのテストのために、
プログラムの中で SIGSEGV シグナルを意図的に生成できないものかと考える。
ぶっちゃけ
任意のsi_code
と si_addr
を持った SIGSEGV シグナルをテスト生成
できないだろうか?
raise(3)
は SIGSEGV シグナルであることしか指定できない。
sigqueue(2)
を利用すると si_code は SI_QUEUE
になる。
si_addr の方は、
siginfo_t
構造体の中で si_addr と si_int/si_ptr が偶然同じ位置を占める
アーキテクチャ以外は指定できないため、
実質的に利用不可能だ。
#include <stdio.h> #include <stdint.h> #include <signal.h> void handler(int signu, siginfo_t * info, void * data) { printf("si_code = %d si_addr = %p\n", info->si_code, info->si_addr); exit(0); } int main(int argc, char** argv) { struct sigaction act; act.sa_flags = SA_SIGINFO; act.sa_sigaction = handler; sigemptyset(&act.sa_mask); if (sigaction(SIGSEGV, &act, NULL)) { perror("sigaction"), exit(1); } #if 1 raise(SIGSEGV); #else sigval_t value; value.sival_ptr = (void*)(uintptr_t)0x0123456789ABCDEFUL; if (sigqueue(getpid(), SIGSEGV, value)) { perror("sigqueue"), exit(1); } #endif return 0; }
実際、si_code や si_addr がデタラメな値になる。 ダメみたい。
si_code = -1 si_addr = 0x1f500003d52
7/26 (金)
[MyWeb] 自サイトの DNS キャッシュ汚染の対策
世の中を賑わせている DNS キャッシュ汚染問題のチェックをしてみる。
- JVNVU#800113 複数の DNS 実装にキャッシュポイズニングの脆弱性
- /.J DNSキャッシュ汚染に関する脆弱性が公表される
- /.J DNSキャッシュポイズニング続報
- DNS-OARC DNS キャッシュサーバのチェックツール
ソースポートのランダム性に関しては POOR のようだ。 ソースポートのランダム性に関しては DNS サーバの bind 任せだから、 今のところ named.conf で設定を変更することはできないようだ。
> nslookup -q=txt porttest.dns-oarc.net ns.nminoru.jp Server: ns.nminoru.jp Address: 219.117.195.5#53 Non-authoritative answer: porttest.dns-oarc.net canonical name = z.y.x.w.v.u.t.s.r.q.p.o.n.m.l.k.j.i.h.g.f.e.d.c.b.a.pt.dns-oarc.net. z.y.x.w.v.u.t.s.r.q.p.o.n.m.l.k.j.i.h.g.f.e.d.c.b.a.pt.dns-oarc.net text = "219.117.195.5 is POOR: 26 queries in 3.3 seconds from 26 ports with std dev 105.58" Authoritative answers can be found from: z.y.x.w.v.u.t.s.r.q.p.o.n.m.l.k.j.i.h.g.f.e.d.c.b.a.pt.dns-oarc.net nameserver = ns.z.y.x.w.v.u.t.s.r.q.p.o.n.m.l.k.j.i.h.g.f.e.d.c.b.a.pt.dns-oarc.net.
とりあえず自宅のネットワークは外部/内部で別の DNS サーバを使っていて、 外部公開用の DNSサーバも recursive query を受け付けていない。 当面はよしとしよう。
7/4 (木)
[Prog] g++ で offsetof が警告に
g++ で offsetof をクラスに対して適用すると警告が表示される。
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
class T {
private:
int _field;
public:
static size_t getFieldOffset();
};
size_t T::getFieldOffset() { return offsetof(T, _field);}
offsetof.cpp: In static member function 'static size_t T::getFieldOffset()': offsetof.cpp:15: warning: invalid access to non-static data member 'T::_field' of NULL object offsetof.cpp:15: warning: (perhaps the 'offsetof' macro was used incorrectly)
そういう時は -Wno-invalid-offsetof
をつけよう。