「車輪の再実装」って言葉が好き(実践はできてない)

git bisectを用いてsyscallテーブルの表記変更コミットを探す

linuxシステムコールを追加しようと思ったりすると、 arch/x86/entry/syscalls/syscall_64.tbl にsys*と書くと思っていたのですが、最近は__x64_sys*という表記に変わっていることに気づきました。

例えばLinux v4.15ではシステムコールテーブルは以下の様になっています。

# 64-bit system call numbers and entry vectors
#
# The format is:
# <number> <abi> <name> <entry point>
#
# The abi is "common", "64" or "x32" for this file.
#
0   common  read            sys_read
1   common  write           sys_write
2   common  open            sys_open
3   common  close           sys_close
4   common  stat            sys_newstat
5   common  fstat           sys_newfstat
6   common  lstat           sys_newlstat
7   common  poll            sys_poll
8   common  lseek           sys_lseek
9   common  mmap            sys_mmap
10  common  mprotect        sys_mprotect

ただ最近(v5.0)このテーブルを見直したら以下のように変更されていました。

#
# 64-bit system call numbers and entry vectors
#
# The format is:
# <number> <abi> <name> <entry point>
#
# The __x64_sys_*() stubs are created on-the-fly for sys_*() system calls
#
# The abi is "common", "64" or "x32" for this file.
#
0   common  read            __x64_sys_read
1   common  write           __x64_sys_write
2   common  open            __x64_sys_open
3   common  close           __x64_sys_close
4   common  stat            __x64_sys_newstat
5   common  fstat           __x64_sys_newfstat
6   common  lstat           __x64_sys_newlstat
7   common  poll            __x64_sys_poll
8   common  lseek           __x64_sys_lseek
9   common  mmap            __x64_sys_mmap
10  common  mprotect        __x64_sys_mprotect

git bisect というコマンドを思い出し、良い練習問題だと思って調べてみることにしました。

manのドキュメントの言葉を借りると、git bisectは

git-bisect - Use binary search to find the commit that introduced a bug

すなわち、ある段階まで動いていたコードにどの段階でバグが入り込んだかを調べる機能です。

テストスクリプトを走らせて*12分探索で該当コミットを探してくれるので、一つずつさかのぼって確認するのと比べ、高速で探索が可能です。 今回はバグではありませんが、以下のテストスクリプト(test.sh)を使い、__x64という表記が混入する場所を探ることにしました。

#!/bin/sh
set -eu

egrep "\ssys_open" arch/x86/entry/syscalls/syscall_64.tbl

git bisect ではまず、該当コミットがある範囲を決めます。

git bisect run <bad/new-commit> <good/old-commit>

今回、v5.0ではx64_sys*という表記となり、v4.15ではsys*という表記であることがわかっているので、以下のように実行しました。

git bisect start v5.0 v4.15

Bisecting: 44096 revisions left to test after this (roughly 16 steps)
[93b9bcdf9fbcb683d4e8c44ee8cec0989053d4de] btrfs: remove unused parameter from btrfs_parse_subvol_options

「だいたい16ステップで終わるよ」と教えてくれるのは便利。

次に、実際に探索を行いたいと思います。

