env for FreeBSD kernel.debug

Table of Contents

minimam jail

前回jailでchrootが利用されてるか調査しようとして、jail構築の複雑さゆえ 断念しちゃった。その後jail(8)してたら、こんな例に出会った。マニュアル をタイパ良く読むコツは、コード例を拾う事です。

Start a shell in the jail:

      jail -c path=/data/jail/testjail mount.devfs \
              host.hostname=testhostname ip4.addr=192.0.2.100 \
              command=/bin/sh

そうか、コマンドラインから、jailの骨格を指定できるとな。必須は、刑務所 の所在地と、中に入って何をするかだけなんだな。ホスト名とか、ネットワー ク設定とかは、本質じゃないだろう。

こう想像すると、jail == chrootと見做せそう。

root@fb:~ # chroot /tmp/MINE
# exit
exit
root@fb:~ # jail -c path=/tmp/MINE command=/bin/csh

chrootが出来る事を確認後、jailしてみた。正に刑務所なう である。

sakae@fb:/tmp $ jls
   JID  IP Address      Hostname                      Path
     2                                                /tmp/MINE
sakae@fb:/tmp $ ps a
 PID TT  STAT    TIME COMMAND
  :
2624  1  I    0:00.00 jail -c path=/tmp/MINE command=/bin/csh

本当に、刑務所が出来たかは、外部から確認できる。

check chroot

gdbしてみます。

root@fb:~ # gdb -q jail
Reading symbols from jail...
Reading symbols from /usr/lib/debug//usr/sbin/jail.debug...
(gdb) b chroot
Function "chroot" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (chroot) pending.
(gdb) r -c path=/tmp/MINE command=/bin/csh
Starting program: /usr/sbin/jail -c path=/tmp/MINE command=/bin/csh
[Detaching after fork from child process 2654]
# exit
exit
[Inferior 1 (process 2652) exited normally]
(gdb)

引っかからないですねぇ。

root@fb:~ # ktrace jail -c path=/tmp/MINE command=/bin/csh
# exit
exit
root@fb:~ # kdump -sS >LOG
root@fb:~ # grep chroot LOG
root@fb:~ #

これにも載っていない。しょうがないので、少しLOGを偵察。

2658 jail     CALL  jail_set[507](0xffbfe890,0x6,0x1)
2658 jail     NAMI  "/tmp/MINE"
2658 jail     NAMI  "/tmp/MINE"
2658 jail     RET   jail_set[507] 4
2658 jail     CALL  cpuset_getid[486](0x1,0x2,0xffffffff,0xffffffff,0x41172c)
2658 jail     RET   cpuset_getid[486] 0
2658 jail     CALL  fork[2]
2658 jail     RET   fork[2] 2659/0xa63

pathは出てくるんだけど、command=/bin/cshは出てきていない。それにforkと 対になるexecveも無い。どうなってるの? 一応、足跡だけでも残しておく。

(gdb) bt
#0  jail_set () at jail_set.S:4
#1  0x20452b60 in jailparam_set (jp=0xffbfe920, njp=2, flags=1)
    at /usr/src/lib/libjail/jail.c:550
#2  0x0040805c in jailparam_set_note (j=0x20674000, jp=jp@entry=0xffbfe920,
    njp=2, flags=1) at /usr/src/usr.sbin/jail/jail.c:917
#3  0x00407cdf in create_jail (j=0x20674000)
    at /usr/src/usr.sbin/jail/jail.c:659
#4  0x00408843 in run_command (j=j@entry=0x20674000)
    at /usr/src/usr.sbin/jail/command.c:330
#5  0x004084e7 in next_command (j=0x20674000)
    at /usr/src/usr.sbin/jail/command.c:167
#6  0x004068e5 in main (argc=2, argv=0x0) at /usr/src/usr.sbin/jail/jail.c:449

vervoseなんてフラグがチラホラしてたんで、

root@fb:~ # jail -v -c path=/tmp/MINE command=/bin/csh
jail_set(JAIL_CREATE) persist path=/tmp/MINE
created
run command in jail: /bin/csh
# exit
exit
jail_set(JAIL_UPDATE) jid=6 nopersist

