鬱猫のつぶやき

鬱猫が日々思っていることをつづります

教育用RISC-V Davis In-Order CPUの論文を読む

Davis In-Order CPUというカリフォルニア大学デービス校のコンピューターアーキテクチャの授業で使われている、RISC-V ISAを実装したCPUがある。少し前に論文として上がっていたので読んでみた。

論文のPDFはこちら

カリフォルニア大学デービス校では長い間、コンピューターアーキテクチャの授業ではLogisimが使われていたが、学生から非常に不評だったとのこと。論文には、学生からのアンケートのコメントと思わしき文章が掲載されていた。

  • “there isn’t much documentation on Logisim online”
  • “I hate logisim with a passion.”

そこで、Chiselを使って設計したDINO CPUを使い、Chiselを教える講義に変更したとのこと。 そして実際に 2クォーター分講義を実施したところ、評価が高かったそうだ。

  • “Very challenging but rewarding course. Please keep using Chisel in the future!”

また、そこでChiselの知識を身に着け、企業のインターンシップに参加した学生もいたらしい。

Additionally, after teaching this course for the first time, 
one student reported that they received an internship 
offer because they had Chisel experience

講義では座学に加え、4つの課題が出され評価がくだされる。

  • ALU Control Unitの実装 (Chiselとハードウェア記述言語になれさせる目的のため)
  • アプリケーションを動かせるようにするために、CPUの全体を設計
  • パイプラインの実装
  • パイプラインに分岐予測を実装

この課題の内容はすべてGitHubに掲載されており、リポジトリをクローンすればカリフォルニア大学デービス校の学生でなくとも取り組むことができるみたいだ。

DINO CPUについては、以下の特徴を備えたCPUであるとのこと。

  • シンプルなデザインで、教育用途に作られており、わかりやすいコードを心がけて実装されている。
  • RISC-V ISA(RV32I)が実装されており、Cのプログラムをコンパイルして実行することができる
    • CSR命令 ecall ebreakは実装していない
    • 将来的にはこれらの命令をサポートすることにフォーカスするとのこと

基本的にはPatterson & HennessyRISC-V Editionに掲載されているCPUをベースに作られているが、Patterson & Hennessyの本ではRV32Iのうち命令のエンコード形式がR形式の一部しか実装されていないのに対し、DINOはそれ以外の特権命令をのぞくほぼすべての命令を実装しているのが大きな違いとなっている。

デジタル回路について予備知識がない人にもChiselが理解できるようにマテリアルを追加したということも書いてあった。Chisel Bootcampなどの資料はVerilogなどの他のHDLの知識を必要としているところがあったのは確かにそのとおり。

However, most of the Chisel documentation targets graduate 
students and professionals with architecture and digital design background. 
Therefore, we supplemented this documentation with our own slimmed down 
documentation which only contained details required for completing the DINO 
CPU assignments.

Chiselからハードウェア設計を学ぶ上で、DINO CPUは最適かもしれない。

講義ノートもすべて公開されているみたいだ。良い時代になったものだ。

Zephyr OSを使ってみた

Zephyr OSというリアルタイムOSを使うことになったので、その環境構築時の手順のメモ。

SDKを導入し、環境変数を設定する。

$ wget https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.10.1/zephyr-sdk-0.10.1-setup.run
$ ./zephyr-sdk-0.10.1-setup.run -- -d ~/zephyr-sdk-0.10.1

環境変数に設定しておくために、.bashrcに以下の設定を記入しておく。

export ZEPHYR_TOOLCHAIN_VARIANT=zephyr
export ZEPHYR_SDK_INSTALL_DIR=${HOME}/zephyr-sdk-0.10.1
$ pip3 install --user west
$ west init zephyrproject
$ cd zephyrproject
$ west update
$ sudo pip3 install --user -r zephyr/scripts/requirements.txt

ビルドを行う。

$ cd zephyr
$ source zephyr-env.sh
$ west build -b litex_vexriscv samples/subsys/shell/shell_module
-- west build: build configuration:
       source directory: /mnt/src/zephyrproject/zephyr/samples/subsys/shell/shell_module
       build directory: /mnt/src/zephyrproject/zephyr/build (created)
       BOARD: litex_vexriscv (origin: command line)
