Skip to content

制限 ​

paludarium は、まだ若いプロジェクトです。次のことは、今はできないか、確かめていません。

できないこと ​

  • 動的リンクのプログラム:静的にリンクしたプログラムだけを動かせます。
  • 32-bit の x86、AVX、AVX-512、x87、MMX:対象外です。
  • ネットワーク:許可の一覧(--allow-net、Config::with_network、ブラウザでは 中継 の要る JavaScript の network の指定)がなければ、 socket() はどのファミリーにも EAFNOSUPPORT を返します。一覧があっても、使えるのは TCP のクライアント、ゲスト自身のソケットのための TCP と UDP のサーバー、名前解決だけです (ネットワーク)。
    • ゲストがサーバーになれるのは、ループバックの上で自身のソケットに対してだけです (ループバックのサーバー)。ホストもほかのマシンも 接続できず、bind が取るのはループバックのアドレスと未指定のアドレスだけです (ほかは EADDRNOTAVAIL。Linux ではマシンのどのアドレスも取れます)。
    • ループバックでは、閉じた接続は TIME_WAIT を残さず、ポートはすぐに空きます。 Linux では、先に閉じた側のポートを 60 秒持ちます(SO_REUSEADDR がなければ EADDRINUSE)。待ち受けの列がいっぱいのときの接続は、accept が空きを作るまで 待ちます。Linux のクライアントは 1、3、7…秒後に試し直し、最後は ETIMEDOUT で あきらめます。SO_REUSEPORT でポートを共有する待ち受けは接続を分け合わず、 すべて最初の待ち受けに届きます。バイトはすぐに届くので、0 でない SO_LINGER の 時間で close が待つことはありません。
    • UDP のソケットが届くのは、DNS の代わりと、ループバックの上のゲスト自身のソケット だけです。ほかのアドレスへのデータグラムは ENETUNREACH で、ほかへ接続した ソケットはポートを取りません(Linux は取ります)。データグラムは、そのバイトと 768 バイトを SO_RCVBUF から取ります。Linux は sk_buff の分を数えるので、 いっぱいになって捨てる点が違います。マルチキャストとブロードキャストの データグラムは、どこにも届きません。raw ソケットは EPERM、ICMP のソケットは EACCES、ほかの種類(Linux がモジュールを読み込んで作る SCTP の SOCK_SEQPACKET など)は ESOCKTNOSUPPORT です。
    • socket(AF_UNIX) はこれまでどおり EAFNOSUPPORT で、ソケットのペアへの connect と bind は ENOSYS です。
    • DNS の代わりが答えるのは A と AAAA だけで、EDNS は読まないので、データグラムの 答えは 512 バイトまでです。ホストが引けない名前は SERVFAIL です。53 番ポートへの TCP の接続は、いつも DNS の代わりに届き、本物の DNS サーバーには届きません。
    • ホストを通した接続が開く前の getsockname は、bind したアドレス、なければ 未指定のアドレスとポート 0 を返します。Linux では取ったポートを返します。 bind したポートはホストに届かず、ホストの接続は自分のポートから出ます。SIOCOUTQ は、相手がまだ受け取りを確かめていない分では なく、ホストがまだ送りに取っていない分を数えます。SO_SNDBUF と SO_RCVBUF は getsockopt のために覚えますが、バッファの大きさは変えません(どちらの向きも 256 KiB です)。TCP_NODELAY 以外のオプションはホストのソケットに届きません。
    • MSG_OOB は緊急データを見つけず(EINVAL)、SIGURG も来ません。
  • ファイルのマッピング:ファイルのマッピングは Linux と同じくファイルのページ キャッシュを見せます(MAP_PRIVATE のものは、まだ書いていないページで)。ただし、 MAP_SHARED のマッピングへの書き込みがホストのファイルに届くのは、ゲストが そのファイルを読むか stat したとき、msync か fsync を呼んだとき、ファイルの最後のマッピングがなくなった ときだけです(Linux は自分の都合で書き戻します)。また、ホストのほかのプログラムが ファイルを変えても、マッピングには届きません。Windows のホストでは、O_APPEND で 開いたディスクリプタから作った共有マッピングへの書き込みは、ファイルに届きません。 マッピングしたファイルの終わり(その先のページは SIGBUS になります)は、ゲスト自身の ファイルの操作で決まる大きさです。ホストのプログラムが大きさを変えても、終わりは 動きません。
  • 一部のシステムコール:実装していないシステムコールは ENOSYS を返すので、 プログラムによってはその機能なしで続けられます。
  • ファイルの持ち主:マウントしたホストのディレクトリのファイルは root のままなので、 そこで別の持ち主やグループに chown すると EPERM を返します。set-user-ID や set-group-ID のプログラムを実行しても、ゲストは root のままです。Linux なら ファイルの持ち主やグループになります。
  • ソケットのメッセージ:sendmsg と recvmsg は、Linux と同じように SCM_RIGHTS でディスクリプタを、SCM_CREDENTIALS で資格情報を受け渡します。 違いは次のとおりです。
    • ゲストは root なので、SCM_CREDENTIALS にはどのプロセス、ユーザー、グループでも 指定できます。Linux では、特権のないプロセスが送れるのは自分のものだけです。
    • 読まれていないディスクリプタが、そのディスクリプタしか開いたままにしていない ソケット(たとえば自分自身)の中で待っていると、そのソケットは閉じられません。 Linux ではガベージコレクタが両方を閉じます。
  • ソケットのオプション:setsockopt は、効き目を再現していないオプションを ENOPROTOOPT で断ります。Linux は受け付けます。SO_PASSSEC、SO_TIMESTAMPING と、 フィルターなど Linux にある ほかのオプションです。SO_RCVTIMEO と SO_SNDTIMEO は 1000 Hz の刻みに丸めます。 Linux は自分の CONFIG_HZ に丸めます。SO_SNDBUF と SO_RCVBUF は値を 返しますが、組のバッファは変わりません。バッファはデータのバイト数だけを数えます。Linux は メッセージや分けた分ごとの付帯分も数えるので、小さなメッセージは Linux より多く入ってから いっぱいになります。SIOCOUTQ も、相手がまだ読んでいないバイト数だけを数えます。 ゲストは root なので、CAP_NET_ADMIN の 要るオプション(SO_DEBUG、SO_SNDBUFFORCE など)も設定でき、SO_PEERCRED の uid と gid は 0 です。
  • シグナル駆動の入出力:O_ASYNC が効くのはパイプとソケットだけで、標準入力や端末は SIGIO を送らず、FIOASYNC で付けようとすると ENOTTY です。Linux では送ります。
  • 名前空間:名前空間のフラグ(CLONE_NEWNS、CLONE_NEWUTS、CLONE_NEWIPC、 CLONE_NEWUSER、CLONE_NEWPID、CLONE_NEWNET、CLONE_NEWCGROUP、clone3 では CLONE_NEWTIME も)を付けた clone と clone3 は、子を作らずに EINVAL で失敗します。 Linux では、root で動くゲストは名前空間を作れます。unshare と setns は ENOSYS を 返します。
  • Linux と macOS 以外のホストでマウントしたディレクトリのファイル:SEEK_DATA や SEEK_HOLE の lseek はファイル全体をデータとして扱います。Linux では穴を 見つけます。Windows では st_blocks も大きさを 512 バイトのブロックに切り上げた 値で、Linux ではホストのファイルシステムが確保した分を数えます。ほかの Unix の ホストは自分の値を返します。Linux と macOS のホストでは、どちらもホストから得ます。 FIOQSIZE はどのホストでも st_blocks をバイトにした値です。Linux ではファイルシステムが その時点で確保した分で、遅延割り当ての ext4 や overlayfs では st_blocks より遅れて 数えます。
  • ブロックの大きさ:st_blksize、stx_blksize、FIGETBSZ は、どのファイルでも 4096 です。Linux ではファイルのあるファイルシステムのブロックの大きさで、/proc と 端末(devpts)は 1024、マウントしたディレクトリはホストのファイルシステムのもの (たいていは 4096)です。マウントしたディレクトリの statfs は、ここでもホストの値を 返します。ブロックの大きさのないファイルシステム(多くのコンテナの overlayfs など) では、Linux の FIGETBSZ は EINVAL です。パイプ、ソケット、memfd などのメモリの 上のファイルは、Linux でも 4096 です。
  • Windows の標準入力:Windows のホストは入力が届いているかを見分けられないので、 poll・select・epoll はホストの標準入力をいつも読めると見ます。Linux と macOS の ホストと WebAssembly では、入力があるときか終わったときだけ読めます。
  • ホストの標準入出力への FIONREAD と SIOCOUTQ:どちらも ENOTTY を返します。 Linux では、その先にあるものについて答えます。FIONREAD はパイプ、端末、ソケット、 通常のファイルについて、SIOCOUTQ は端末とソケットについてです。ゲスト自身のパイプ、 ソケット、通常のファイルは Linux と同じに数えます。
  • コアファイル:コアは、カーネルの既定の core_pattern(作業ディレクトリの core)と同じように書きます。コアをクラッシュの処理系(systemd-coredump、apport) に渡すホストの設定は再現しません。コアにはレジスタ、致命的なシグナル、補助ベクトル、 メモリが入りますが、Linux が書くノートのすべて(NT_FILE、NT_X86_XSTATE)は 入りません。また、無名のマッピングはすべて入れます。Linux は一度も書かれていない ものを入れません。
  • メモリの上限:上限を超える書き込みをしたプロセスが終わります。Linux の OOM killer は、いちばんメモリを使っているプロセスを選びます。
  • ネイティブの JIT:JIT があるのは WebAssembly 版だけです。ネイティブ版は インタプリタで動きます。
  • stable の Rust:nightly の Rust が必要です。

