web-dev-qa-db-ja.com

Linuxで一見使用できないメモリマッピングの目的は何ですか?

すべてのプロセスには、読み取りも書き込みも実行もできないプライベートメモリマッピングがあるようです(フラグは「--- p」です)。

grep -- --- /proc/self/maps
7f2bd9bf7000-7f2bd9df6000 ---p 001be000 fc:00 3733                       /lib/x86_64-linux-gnu/libc-2.19.so
7f2bd9e04000-7f2bda003000 ---p 00003000 fc:00 3743                       /lib/x86_64-linux-gnu/libdl-2.19.so
7f2bda042000-7f2bda241000 ---p 0003d000 fc:00 36067                      /lib/x86_64-linux-gnu/libpcre.so.3.13.1

共有ライブラリでいくつかを返し、Java(JVM)プロセスに対してそれを行うと、数百メガバイトの数十の匿名マッピングも返されます。

編集:これらのマッピングがプレースホルダーである場合、イベントや他のアクティビティから保護されるこれらの保持された場所を使用するのは誰ですか?言い換えると、これらのプレースホルダーが存在しない場合に発生する可能性のある間違った動作は何ですか?

2番目の編集:共有ライブラリのこれらの穴がコンパイラやダイナミックリンカの目的に実際に役立つことを考えると、JVMプロセスに表示される匿名マッピングのこれらの穴には他の目的が必要です。 TomcatJVMプロセスの匿名マッピングをサイズでソートしました。

 20 MB  00007FA0AAB52000-00007FA0AC000000 ---p 00000000 00:00 0
 41 MB  00007FA0B1603000-00007FA0B4000000 ---p 00000000 00:00 0
 50 MB  00007FA090D04000-00007FA094000000 ---p 00000000 00:00 0
 53 MB  00007FA0F8A40000-00007FA0FC000000 ---p 00000000 00:00 0
 61 MB  00007FA0C42C5000-00007FA0C8000000 ---p 00000000 00:00 0
 61 MB  00007FA0CC29A000-00007FA0D0000000 ---p 00000000 00:00 0
 61 MB  00007FA0D0293000-00007FA0D4000000 ---p 00000000 00:00 0
 62 MB  00007FA0D814C000-00007FA0DC000000 ---p 00000000 00:00 0
 62 MB  00007FA0E017E000-00007FA0E4000000 ---p 00000000 00:00 0
 63 MB  00007FA0B803B000-00007FA0BC000000 ---p 00000000 00:00 0
 63 MB  00007FA0BC021000-00007FA0C0000000 ---p 00000000 00:00 0
 63 MB  00007FA0C0021000-00007FA0C4000000 ---p 00000000 00:00 0
 63 MB  00007FA0D4075000-00007FA0D8000000 ---p 00000000 00:00 0
 63 MB  00007FA0DC040000-00007FA0E0000000 ---p 00000000 00:00 0
 63 MB  00007FA0E4067000-00007FA0E8000000 ---p 00000000 00:00 0
189 MB  00007FA0EC300000-00007FA0F8000000 ---p 00000000 00:00 0
1008 MB  0000000100FF5000-0000000140000000 ---p 00000000 00:00 0
6
Juergen

Null権限を持つ2044KBの2つのメモリ領域があることに注意してください。前述のように、ELFの「実行ビュー」は実行可能バイナリをメモリにロードする方法に関係しています。 ld.soがダイナミックライブラリを取り込むと、LOADとラベル付けされたセグメントを調べます(readelf -a xxx.soコマンドの「ProgramHeaders」と「SectiontoSegmentmapping」を見てください)。通常、2つのLOADセグメントがあります。 2つのセグメントの間に「穴」があるため(これら2つのセグメントのVirtAddrとMemSizを見てください)、ld.soはこの穴に意図的にアクセスできないようにします。elf/ dl-load.cの_dl_map_object_from_fdでPROT_NONEシンボルを探します。

http://www.cs.stevens.edu/~jschauma/810/elf.html

また、straceを使用して、これがmprotect呼び出しとして発生していることを簡単に確認できます。 strace -f grep -- . /proc/self/maps 2>&1 |less

open("/lib64/libpcre.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\300\25\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=471728, ...}) = 0
mmap(NULL, 2564360, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f0e3ad2a000
mprotect(0x7f0e3ad9c000, 2093056, PROT_NONE) = 0
mmap(0x7f0e3af9b000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x71000) = 0x7f0e3af9b000
close(3)                                = 0

Githubにはglibcリポジトリのミラーがあるため、PROT_NONEを検索するのは難しくありませんでした...

/ *この実装では、(dl-unmap-segments.hの_dl_unmap_segmentsの対応する実装と同様に)共有オブジェクトは常にすべてのセグメントが隣接して(またはページ全体を予約することが望ましいほど小さいギャップで)レイアウトされていると想定しています。アドレス空間のそれらの部分の他の使用を許可するのではなく、PROT_NONEマッピングとのギャップの内側)。 * /

https://github.com/bminor/glibc/blob/73dfd088936b9237599e4ab737c7ae2ea7d710e1/elf/dl-map-segments.h#L21

/ * _dl_map_segmentsは、セグメント間のギャップにあるページ全体がPROT_NONEマッピングで埋められるようにします。したがって、1回の急降下で範囲全体のマップを解除できます。 * /

https://github.com/bminor/glibc/blob/73dfd088936b9237599e4ab737c7ae2ea7d710e1/elf/dl-unmap-segments.h#L25

Java

OpenJDK ...は、PROT_NONEマッピングを使用して、コミットされていないアドレス空間を予約します(必要に応じて、mprotect呼び出しでコミットされます)。

自然な仮定は、何らかの理由で連続したヒープメモリが必要であるということです。

実際に必要になるまで、PROT_NONEを使用してスペースを予約します。このコメントの元のコンテキストは、Linuxに関する議論ですVMオーバーコミット:アクセスできないマッピングを使用すると、カーネルが構成されている場合に、カーネルからのコミットメントが不要になります(マッピングが必要になり、アクセス可能になるまで) 厳密なコミットモード の場合。

JVMのコンテキストで事前にこの予約を行う必要がある理由がわからない場合は、 [〜#〜] jni [〜#〜] または同等のものを使用してリンクされたネイティブコードも考慮してください。 mmapを使用している。

https://lwn.net/Articles/627728/

6
sourcejedi