-- west build: generating a build system
Zephyr version: 1.14.99
-- Found PythonInterp: /usr/bin/python3 (found suitable version "3.6.8", minimum required is "3.4") 
-- Selected BOARD litex_vexriscv
-- Found west: /home/tsunekoh/.local/bin/west (found suitable version "0.5.7", minimum required is "0.5.6")
-- Loading /mnt/src/zephyrproject/zephyr/boards/riscv32/litex_vexriscv/litex_vexriscv.dts as base
-- Overlaying /mnt/src/zephyrproject/zephyr/dts/common/common.dts
Parsing Kconfig tree in /mnt/src/zephyrproject/zephyr/samples/subsys/shell/shell_module/Kconfig
Loaded configuration '/mnt/src/zephyrproject/zephyr/boards/riscv32/litex_vexriscv/litex_vexriscv_defconfig'
Merged configuration '/mnt/src/zephyrproject/zephyr/samples/subsys/shell/shell_module/prj.conf'
Configuration saved to '/mnt/src/zephyrproject/zephyr/build/zephyr/.config'
-- Cache files will be written to: /home/tsunekoh/.cache/zephyr
-- The C compiler identification is GNU 8.3.0
-- The CXX compiler identification is GNU 8.3.0
-- The ASM compiler identification is GNU
-- Found assembler: /mnt/src/zephyr-sdk/riscv32-zephyr-elf/bin/riscv32-zephyr-elf-gcc
-- Performing Test toolchain_is_ok
-- Performing Test toolchain_is_ok - Success
Including module: esp-idf in path: /mnt/src/zephyrproject/modules/hal/esp-idf/zephyr
Including module: fatfs in path: /mnt/src/zephyrproject/modules/fs/fatfs
Including module: qmsi in path: /mnt/src/zephyrproject/modules/hal/qmsi
Including module: cypress in path: /mnt/src/zephyrproject/modules/hal/cypress
Including module: silabs in path: /mnt/src/zephyrproject/modules/hal/silabs
Including module: st in path: /mnt/src/zephyrproject/modules/hal/st
Including module: stm32 in path: /mnt/src/zephyrproject/modules/hal/stm32
Including module: libmetal in path: /mnt/src/zephyrproject/modules/hal/libmetal
Including module: lvgl in path: /mnt/src/zephyrproject/modules/lib/gui/lvgl
Including module: mbedtls in path: /mnt/src/zephyrproject/modules/crypto/mbedtls
Including module: mcumgr in path: /mnt/src/zephyrproject/modules/lib/mcumgr
Including module: nffs in path: /mnt/src/zephyrproject/modules/fs/nffs
Including module: open-amp in path: /mnt/src/zephyrproject/modules/lib/open-amp
Including module: segger in path: /mnt/src/zephyrproject/modules/debug/segger
Including module: tinycbor in path: /mnt/src/zephyrproject/modules/lib/tinycbor
-- Configuring done
-- Generating done
-- Build files have been written to: /mnt/src/zephyrproject/zephyr/build
-- west build: building application
[1/114] Preparing syscall dependency handling

[109/114] Linking C executable zephyr/zephyr_prebuilt.elf
Memory region         Used Size  Region Size  %age Used
             RAM:       69936 B       256 MB      0.03%
        IDT_LIST:          41 B         2 KB      2.00%
[114/114] Linking C executable zephyr/zephyr.elf

ここで、dtcのコンパイラのバージョンが古く、cmakeに失敗する場合がある。その場合には、新規にdtcを入れ直す。ソースコードを落としてきて、dtcをビルドし /usr/local/bin あたりに配置すれば良い。 dtcのソースコードここからダウンロードできる。

新しいバージョンのdtcを入れたにもかかわらずビルドに失敗する場合には、zephyr sdkのdtcを参照しにいっている可能性がある。その場合には、zephyr sdkのdtcの名前を適当に変更すれば良い。

Renodeでシミュレーション

実際に起動するのかを確かめたいのだが、残念ながら実機環境を持っていない。仕方ないので、Renodeでシミュレーションできるかどうかを確かめてみる。

Renodeからリリースビルドを落としてきて、プラットフォームごとに用意されたインストーラーを起動すればインストール自体は問題なくできるはず。

ターミナルを開き、renodeを起動する。

$ cd /mnt/src/zephyrproject/zephyr/build # <-- 先程のzephyrのビルドされたものがディレクトリに移動
$ renode