凄くマクロな説明だな。

add disk

qemuを入れて、そこでFreeBSDを動作させたい。母艦が手狭なんでOSの新規イ ンストールを兼ねて、母艦を広くするか。なんか面倒そうだな。Diskを増設し ちゃうか。やってみた。そして沢山の子分をつれて、qemuがやってきた。

Disk増設は簡単。VMwareの設定でDiskを追加。IDEとかSCSIとかから選べるけ ど、推奨はIDEとの事なんでそれにした。OSを起動したらdmesg

sakae@fb:~ $ dmesg | grep ada
Trying to mount root from ufs:/dev/ada0s1a [rw]...
ada0 at ata0 bus 0 scbus0 target 0 lun 0
ada0: <VMware Virtual IDE Hard Drive 00000001> ATA-4 device
ada0: Serial Number 00000000000000000001
ada0: 33.300MB/s transfers (UDMA2, PIO 32768bytes)
ada0: 12288MB (25165824 512 byte sectors)
ada1 at ata0 bus 0 scbus0 target 1 lun 0
ada1: <VMware Virtual IDE Hard Drive 00000001> ATA-4 device
ada1: Serial Number 01000000000000000001
ada1: 33.300MB/s transfers (UDMA2, PIO 32768bytes)
ada1: 12288MB (25165824 512 byte sectors)

母艦がada0だ。だってmountを試みているからね。するってぇと、増設した奴 は、ada1って事になる。newfs /dev/ada1 してから、/optにマウント。忘れず に/etc/fstabを更新しておこう。

母艦のDiskが本当に手狭になってしまったので、/usr/freebsd-distを、増設 Diskである/optに移動した。

FreeBSD on QEMU

現在のFreeBSDにはXを入れていないので、インストール時に必要となるvga端 末が表示できない。そこで、OpneBSD側で、FreeBSDをインストールして、その DISKを利用する事にした。

インストールがめっぽう遅い。おまけに起動すると、パニックですよ。もう一 度試してみる? 御免こうむるな。別の手を考えよう。

sakae@fb:/opt/MINE $ dd if=/dev/zero of=disk.img bs=1M count=2048
root@fb:/opt/MINE # mdconfig -a -t vnode -f disk.img -u 0
root@fb:/opt/MINE # gpart add -t freebsd -s 2000M -a 4k /dev/md0
root@fb:/opt/MINE # gpart set -a active -i 1 /dev/md0
root@fb:/opt/MINE # gpart bootcode -b /boot/boot0 /dev/md0
root@fb:/opt/MINE # gpart create -s BSD -n 20 /dev/md0s1
root@fb:/opt/MINE # gpart add -t freebsd-ufs -s 1800M /dev/md0s1
root@fb:/opt/MINE # gpart add -t freebsd-swap -s 200M /dev/md0s1
root@fb:/opt/MINE # gpart bootcode -b /boot/boot /dev/md0s1
root@fb:/opt/MINE # newfs /dev/md0s1a

こんな風に土台になるファイルを作成。それを仮想diskに見立てる。。gpart を利用して、スライスを切る。スライスはDOSのパーテションの事だ。

root@fb:/opt/MINE # mount /dev/md0s1a /mnt
root@fb:/opt/MINE # cd /mnt/
root@fb:/mnt # tar jxf /opt/freebsd-dist/base.txz
root@fb:/mnt # tar jxf /opt/freebsd-dist/kernel.txz

後は、バイナリーを展開。それから、少々強引だけど、cp /etc/{master.passwd,passwd} /mnt/etc して、userを登録。rootユーザーさ え動作すれば、後はbsdinstallで個別の設定は、どうにでもなる、かな? bsdinstallに面白いターゲットを発見(どうでもいい事ですが)。

jail destination  Sets up a new chroot system at destination, suitable
                  for use with jail(8).  Behavior is generally similar to
                  auto, except that disk partitioning and network setup
                  are skipped and a kernel is not installed into the new
                  system.

jailがすっかり根付いてますなあ。それと富豪な人向けにzfs。これソラリス 由来。暗号化も御茶の子さいさい。暗号化と言えば、KADOKAWAさんの所、攻撃 受けて、勝手に暗号化されちゃったんですかね。

