めもめも

このブログに記載の内容は個人の見解であり、必ずしも所属組織の立場、戦略、意見を代表するものではありません。

vermagic と modversions に関する覚え書き

カーネルモジュールをロードする際の実行中カーネルに対する整合性チェックの仕組みには、vermagic と modversions の 2 種類があります。

太古の昔は・・・

古いカーネルでは、モジュールをコンパイルする際に使ったカーネルソースのバージョン・ストリング(include/linux/version.h)を見て、ロード可能なカーネルを決定していたような記憶がかすかに・・・。これは、安全性と柔軟性の両面において『帯に短し、たすきに長し』でした。

・安全にロードできるカーネルを厳密にチェックするという意味では、不十分です。(同じバージョン名でも、カーネルの Config オプションが異なる可能性などを排除できない。)

・複数バージョンのカーネルにロードできるモジュールを作れないので、もちろん柔軟性に欠けています。

その後、安全サイドをより強固にサポートする vermagic と、柔軟サイドをサポートする modversions の 2 種類の仕組みが追加されました。

vermagic

カーネルソースの include/linux/vermagic.h にカーネルバージョンに加えて、主要な Config オプションが記載されます。モジュールをコンパイルする際にこの情報がモジュールにリンクされます。

vermagic チェックが有効な状態で、モジュールをロードしようとすると、モジュールに含まれる vermagic 情報と実行中のカーネルの vermagic 情報が比較されます。これが一致しないとロードに失敗して、下記のエラーが出力されます。

Error inserting 'hogehoge.ko': -1 Invalid module format

modversions

CONFIG_MODVERSIONS を有効にしてカーネルをコンパイルすると、カーネルがモジュールに公開するシンボル名(モジュールから呼び出し可能なカーネル内部の関数名など)について、個々に特殊な『チェックサム』が付与されます。

このチェックサムは、関数の引数のタイプなどから計算されるもので、関数の仕様が変化するとそれにあわせてチェックサムも変化することになります。つまり、

・カーネルのバージョンが変わってもある関数のチェックサムが変化していなければ、その関数の仕様は変化しておらず、以前のバージョンのカーネルと同様に呼び出しても問題ないはず。

という判断ができます。

モジュールのコンパイル時に、モジュールが利用する全てのシンボルに対して、コンパイルに使用したカーネルが持つ該当シンボルのチェックサムがモジュール内に記録されます。

モジュールに記録されたチェックサムは次のコマンドで確認できます。

# modprobe --dump-modversions /lib/modules/2.6.32-71.el6.x86_64/kernel/drivers/scsi/ips.ko
0x14522340      module_layout
0xc917223d      pci_bus_read_config_byte
0x2d5528c9      sg_copy_to_buffer
0x349cba85      strchr
0xa19d6a63      scsi_host_alloc
0x25ec1b28      strlen
0x492ff334      scsi_add_host_with_dma
0xd2037915      dev_set_drvdata
0xfa2e111f      slab_buffer_size
0x71f6eb38      sg_copy_from_buffer
...

一方、それぞれのカーネルが持つシンボルのチェックサムは、カーネルソースの Module.symvers に記録されています。下記の例では、strchr のチェックサムがちゃんと上記と一致していますね。

# grep strchr /lib/modules/2.6.32-71.el6.x86_64/build/Module.symvers
0x349cba85      strchr  vmlinux EXPORT_SYMBOL

※ 実行中のカーネルの symvers をダイレクトに見る方法ってありましたっけ?

vermagic が無効化されていて、かつ modversions が有効化されている場合は、ロードしようとするモジュールに含まれる各シンボルのチェックサムと実行中のカーネルに含まれる該当シンボルのチェックサムが比較されます。チェックサムが異なるシンボルがあるとロードに失敗して、"disagrees about version of symbol HOGEHOGE" のようなエラーが出力されます。

vermagic と modversions の有効/無効

RHEL5/6 を含む多くのディストリビューションのカーネルでは、CONFIG_MODVERSIONS が有効化されています。これらでは、ロードするモジュールに含まれるシンボルがチェックサムを持っている場合は、vermagic は無視されて、modverions によるチェックのみが行われます。ただし、モジュールにチェックサムが含まれていない場合は、vermagic による確認が行われます。

modprobe に次のオプションを指定するとこれらのチェックを無効化します。

・--force-modversion → modversions によるチェックを無効化
・--force-vermagic → vermagic によるチェックを無効化
・-f → 両方を無効化

※ insmod の -f がどちらを無効化するのか確認できていません。。。。

余談ですが、Rusty Russel と Linus のこんなやりとりも発見。http://www.gossamer-threads.com/lists/linux/kernel/918204

RHEL5/6 固有の工夫