ターミナルが開くので、先程のビルド時に指定したボード名を指定し、ELFをロードしシミュレーションをスタートする。

(monitor) mach create "litex-vexriscv"
(litex-vexriscv) machine LoadPlatformDescription @platforms/cpus/litex_vexriscv.rep
l
(litex-vexriscv) sysbus LoadELF @zephyr.elf
(litex-vexriscv) showAnalyzer sysbus.uart
(litex-vexriscv) start
Starting emulation...
(litex-vexriscv) 

terminalが開き、kernel versionとか叩くとzephyrのバージョンを表示することができる。

今回、RISC-V Getting Started Guildeを大いに参考にした。

spike-dasmの逆アセンブル結果がおかしい件について

risv-isa-simに付随しているspike-dasmの逆アセンブル結果におかしい点を見つけた。

$ echo "DASM(0x00000000)" | spike-dasm
c.addi4spn s0, sp, 0

と表示されるんだが、これ unimp が表示されるのが適切。 c.addi4spn に関しては即値が0以外であることが、RISC-V ISA の仕様書にも書いてある。

C.ADDI16SP adds the non-zero sign-extended 6-bit immediate to the value in the stack pointer 

ちなみに、objdump や radare2 は正しく出してくれた。

$ rasm2 -a riscv -d "0x0000" 
unimp

すでに、GitHubでもイシューとして上がっているけども、長い間修正されていないみたいだ。

spike-dasmを使う場合には注意を。

Practical Binary Analysisを読み始めた 1

先週の連休からPractical Binary Analysisを読み始めた。

www.amazon.co.jp

会社の先輩に進められて読み始めたが、なかなか良い。最初のチャプターでCのソースコードコンパイルして実行ファイルが生成されるまで書いてあるのは良かった。

その他、bdf libraryの使ってのELF/PEの自作パーサーの作成、ディスアセンブリが逆アセンブルする仕組み、コードインジェクションの手法などが書かれているみたいである。(まだ、そこまでは読んでいない。)

知っていることも多いので、拾い読みしていく予定。

hope-RIPEを使ってみる その2

昨日、hope-RIPEをSpikeシミュレーターで動かしてみたんだが、Stack Buffer Overflowを利用したROPが成功しないという不可解な現象に遭遇した。SSPが有効になっているからかと思ったけども、Makefileを見る限りではそうではないみたいだし、これは何だろうということで詳しく調べてみた。

失敗したケースのうち、以下のケースを取り上げる。

ripe_attack_generator -t direct -i rop -c ret -l stack -f memcpy

デバッガーで原因を探る

デバッガーで追ってみたところ、ropでjumpする先のアドレスを間違えていることが原因でセグフォしていることがわかった。

アセンブルの内容

0000000000010e00 <perform_attack>:
...

   11b84:       78813083                ld      ra,1928(sp) <-- ここで return address を書き換える
   11b88:       78013403                ld      s0,1920(sp) <-- ここに breakpoint を貼って、p $raしてみる
   11b8c:       79010113                addi    sp,sp,1936 
   11b90:       8082                    ret
   
0000000000011ec8 <rop_target>: // <-- jump先ターゲット
   11ec8:       1141                    addi    sp,sp,-16
   11eca:       e406                    sd      ra,8(sp)
   11ecc:       e022                    sd      s0,0(sp)
   11ece:       0800                    addi    s0,sp,16
   11ed0:       67cd                    lui     a5,0x13
   11ed2:       7d878513                addi    a0,a5,2008 # 137d8 <.annobin_elf_init.c_end+0x73e>
   11ed6:       c3bfe0ef                jal     ra,10b10 <puts@plt>
   11eda:       4501                    li      a0,0
   11edc:       c55fe0ef                jal     ra,10b30 <exit@plt>  

デバッガーの出力

(gdb) b *0x11b88
Breakpoint 1 at 0x11b88
(gdb) r
Starting program: /root/hope-RIPE/build/ripe_attack_generator -t direct -i rop -c ret -l stack -f memcpy
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.28.9000-24.fc30.riscv64
tech: 100
attack: 202
code ptr: 300
location: 400
function: 500


Breakpoint 1, 0x0000000000011b88 in perform_attack ()
(gdb) p $ra
$1 = (void (*)()) 0x11ed8 <rop_target+16>