ああ忘れる所だった。最後にumountした後、mdconfig -d -u 0 を実行して、 開放を忘れない事。

vi /mnt/boot/loader.conf

boot_multicols="yes"
boot_serial="yes"
comconsole_speed="9600"
console="comconsole,vidconsole"

を登録。シリアルコンソールから利用できる様に設定。これでVGAな端末と縁 が切れるはず。

qemu-system-i386 -m 256 -nographic -no-fd-bootchk -s \
  -net nic -net user,hostfwd=tcp::2022-:22  \
  -hda disk.img

起動は、こんな設定だ。

CPU: QEMU Virtual CPU version 2.5+ (2401.24-MHz 686-class CPU)
  Origin="GenuineIntel"  Id=0x663  Family=0x6  Model=0x6  Stepping=3
  Features=0x781abfd<FPU,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,PGE,CMOV,PAT,MMX,FXSR,SSE,SSE2>
  Features2=0x80000001<SSE3,HV>
  Hypervisor: Origin = "TCGTCGTCGTCG"
real memory  = 268304384 (255 MB)
avail memory = 233918464 (223 MB)
 :
ada0: <QEMU HARDDISK 2.5+> ATA-7 device
ada0: Serial Number QM00001
ada0: 16.700MB/s transfers (WDMA2, PIO 8192bytes)
ada0: 2048MB (4194304 512 byte sectors)
cd0 at ata1 bus 0 scbus1 target 0 lun 0
cd0: <QEMU QEMU DVD-ROM 2.5+> Removable CD-ROM SCSI device
cd0: Serial Number QM00003
cd0: 16.700MB/s transfers (WDMA2, ATAPI 12bytes, PIO 65534bytes)
cd0: Attempt to query device size failed: NOT READY, Medium not present

Loader variables:
  :
Manual root filesystem specification:
  <fstype>:<device> [options]
  Mount <device> using filesystem <fstype>
      and with the specified (optional) option list.                            
    eg. ufs:/dev/da0s1a
        zfs:zroot/ROOT/default
        cd9660:/dev/cd0 ro
          (which is equivalent to: mount -t cd9660 -o ro /dev/cd0 /)

  ?               List valid disk boot devices
  .               Yield 1 second (for background tasks)
  <empty line>    Abort manual input

あらら、/etc/fstabを忘れているっぽいぞ。それから、デバイス・ファイルも ね。ああ、デバイス・ファイルは自動生成してくれるからイジルナ。

root@qemu:~ # cat /etc/fstab
# Device        Mountpoint      FStype  Options Dump    Pass#
/dev/ada0s1a    /               ufs     rw      1       1
/dev/ada0s1b    none            swap    sw      0       0

これで、無事に動いた。多少のワーニングは出てくるけど、これだけ動けば、 gdbのターゲットとしては十分でしょう。

sakae@qemu:~ $ df -h
Filesystem      Size    Used   Avail Capacity  Mounted on
/dev/ada0s1a    1.7G    1.0G    570M    64%    /
devfs           1.0K      0B    1.0K     0%    /dev

portsとか余計な物を入れないと、こんなもんだ。そして、下記はtopの冒頭部 分。

last pid:   859;  load averages:  0.90,  0.55,  0.40    up 0+00:17:55  07:54:12
12 processes:  1 running, 11 sleeping
CPU:  1.1% user,  0.0% nice,  2.2% system,  1.9% interrupt, 94.8% idle
Mem: 5036K Active, 1036K Inact, 34M Wired, 17M Buf, 184M Free
Swap: 200M Total, 200M Free

256Mもメモリーが有れば余裕ですよ。まあ、余計なプロセスが動いていないか らねぇ。

それから、一応 ssh -p 2022 localhost して、qemu側のFreeBSDに接続できる 事を確認したよ。

Linux in FreeBSD

注目のリナのエミュレーション機能を調べてみる。その前に、デフォのDiskに 余裕が無いので、/usr/ports一式を、増設したDiskに引越しする事にした。 引越しは、0123にお任せじゃなくて、下記の定番な方法で実施。

