Ubuntu Weekly Recipe

第443回 再起動なしにカーネルを更新する「Canonical Livepatch Service」

この記事を読むのに必要な時間:およそ 7 分

自分でLivepatchモジュールをビルドしてみる

Livepatch機能そのものはオリジナルのカーネルに存在する機能です。このため,セキュリティ対応やCanonicalのLivepatchサービスとは関係なく,Livepatch対応のモジュールを作ることが可能です。

特定の関数をフックするモジュール

Livepatch機能はカーネルの関数トレーシング機能CONFIG_FTRACEを利用して実装しています。この機能を使うと,特定の関数の呼び出しをフックして別の関数を呼び出すことが可能です。カーネルのソースコードにはLivepatch機能のサンプルとして,fs/proc/cmdline.ccmdline_proc_show()をフックするlivepatch-sample.cが存在します。cmdline_proc_show()をフックすれば,/proc/cmdlineの出力結果を変更できます。

そこでこのサンプルコードをビルドしてみましょう。まずカーネルモジュールのビルドのために,カーネルヘッダーパッケージとカーネルビルドに必要なパッケージをインストールします※3)⁠

※3
今回はaptbuild-depサブコマンドを使ってLinuxカーネルをビルドするためのパッケージ一式をインストールしていますが,モジュールをビルドするだけならgccmakeパッケージだけをインストールすれば事足りるはずです。
$ sudo apt install linux-headers-`uname -r`
$ sudo apt build-dep linux
$ mkdir livepatch && cd $_
$ wget http://kernel.ubuntu.com/git/ubuntu/ubuntu-xenial.git/plain/samples/livepatch/livepatch-sample.c

モジュールビルド用にKbuildとMakefileを作りましょう。

(Kbuildの内容)
obj-m := livepatch-sample.o
(Makefileの内容)
KDIR ?= /lib/modules/`uname -r`/build

default:
    $(MAKE) -C $(KDIR) M=$$PWD

あとはmakeするだけです。

$ make
$ ls
Kbuild    Module.symvers  livepatch-sample.c   livepatch-sample.mod.c  livepatch-sample.o
Makefile  built-in.o      livepatch-sample.ko  livepatch-sample.mod.o  modules.order

ビルドしたlivepatch-sample.koをロードして,その前後の/proc/cmdlineの結果を比較してみましょう。

$ cat /proc/cmdline
BOOT_IMAGE=/boot/vmlinuz-4.4.0-43-generic.efi.signed (略)
$ sudo insmod livepatch-sample.ko
$ cat /proc/cmdline
this has been live patched
$ echo 0 | sudo tee /sys/kernel/livepatch/livepatch_sample/enable
$ cat /proc/cmdline
BOOT_IMAGE=/boot/vmlinuz-4.4.0-43-generic.efi.signed (略)

/proc/cmdlineの出力結果がlivepatch-sample.cで指定した「this has been live patched」に変わっていることがわかります。さらにsysfs経由で当該モジュールを無効化すれば,/proc/cmdlineの出力結果も元に戻っています※4)⁠

※4
Canonical Livepatch Serviceでロードしたパッチモジュールに対してsysfsで無効化すると,パッチに関連する処理を行っている時にカーネルパニックするようです。具体的な原因は不明ですが,Livepatch Service由来のモジュールについてはサービスを停止する形で無効化するようにしましょう。

差分ファイルからパッチモジュールを作る

前項では特定の関数を完全に置き換えるパッチモジュールを作成しました。しかしながら脆弱性対応という観点から言えば,既存の処理を少しだけ修正した形でモジュールを置き換えるほうが一般的です。つまり修正用の差分ファイルからパッチモジュールを作ると便利です。Red Hatが開発しているkpatchシステムは,カーネルに対するパッチファイルからパッチモジュールを作成する仕組みです。UbuntuではこのkpatchをUbuntuカーネルの流儀にあわせて変更を加えた上でパッケージングしています。

kpatchの一部であるkpatch-buildをインストールすれば,前述のDirty COW対応モジュールのようなパッチモジュールを簡単に作ることが可能です※5)⁠Canonical Livepatch Serviceも,基本的にこの方法で作ったカーネルモジュールを配布しています※6)⁠そこでLivepatch Serviceで使われているカーネルへのパッチのコードリポジトリを利用して,Dirty COW対応のパッチモジュールを作ってみましょう。

※5
手順は簡単ですが,カーネルをビルドできるぐらいのマシンスペックは必要です。
※6
ただkpatchで作るだけでなく,セキュアブート用にモジュールへの署名も行っています。当然のことながら,この署名用の秘密鍵などは非公開なので,Livepatch Serviceと完全に同じモジュールを作れるわけではありません。
$ sudo apt build-dep linux
$ sudo apt install kpatch-build
$ git clone https://git.launchpad.net/~ubuntu-livepatch/+git/xenial-livepatches
$ cd xeinali-livepatches/Ubuntu-4.4.0-43.63
$ cp Ubuntu-4.4.0-43.63.diff livepatch-Ubuntu-4.4.0-43.63-generic_13.diff

kpatch-buildコマンドは内部でカーネルをビルドするため,前項と異なり今回はカーネルビルドに必要なパッケージをすべてインストールしておく必要があります。また本記事執筆時点での最新コミットであるcommit:fa0c02eb4では,上記の最後のコマンドのように差分ファイルの名前を変えないといけませんでした。

あとはmakeを実行すれば,カーネルのソースコードなどをダウンロードして,内部でkpatch-buildを実行してくれます。かなり時間がかかりますので,気長に待ちましょう。

$ make
dget -u https://launchpad.net/ubuntu/+archive/primary/+files/linux_4.4.0-43.63.dsc
(中略)
wget -q https://launchpad.net/ubuntu/+archive/primary/+files/linux-image-4.4.0-43-generic-dbgsym_4.4.0-43.63_amd64.ddeb
(中略)
+ rm -rf /tmp/tmp.NlCVnRvZeW/buildroot
+ unset KCFLAGS
echo "Build complete for 4.4.0-43.63 generic amd64 revision 13"
Build complete for 4.4.0-43.63 generic amd64 revision 13
$ ls
Makefile                 linux-image-4.4.0-43-generic-dbgsym_4.4.0-43.63_amd64.ddeb  livepatch-Ubuntu-4.4.0-43.63-generic_13.diff
Ubuntu-4.4.0-43.63.diff  linux_4.4.0-43.63.diff.gz                                   livepatch_Ubuntu_4_4_0_43_63_generic_13.ko
linux-4.4.0              linux_4.4.0-43.63.dsc                                       vmlinux
linux-4.4.0.config       linux_4.4.0.orig.tar.gz

上記のうちlivepatch_Ubuntu_4_4_0_43_63_generic_13.koが,パッチモジュールです。あとはこれをロードすれば,Livepatch Serviceの時と同様にDirty COWの問題を防ぐことができます。

著者プロフィール

柴田充也(しばたみつや)

Ubuntu Japanese Team Member株式会社 創夢所属。数年前にLaunchpad上でStellariumの翻訳をしたことがきっかけで,Ubuntuの翻訳にも関わるようになりました。