<rop_target+16>がちゃんと命令の先頭アドレスとなっていれば良いが、そうなっていない。0x11ed8なので、jal ra,10b10という命令の"ど真ん中に"ジャンプしてしまう。で、decodeができずIllegal Instructionで落ちていることがわかった。

以下の箇所がjump先を決めているところだが、コンパイラのバージョンが変わったことにより、この16というオフセット値が変わってしまったのだと予想される。

github.com

まとめると、昨日hope-RIPEを実行してFAILと表示されたケースは、SSPなどが働いて脆弱性攻撃に失敗したというわけではなく、単純に攻撃コードのバグによって失敗しているということだった。

ということで、修正してプルリクを出しておくのが良さそうである。

hope-RIPEを試してみる その1

Runtime Intrusion Prevention Evaluator (RIPE) という、脆弱性攻撃に対する防御のカバレッジを測定するためのツールがある。これを用いると種々のパラメータを変化させながら種々の脆弱性攻撃用コードを生成、そのコードを実行し脆弱性攻撃の成否をレポートしてくれる。 RIPEのもとの論文はここからダウンロードできる。

このRIPEだが、RISC-V向けにポートしたものが昨年リリースされていたので、試してみた。hope-RIPEと呼ぶらしい。

以下では、RISC-V toolsがインストールされており、パスが通してあることを前提として話をすすめる。

build & run

基本的に普通にmakeで問題なくビルドできた。

$ git clone https://github.com/draperlaboratory/hope-RIPE.git
$ cd hope-RIPE
$ make

すべてのテストケースを実行するためにpythonスクリプトが用意されている。pythonスクリプトの中身を見ると

for attack in attacks:
        for tech in techniques:
                for loc in locations:
                        for ptr in code_ptr:
                                for func in funcs:
                                        os.system('rm -f out/out.text')
                                        cmdargs = 'build/ripe_attack_generator ' + '-t ' + tech + ' -i ' + attack + ' -c ' + ptr + ' -l ' + loc + ' -f ' + func
                                        cmdline= run_cmd + ' ' + cmdargs + ' 1> out/out.text 2>/dev/null'

                                        if is_attack_possible (attack, tech, loc, ptr, func) == 0:
                                                total_np += 1

となっており、要はすべてのパラメータの組み合わせについてRIPEを実行するだけとなっている。

テストを実行した結果は以下の通り。

...

('build/ripe_attack_generator -t direct -i rop -c longjmpdata -l data -f memcpy', 0)
('build/ripe_attack_generator -t direct -i dataonly -c bof -l stack -f memcpy', 1)
('build/ripe_attack_generator -t direct -i dataonly -c iof -l stack -f memcpy', 1)
('build/ripe_attack_generator -t direct -i dataonly -c leak -l stack -f memcpy', 0)
('build/ripe_attack_generator -t direct -i dataonly -c bof -l heap -f memcpy', 1)
('build/ripe_attack_generator -t direct -i dataonly -c iof -l heap -f memcpy', 1)
('build/ripe_attack_generator -t direct -i dataonly -c leak -l heap -f memcpy', 0)
('build/ripe_attack_generator -t direct -i dataonly -c bof -l bss -f memcpy', 1)
('build/ripe_attack_generator -t direct -i dataonly -c iof -l bss -f memcpy', 1)
('build/ripe_attack_generator -t direct -i dataonly -c leak -l bss -f memcpy', 0)
('build/ripe_attack_generator -t direct -i dataonly -c bof -l data -f memcpy', 1)
('build/ripe_attack_generator -t direct -i dataonly -c iof -l data -f memcpy', 1)
('build/ripe_attack_generator -t direct -i dataonly -c leak -l data -f memcpy', 0)
('build/ripe_attack_generator -t indirect -i dataonly -c bof -l stack -f memcpy', 1)
('build/ripe_attack_generator -t indirect -i dataonly -c bof -l bss -f memcpy', 1)
('build/ripe_attack_generator -t indirect -i dataonly -c bof -l data -f memcpy', 1)
SUMMARY
----------------------------------------------------------------
Total OK: 123
Total FAIL: 57
Total Attacks Executed: 180

OKは攻撃に成功したことを示し、FAILは攻撃に失敗したことを示す。攻撃に成功した場合には、標準出力に success. **** function reached. というメッセージが表示される(****には攻撃の手法の名前が入る)。

57件FAILしているわけだが、どういうことなのか。。。SSPによって守られているとかかな?