root@fb:/usr # tar cf - ./ports | (cd /opt; tar xpf -)

跡地を開放してから、リンクを貼ったよ。

sakae@fb:~ $ df -h
Filesystem      Size    Used   Avail Capacity  Mounted on
/dev/ada0s1a     11G    7.1G    2.7G    72%    /
devfs           1.0K      0B    1.0K     0%    /dev
tmpfs           384M    3.6M    380M     1%    /tmp
/dev/ada1        12G    4.0G    6.7G    37%    /opt

これで少しはバランスが回復したかな。

第10章 Linux® バイナリ互換機能 リナの様に振る舞う方法が解説されてた。32Bit用には、CentOS 7が、64Bit 用には、Rocky Linux 9が提供されてるそうだ。

sakae@fb:~ $ pkg search linux-c7
linux-c7-7.9.2009_1            Meta-port for all things CentOS 7.9.2009
linux-c7-alsa-lib-1.1.8        Advanced Linux Sound Architecture libraries (Linux CentOS 7.9.2009)
 :
linux-c7-xcb-util-keysyms-0.4.0_1 Port of Xlib's XImage and XShmImage functions (Linux CentOS 7.9.2009)
linux-c7-xorg-libs-7.7_11      Xorg libraries (Linux CentOS 7.9.2009)
linux-c7-zlib-devel-1.2.7      Zlib headers (Linux CentOS 7.9.2009)

確かに提供されてけど、品揃えがX関係に偏っているぞ。無理して入れても、 持て余しそうなんで、横目で眺めるだけにする。Rockyの方は、どうなんでしょ うかね? ArchLinuxを既にインストールしてるから、入れる事は無いでしょう。

kernel.debug

qemu/FreeBSDの用意が出来たので、母艦のFreeBSDに有るkernel.debugを使っ てみる。準備として、

sakae@fb:/usr/lib/debug/boot/kernel $ cat .gdbinit
target remote :1234

を入れ、qemuのdebug支援機構とgdbが通信できる様にしておく。それでは、 qemu/FreeBSDを起動しておく。それから、母艦で

sakae@fb:/usr/lib/debug/boot/kernel $ gdb -q kernel.debug
Reading symbols from kernel.debug...
warning: remote target does not support file transfer, attempting to access files from local filesystem.
warning: Unable to find dynamic linker breakpoint function.
GDB will be unable to debug shared library initializers
and track explicitly loaded dynamic code.
acpi_cpu_c1 () at /usr/src/sys/x86/x86/cpu_machdep.c:245
245     }
(gdb) b sys_chroot
Breakpoint 1 at 0x10fd9cf: file /usr/src/sys/kern/vfs_syscalls.c, line 976.
(gdb) c
Continuing.
root@qemu:~sakae # jail -c path=/home/sakae/MINE command=/bin/csh
# exit
exit

何の反応も有りませんでした。jailでは、chrootが使用されていないと結論付 けてもいいかな。ちょいと悔しいので、同類項で試してみます。

(gdb) b sys_chdir
Breakpoint 2 at 0x10fd826: file /usr/src/sys/kern/vfs_syscalls.c, line 933.
(gdb) c
Continuing.

Breakpoint 2, sys_chdir (td=0xccefb40, uap=0xccefdd8)
    at /usr/src/sys/kern/vfs_syscalls.c:933
933             return (kern_chdir(td, uap->path, UIO_USERSPACE));
(gdb) bt
#0  sys_chdir (td=0xccefb40, uap=0xccefdd8)
    at /usr/src/sys/kern/vfs_syscalls.c:933
#1  0x0143d9bf in syscallenter (td=<optimized out>)
    at /usr/src/sys/i386/i386/../../kern/subr_syscall.c:188
#2  syscall (frame=0xcb9dce8) at /usr/src/sys/i386/i386/trap.c:1161
#3  0xffc03479 in ?? ()
#4  0x0000003b in ?? ()
#5  0x0000003b in ?? ()
#6  0x2067a020 in ?? ()
#7  0xffbfeab0 in ?? ()

ここまで調査できれば、後はユーザーランドでgdbを振り回すだけで十分だな。 たまにはOpenBSDに火を入れてみるか。