RHEL5/6 には、modversions をより有効に活用するための独自の工夫があります。

参考:RHEL5 Driver Update Program Technical Whitepaper

(1) カーネルのマイナーバージョンアップでシンボルのチェックサムが変化しないように気をつけながら、カーネルメンテナンスを行う。

RHEL に含まれるカーネルは、Red Hat 独自のパッチが当たっていますが、これらのパッチにより、RHEL カーネルのマイナーバージョンアップで、シンボルのチェックサム(つまり外部から見える関数の仕様、すなわち Kernel ABI - Kernel Application Binary Interface)をできるだけ変更しないようにメンテナンスされています。つまり、特定のカーネルバージョンに対してコンパイルしたモジュールは、複数のマイナーバージョンで共通に使えます。

RHEL のカーネル RPM に同梱のモジュールは、基本的には、該当のカーネルソースの下でコンパイルされていますが、サードベンダが独自のモジュールを公開する際に、複数のマイナーバージョンで利用可能なモジュールを提供できるようにとの配慮によるものです。

(2) 主要なシンボルをいくつかのグループに分けて、各グループごとの独自のチェックサム(グループ・チェックサム)を計算している。

サードベンダが独自のモジュールをパッケージングする際に、Red Hat が提供するツール(kmodtool)を利用すると、このグループごとのチェックサムがカーネルモジュールを含む RPM パッケージに埋め込まれます。このグループ・チェックサムは、RPM パッケージの --requires セクションによる依存関係として設定されます。したがって、必要な条件を満たすカーネルが導入されていないと、RPM パッケージの導入に失敗します。

$ rpm -qp --requires rpm/RPMS/x86_64/kmod-redhat-example-2.0-0.x86_64.rpm
rpmlib(VersionedDependencies) <= 3.0.3-1
redhat-example-kmod-common >= 1.0
/sbin/depmod
/sbin/depmod
/bin/sh
/bin/sh
/bin/sh
rpmlib(PayloadFilesHavePrefix) <= 4.0-1
rpmlib(CompressedFileNames) <= 3.0.4-1
kernel(rhel5_kernel_ga) = 80eb4f3bb28f48a6c959596f7dd1bf8bc3526b1a ← これ。

一方、複数バージョンのカーネルがインストールされている環境でこの RPM をインストールすると、このモジュールが利用可能なカーネルを modversions のチェックサムから自動的に判断して、該当するカーネルの /lib/modules/ ディレクトリにモジュールが配置されます。つまり、複数バージョンのカーネルに対するモジュールのインストール作業も自動化されます。

正確には、次のようなモジュール配置になります。

・実際にそのモジュールがコンパイルされたバージョンの /lib/modules//extra/ 以下にモジュールが配置される。
・このモジュールを利用可能な他のバージョンの /lib/modules//weak-updates/ 以下からシンボリックリンクが張られる。

また、新しいカーネル RPM を追加導入した際は、既存の /extra/ 以下のモジュールで利用可能ものに対しては、新しいカーネルの /weak-updates/ 以下からのシンボリックリンクが自動作成されます。

一般に、サードベンダが提供するカーネルモジュールは、カーネルをアップデートするごとに再コンパイルが必要でしたが、この仕組みに乗っかって提供されるモジュールに関しては、基本的には再コンパイルが不要になります。(Red Hat がチェックサムが変わるようなカーネルの仕様変更をしないかぎり。)

※ kmodtool は このあたりとか、このあたりも参考になります

カーネルモジュールへの署名について

RHEL5/6 では、モジュールをロードする際のチェックがもう一つあります。RHEL5/6 のカーネル RPM に同梱のモジュールは、Red Hat による署名が含まれており、他のバージョンのカーネルではロードすることができません。かならず、該当カーネルに同梱のモジュールを使用する必要があります。

Red Hat のサポート外になることを理解した上で、強制的に他のバージョンでロードする場合は、モジュールから署名を削除します。

# objcopy -R .module_sig hogehoge.ko hogehoge-nosig.ko

詳細は、こちら の『カーネルモジュールローディング』を参照してください。

(おまけ)Kernel2.4 における modverions の実装

Kernel 2.4 で最初に modversions が導入された時は、シンボル名そのものにチェックサムをくっつけて、コンパイル時にシンボル名を書き換えるという大胆なことをやっていました。

参考: http://www.skynet.ie/~mark/home/kernel/symbols.html

こうすることで、チェックサムが異なるとシンボル名そのものが変わってしまうので、チェックサムが一致しないカーネルにモジュールをロードしようとすると、シンボル名が見つからないというエラーでロードに失敗します。また、insmod に -f を指定して vermagic チェックを無効化してもシンボル名そのものが異なるので、モジュールを強制ロードすることも不可能です。