気になってMakefileを見てみると、

# Makefile for RIPE
# @author John Wilander & Nick Nikiforakis
# Modified for RISC-V by John Merrill

#Depending on how you test your system you may want to comment, or uncomment
#the following
CFLAGS= -fno-stack-protector -z execstack
CC=riscv64-unknown-elf-gcc

all: ripe_attack_generator

clean:
        rm -rf build/ out/

ripe_attack_generator: ./source/ripe_attack_generator.c
        mkdir -p build/ out/
        $(CC) \
                ./source/ripe_attack_generator.c -o ./build/ripe_attack_generator

となっており、バッチリ-fno-stack-protectorが入っている。

FAILしている原因調査は後日行うこととするが、ひとまずFAILしていたテストケースとして何があったかだけ調べたので、以下に掲載しておく。

パット見、direct rop stack ret memcpyとかで失敗しているが、このケースでの失敗はちょっと考えにくい。RISC-Vのポーティングを間違えている可能性が高そう。

direct shellcode funcptrbss bss memcpy
direct shellcode structfuncptrbss bss memcpy
direct shellcode longjmpbss bss memcpy
direct shellcode funcptrdata data memcpy
direct shellcode structfuncptrdata data memcpy
direct shellcode longjmpdata data memcpy
indirect shellcode ret bss memcpy
indirect shellcode funcptrstackvar bss memcpy
indirect shellcode funcptrstackparam bss memcpy
indirect shellcode funcptrheap bss memcpy
indirect shellcode funcptrbss bss memcpy
indirect shellcode funcptrdata bss memcpy
indirect shellcode structfuncptrstack bss memcpy
indirect shellcode structfuncptrheap bss memcpy
indirect shellcode structfuncptrdata bss memcpy
indirect shellcode structfuncptrbss bss memcpy
indirect shellcode longjmpstackvar bss memcpy
indirect shellcode longjmpstackparam bss memcpy
indirect shellcode longjmpheap bss memcpy
indirect shellcode longjmpdata bss memcpy
indirect shellcode longjmpbss bss memcpy
indirect shellcode ret data memcpy
indirect shellcode funcptrstackvar data memcpy
indirect shellcode funcptrstackparam data memcpy
indirect shellcode funcptrheap data memcpy
indirect shellcode funcptrbss data memcpy
indirect shellcode funcptrdata data memcpy
indirect shellcode structfuncptrstack data memcpy
indirect shellcode structfuncptrheap data memcpy
indirect shellcode structfuncptrdata data memcpy
indirect shellcode structfuncptrbss data memcpy
indirect shellcode longjmpstackvar data memcpy
indirect shellcode longjmpstackparam data memcpy
indirect shellcode longjmpheap data memcpy
indirect shellcode longjmpdata data memcpy
indirect shellcode longjmpbss data memcpy
indirect returnintolibc structfuncptrbss bss memcpy
indirect returnintolibc structfuncptrdata data memcpy
direct rop ret stack memcpy
direct rop funcptrstackvar stack memcpy
direct rop funcptrstackparam stack memcpy
direct rop structfuncptrstack stack memcpy
direct rop longjmpstackvar stack memcpy
direct rop longjmpstackparam stack memcpy
direct rop funcptrheap heap memcpy
direct rop structfuncptrheap heap memcpy
direct rop longjmpheap heap memcpy
direct rop funcptrbss bss memcpy
direct rop structfuncptrbss bss memcpy
direct rop longjmpbss bss memcpy
direct rop funcptrdata data memcpy
direct rop structfuncptrdata data memcpy
direct rop longjmpdata data memcpy
direct dataonly leak stack memcpy
direct dataonly leak heap memcpy
direct dataonly leak bss memcpy
direct dataonly leak data memcpy

『うつを甘く見ていました』を読んだ。

鬱病のこともそうだが、それよりも結婚するとか家族になるってどういうことだろうということの方を考えさせる内容だった。

漫画の内容に関して、旦那さんの非難が集まっていたらしいが、正直そこまで責めるべきでもないような気がする。(筆者もあとがきで夫を非難する内容にしたわけではないと書いてはいるのだけども。)

楽しい時をシェアできるよりも辛い場合にしっかり支え合えるのかとか、そういうことが結婚する相手に求められるべきなんだなと思った。

https://www.amazon.co.jp/dp/B07GSSHLCC/