chroot/chdir

chroot /tmp/MINE を題材にカーネル側を散策する。

ob$ gdb -q bsd.gdb
Reading symbols from bsd.gdb...
acpicpu_idle () at /usr/src/sys/dev/acpi/acpicpu.c:1207
1207                    break;
(gdb) b sys_chroot
Breakpoint 1 at 0xd02fffea: file /usr/src/sys/kern/vfs_syscalls.c, line 804.
(gdb) c
Continuing.

Breakpoint 1, sys_chroot (p=0xd149d34c, v=0xf1b21134, retval=0xf1b2112c)
    at /usr/src/sys/kern/vfs_syscalls.c:804
804             } */ *uap = v;
(gdb) bt
#0  sys_chroot (p=0xd149d34c, v=0xf1b21134, retval=0xf1b2112c)
    at /usr/src/sys/kern/vfs_syscalls.c:804
#1  0xd068ba01 in mi_syscall (p=0xd149d34c, code=61, indirect=-1,
    callp=0xd0d0f3bc <sysent+732>, argp=0xf1b21134, retval=0xf1b2112c)
    at /usr/src/sys/sys/syscall_mi.h:110
#2  0xd068b604 in syscall (frame=0xf1b21170)
    at /usr/src/sys/arch/i386/i386/trap.c:574
#3  0xd071a135 in Xsyscall_untramp ()
#4  0xf1b21170 in ?? ()
#5  0x1be0bcaf in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)

gdbしてると、視野が狭くなるので、ソースを眺めつつ、これはと思う所を集 中攻撃するのが、良いと思える。いわゆる、木を見て森を見ず を避けるのが 賢明だ。

NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE,
    SCARG(uap, path), p);
if ((error = change_dir(&nd, p)) != 0)
        return (error);
if (fdp->fd_rdir != NULL) {
        /*
         * A chroot() done inside a changed root environment does
         * an automatic chdir to avoid the out-of-tree experience.
         */

例えば、こんな下りがある。NDINITってマクロだろうと想像がつく。展開して、 どうなってるか見ておきたい。だって、コードの流れとして、pathを検索して、 その結果が、ndの中に格納され、それを使って、 change_dir してると想像 できるから。

こういう時は、TAGSを利用して追跡するに限る。namei.h

#define NDINIT(ndp, op, flags, segflp, namep, p) \
        ndinitat(ndp, op, flags, segflp, AT_FDCWD, namep, p)

この関数は検索用の構造体をイニシャライズしてた。本物は

/*
 * Common routine for chroot and chdir.
 */
static int
change_dir(struct nameidata *ndp, struct proc *p)
         :
        if ((error = namei(ndp)) != 0)
                return (error);
        vp = ndp->ni_vp;

どうやらnameiが肝みたい。それから、fdpって変数も現実を反映するのに大事。 これも、 sys_chroot の冒頭付近で宣言されてる。

struct filedesc {
        struct  file **fd_ofiles;       /* [f/w,m] file structures for
                                         *     open files */
        char    *fd_ofileflags;         /* [f] per-process open file flags */
        struct  vnode *fd_cdir;         /* [K] current directory */
        struct  vnode *fd_rdir;         /* [K] root directory */
        :

プロセス毎に、chdir,rootdirを保持してるのね。vnodeってのも複雑な構造体 になってるぞ。もう完全にバイナリーな世界なんで、デコードするには、TAGS を十分に駆使して、あちことを飛び回らないといけない。正直難儀な事です。

GPT-4o

無料で使う「GPT-4o」その概要と注意点

言葉は世界を拡張するーミニ読書感想『言語の力』(ビオリカ・マリアンさん)

KADOKAWAさんの所が、復旧してなかったので、読書感想のノートを参照させて いただきます。

GPTが発達した現代。苦労して英語なんか学ぶ必要が有るんか? そんな疑問に、 バイリンガル、マルチリンガルな人の効用が解説されてます。

マルチな人は、収入も多いそうだ。言語って言ったら、C語とかも、そうだか らね。だったら、オイラーは自信を持ってマルチリンガルだな。

普通の人が毛嫌いする、括弧言語なんて、大好物だもの。

etc