確かめていないこと ​

  • 確かめたのは、対応している機能 に挙げたプログラムと、命令ごとのテストです。 ほかのプログラムが完全に同じように動くことは、まだ保証していません。
  • ファジングは、決まった時間の中で問題が見つからなかったことを示すもので、どんな入力でも 安全だという証明ではありません。
  • 差分テストの基準を取った CPU は、記録してある一部の AMD と Intel の CPU です。 まだ観測していない CPU のプロファイルでは、テストがわざと止まるようにしてあります。
  • メモリ、タスク(スレッドとプロセス)、開けるファイルの数は、埋め込む側が上限を 指定しない限り無制限です。上限がなければ、信頼できないプログラムはホストのメモリを 使い切るおそれがあります。メモリの上限が数えるのはゲストのページで、ファイルの 中身、パイプやソケットのバッファ、エミュレーター自身のメモリは数えません(コマンドライン、Rust、 JavaScript)。
  • Safari の自動テストは、ブラウザの起動を待つ間に時間切れになることがありました。 対策を入れて、10 回続けて成功していますが、今後揺れないことの保証ではありません。

不具合を見つけたら ​

CPU のフォールトでゲストが終わったときは、シグナル、アドレス、命令のバイト列が 表示されます。--report-enosys を付けて動かし直すと、ENOSYS を返したシステムコールも わかります(コマンドライン)。それらを添えて、 Issue で知らせてください。

Apache-2.0 ライセンスで公開しています。