git bisect run ./test.sh
running ./test.sh
Bisecting: 22106 revisions left to test after this (roughly 15 steps)
[38047d5c269bbdedf900fc86954913f3dffa01f1] Merge tag 'driver-core-4.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core
running ./test.sh
2   common  open            sys_open
257 common  openat          sys_openat
304 common  open_by_handle_at   sys_open_by_handle_at
Bisecting: 10700 revisions left to test after this (roughly 14 steps)
[135c5504a600ff9b06e321694fbcac78a9530cd4] Merge tag 'drm-next-2018-06-06-1' of git://anongit.freedesktop.org/drm/drm
running ./test.sh
Bisecting: 5696 revisions left to test after this (roughly 13 steps)
[a72db42cee37a43f8a40e1f47358ac86921ad8e4] Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
running ./test.sh
Bisecting: 2942 revisions left to test after this (roughly 12 steps)
[28da7be5ebc096ada5e6bc526c623bdd8c47800a] Merge tag 'mailbox-v4.17' of git://git.linaro.org/landing-teams/working/fujitsu/integration
running ./test.sh
2   common  open            sys_open
257 common  openat          sys_openat
304 common  open_by_handle_at   sys_open_by_handle_at
Bisecting: 1471 revisions left to test after this (roughly 11 steps)
[5504ed29692faad06ea74c4275e96a8ffc83a1e1] mm/hmm: do not differentiate between empty entry or missing directory
running ./test.sh
2   common  open            sys_open
257 common  openat          sys_openat
304 common  open_by_handle_at   sys_open_by_handle_at
Bisecting: 733 revisions left to test after this (roughly 10 steps)
[80a17a5f501ea048d86f81d629c94062b76610d4] Merge tag 'apparmor-pr-2018-04-10' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor
running ./test.sh
2   common  open            sys_open
257 common  openat          sys_openat
304 common  open_by_handle_at   sys_open_by_handle_at
Bisecting: 373 revisions left to test after this (roughly 9 steps)
[ba2b137d10bafc3cc514e52172b549e64a5402fb] Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux
running ./test.sh
2   common  open            sys_open
257 common  openat          sys_openat
304 common  open_by_handle_at   sys_open_by_handle_at
Bisecting: 193 revisions left to test after this (roughly 8 steps)
[f0d98d85831bf1a3b1f56f8c14af60797aaca536] Merge tag 'scsi-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
running ./test.sh
Bisecting: 71 revisions left to test after this (roughly 7 steps)
[174e719439b8224d7cedfbdd9529de396cac01ff] Merge branch 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
running ./test.sh
2   common  open            sys_open
257 common  openat          sys_openat
304 common  open_by_handle_at   sys_open_by_handle_at
Bisecting: 30 revisions left to test after this (roughly 5 steps)
[9fb71c2f230df44bdd237e9a4457849a3909017d] Merge branch 'x86-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
running ./test.sh
Bisecting: 18 revisions left to test after this (roughly 4 steps)
[6b0a02e86c293c32a50d49b33a1f04420585d40b] Merge branch 'x86-pti-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
running ./test.sh
2   common  open            sys_open
257 common  openat          sys_openat
304 common  open_by_handle_at   sys_open_by_handle_at
Bisecting: 9 revisions left to test after this (roughly 3 steps)
[5ac9efa3c50d7caff9f3933bb8a3ad1139d92d92] syscalls/core, syscalls/x86: Clean up compat syscall stub naming convention
running ./test.sh
2   common  open            sys_open
257 common  openat          sys_openat
304 common  open_by_handle_at   sys_open_by_handle_at
Bisecting: 4 revisions left to test after this (roughly 2 steps)
[d94a155c59c98c19b98ee949eaab6a0312bbd6be] x86/cpu: Prevent cpuinfo_x86::x86_phys_bits adjustment corruption
running ./test.sh
2   common  open            sys_open
257 common  openat          sys_openat
304 common  open_by_handle_at   sys_open_by_handle_at
Bisecting: 2 revisions left to test after this (roughly 1 step)
[c76fc98260751e71c884dc1a18a07e427ef033b5] syscalls/x86: Adapt syscall_wrapper.h to the new syscall stub naming convention
running ./test.sh
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[d5a00528b58cdb2c71206e18bd021e34c4eab878] syscalls/core, syscalls/x86: Rename struct pt_regs-based sys_*() to __x64_sys_*()
running ./test.sh
d5a00528b58cdb2c71206e18bd021e34c4eab878 is the first bad commit
commit d5a00528b58cdb2c71206e18bd021e34c4eab878
Author: Dominik Brodowski <linux@dominikbrodowski.net>
Date:   Mon Apr 9 12:51:44 2018 +0200

    syscalls/core, syscalls/x86: Rename struct pt_regs-based sys_*() to __x64_sys_*()
    
    This rename allows us to have a coherent syscall stub naming convention on
    64-bit x86 (0xffffffff prefix removed):
    
     810f0af0 t            kernel_waitid    # common (32/64) kernel helper
    
     <inline>            __do_sys_waitid    # inlined helper doing actual work
     810f0be0 t          __se_sys_waitid    # C func calling inlined helper
    
     <inline>     __do_compat_sys_waitid    # inlined helper doing actual work
     810f0d80 t   __se_compat_sys_waitid    # compat C func calling inlined helper
    
     810f2080 T         __x64_sys_waitid    # x64 64-bit-ptregs -> C stub
     810f20b0 T        __ia32_sys_waitid    # ia32 32-bit-ptregs -> C stub[*]
     810f2470 T __ia32_compat_sys_waitid    # ia32 32-bit-ptregs -> compat C stub
     810f2490 T  __x32_compat_sys_waitid    # x32 64-bit-ptregs -> compat C stub
    
        [*] This stub is unused, as the syscall table links
            __ia32_compat_sys_waitid instead of __ia32_sys_waitid as we need
            a compat variant here.
    
    Suggested-by: Ingo Molnar <mingo@kernel.org>
    Signed-off-by: Dominik Brodowski <linux@dominikbrodowski.net>
    Cc: Al Viro <viro@zeniv.linux.org.uk>
    Cc: Andrew Morton <akpm@linux-foundation.org>
    Cc: Andy Lutomirski <luto@kernel.org>
    Cc: Borislav Petkov <bp@alien8.de>
    Cc: Brian Gerst <brgerst@gmail.com>
    Cc: Denys Vlasenko <dvlasenk@redhat.com>
    Cc: Josh Poimboeuf <jpoimboe@redhat.com>
    Cc: Linus Torvalds <torvalds@linux-foundation.org>
    Cc: Peter Zijlstra <peterz@infradead.org>
    Cc: Thomas Gleixner <tglx@linutronix.de>
    Link: http://lkml.kernel.org/r/20180409105145.5364-4-linux@dominikbrodowski.net
    Signed-off-by: Ingo Molnar <mingo@kernel.org>

:040000 040000 3f963b85ebd00a03e36f36f6417684a64da0b8dc e83d401c538f34cb271d6c0ae3c8563f43db6a5f M  arch
bisect run success

うまく行きました。

これで、ちょうど”バグ”が入り込んだコミット。 すなわちここでは_x64_sys*という表記に変わったコミットにチェックアウトした状態となっています。 git diff HEAD~とすると

前略
-0      common  read                    sys_read
-1      common  write                   sys_write
-2      common  open                    sys_open
-3      common  close                   sys_close
-4      common  stat                    sys_newstat
-5      common  fstat                   sys_newfstat
中略
+0      common  read                    __x64_sys_read
+1      common  write                   __x64_sys_write
+2      common  open                    __x64_sys_open
+3      common  close                   __x64_sys_close
+4      common  stat                    __x64_sys_newstat
+5      common  fstat                   __x64_sys_newfstat
後略

となっていたので、このコミット(d5a00528b58cdb2c71206e18bd021e34c4eab878)で間違いなさそうです。

さて、このコミットがどのバージョンかですが、 git merge-base --is-ancestor v4.16 HEAD に成功して、git merge-base --is-ancestor v4.17 HEADに失敗したのでv4.17から変更が入っていそうです。

おわり

*1:このコミットは良い、悪いをスクリプトではなく手動で指定することもできそうです