NetBSD+qemuでPaXフラグまわりの設定を行う


[NetBSD Advent Calendar 2020](https://qiita.com/advent-calendar/2020/netbsd) 7日目の記事です。
今日はNetBSD上で[qemu](https://www.qemu.org/)を動かす際にハマった個所があったので、メモも兼ねて記事にしておこうと思います。

## 背景

ソースコードからビルドした[qemu](https://www.qemu.org/)をNetBSDで動かそうとした際、以下のようなエラーに引っかかってしまいました。ググってみたところ、どうやら[PaX](https://ja.wikipedia.org/wiki/PaX)まわりの設定により、エラーとなっているようです。

```sh
$ /opt/qemu-5.0.1/bin/qemu-system-x86_64 
Could not allocate dynamic translator buffer
```

## PaX

NetBSDにおける[PaX](https://ja.wikipedia.org/wiki/PaX)はメモリに関する脆弱性緩和(exploit mitigation)機能であり、[security(7)](https://man.netbsd.org/security.7)によると以下の3つが利用可能となっています。

 * PaX ASLR (Address Space Layout Randomization).
 * PaX MPROTECT (mprotect(2) restrictions)
 * PaX SegvGuard

## ktrussでエラーが発生するまでをトレースする

[ktruss(1)](https://man.netbsd.org/ktruss.1)で[qemu](https://www.qemu.org/)を起動してからエラーになるまでの流れをトレースしてみます。
どうやらエラーは[mmap(2)](https://man.netbsd.org/mmap.2)を呼び出した際に発生しており、 `EACCESS` が返されているようです。


```sh
$ ktruss /opt/qemu-5.0.1/bin/x86_64-softmmu/qemu-system-x86_64
...中略...
   738      1 qemu-system-x86_ mmap(0, 0x40000000, 0x7, 0x1002, 0xffffffff, 0, 0) Err#13 EACCES
   738      1 qemu-system-x86_ write(0x2, 0x747369b7da00, 0x2d) = 45
       "Could not allocate dynamic translator buffer\n"
```

[mmap(2)](https://man.netbsd.org/mmap.2)のマニュアルの"ERRORS"の節には以下の説明があり、どうやらメモリの読み書きや属性に関する要因により発生するエラーのようです。

```sh
$ man 2 mmap
...中略...
ERRORS
     mmap() will fail if:

     [EACCES]           The flag PROT_READ was specified as part of the prot
                        parameter and fd was not open for reading.

                        The flags MAP_SHARED and PROT_WRITE were specified as
                        part of the flags and prot parameters and fd was not
                        open for writing.

                        PaX mprotect restrictions prohibit the requested pro-
                        tection.
```

`PROT_READ` や `MAP_SHARED` 、 `PROT_WRITE` のフラグの扱いが原因という可能性もありますが、qemuの処理的にメモリの読み書きをしないというのは考えにくいため、残りの "PaX mprotect restrictions" の可能性から調べてみます。

## paxctl

[PaX](https://ja.wikipedia.org/wiki/PaX)まわりの属性を操作するためのコマンドとして、[paxctl(8)](https://man.netbsd.org/paxctl.8)が提供されています。マニュアルを見ると、[mprotect(2)](https://man.netbsd.org/mprotect.2)まわりで付与されている制限は `m` オプションで無効化できるようです。

```sh
$ man 8 paxctl
NAME
     paxctl -- list and modify PaX flags associated with an ELF program

SYNOPSIS
     paxctl flags program ...
...中略...
     m     Explicitly disable PaX MPROTECT (mprotect(2) restrictions) for
           program.
```

さっそく試してみます。 `sudo paxctl +m <実行バイナリ>` で[mprotect(2)](https://man.netbsd.org/mprotect.2)まわりの制限(というかPaXフラグ)を落とします。

```sh
$ sudo paxctl /opt/qemu-5.0.1/bin/x86_64-softmmu/qemu-system-x86_64
No PaX flags.
$
$ # PaX MPROTECTフラグを落とす(無効化する)。
$ # (メッセージなどは出力されず、そのままコマンドが終了してプロンプトが返される)
$ sudo paxctl +m /opt/qemu-5.0.1/bin/x86_64-softmmu/qemu-system-x86_64
$
$ sudo paxctl /opt/qemu-5.0.1/bin/x86_64-softmmu/qemu-system-x86_64
PaX flags:
  m: mprotect(2) restrictions, explicit disabl
```

この設定により、[qemu](https://www.qemu.org/)が起動するようになりました。ひとまずこれで問題解決です。

```sh
$ # qemuが起動するようになった！(^_^)
$ /opt/qemu-5.0.1/bin/x86_64-softmmu/qemu-system-x86_64
VNC server running on ::1:5900
```

## その他：パッケージのqemuではダメなの？

じつはこの問題を調査する前に、パッケージでインストールした[qemu](https://www.qemu.org/)を利用しようとしていました。ところが私の環境ではなぜかSEGVで[qemu](https://www.qemu.org/)が落ちてしまうという状態になっており、ソースからビルドしたものを利用していました。
パッケージ版の[qemu](https://www.qemu.org/)は上記の設定でもSEGVになってしまうため、こちらはもう少し調査が必要そうです。

```sh
$ # 設定可能なPaXフラグ全てを落としてもSEGVになってしまう...。
$ sudo paxctl +a /usr/pkg/bin/qemu-system-x86_64
$ sudo paxctl +g /usr/pkg/bin/qemu-system-x86_64
$ sudo paxctl +m /usr/pkg/bin/qemu-system-x86_64
$
$ sudo paxctl /usr/pkg/bin/qemu-system-x86_64 
PaX flags:
  a: ASLR, explicit disable
  g: Segvguard, explicit disable
  m: mprotect(2) restrictions, explicit disabl
$
$ /usr/pkg/bin/qemu-system-x86_64 
Segmentation fault (コアダンプ)
$
$ ktruss /usr/pkg/bin/qemu-system-x86_64
...中略...
  1284      1 qemu-system-x86_ mmap(0, 0x9000, 0x3, 0x1002, 0xffffffff, 0, 0) = 0x7f7ff7edc000
  1284      1 qemu-system-x86_ SIGSEGV SIG_DFL
```

## まとめ

NetBSDで[qemu](https://www.qemu.org/)を動かす際に[PaX](https://ja.wikipedia.org/wiki/PaX)フラグの設定周りでハマってしまった話を紹介しました。発生するエラーと対応方法が分かってしまえば何ということはありませんが、同様の問題に遭遇してしまった人の参考になればと思います。

## 参考URL

 * [qemu](https://www.qemu.org/)
 * [PaX](https://ja.wikipedia.org/wiki/PaX)
 * NetBSDマニュアル
   * [ktruss(1)](https://man.netbsd.org/ktruss.1)
   * [mmap(2)](https://man.netbsd.org/mmap.2)
   * [mprotect(2)](https://man.netbsd.org/mprotect.2)
   * [security(7)](https://man.netbsd.org/security.7)
   * [paxctl(8)](https://man.netbsd.org/paxctl.8)