見出し画像

人間 Cコンパイラコンテスト?

今日(2023/09/30) 15:20 から、「人間 Cコンパイラコンテスト」なるものが開催されるらしい。

Home | HCCC

面白そうなのでルールなどを確認したところ、多くの問題点がありそうだった。
そこで、気付いた点をここで紹介する。


ルールの気になる点

「RISC-V」の扱いがわからない

Regulationでは、対象アーキテクチャについて以下のように書かれている。

ii. ABIの仕様はSystem V Application Binary Interfaceの仕様に従う.
iii. 対象アーキテクチャはx86_64とRISC-Vとする.

さらに、Q&Aにも以下のように書かれている。

C99, X86_64, RISC-Vを採用しています.ABIの仕様はAMD64の仕様に従います.

直感的には、「RISC-V」は最近流行っているオープンソースアーキテクチャのことであると推測できる。
Specifications – RISC-V International
しかし、このアーキテクチャには基礎だけで

  • 32bit (RV32I)

  • 64bit (RV64I)

  • 128bit (RV128I)

  • 32bitサブセット (RV32E)

という種類が存在し、「RISC-V」だけではどれを指しているのかわからない。
全部に対応しろ、もしくは問題によって指定される、ということなのだろうか?

さらに、ABIの仕様はAMD64の仕様に従うことになっている。
この仕様書を読むと、%rax などの(少なくとも標準の)RISC-Vには存在しないであろうレジスタが登場する。
どうやって従えばよいのだろうか?
RISC-VではABIは利用しない、ということだろうか?

Regulationには「ジャッジは以下の環境で行われる.」として、「uname -a」の出力および lld、gcc、as、ld のバージョンが掲載されている。
uname は x86_64 であることを示しており、RISC-Vに関する情報はここでも得られなそうである。

コンパイルエラー誤検知のペナルティが不明瞭

Regulationでは、以下のように書かれている。

iv. 誤答のうちコンパイルできるはずのものにコンパイルエラーを出した場合(WCとなった場合),以後は提出が不可能となる.

「以降が提出が不可能となる」と書かれており、この提出の対象は明示されていない。
通常はWCを出した問題に対する提出のみが不可能になりそうだと推測できるが、このコンテストの全ての問題に対する提出が一切不可能になる可能性も否定できない。

使用が禁止される「ツール」の定義が不明瞭

Regulationでは、以下のように書かれている。

競技者は特に以下のことを禁止される.

1. 既存のコンパイラを使用すること.
2. その他既存のツールを使用すること.
3. コンパイラやツールを自作して使用すること.

ここで使用が禁止される「ツール」とは、どのようなものだろうか?

Wikipediaなどを参照すると、コンピュータを利用する直接の目的となるアプリケーションソフトウェアではなく、特定の機能を持ち、コンピュータの管理や作業の補助を行うユーティリティソフトウェアを指すようである。
例として、デバッガ、アーカイバ、ファイル間の差異を検出するソフトウェア、バイナリエディタ、時計などが挙げられている。
Vectorの「ユーティリティ」カテゴリを見てみると、ヒントになるかもしれない気がする。

とはいえ、この定義が大会のルールで採用されているとは限らず、別の定義の可能性もある。

なお、「参加条件」に

当日はSECCONCONイベント内のZoomで進行を行う

と書かれているが、

Zoomとは、世界中でNO1のシェアを誇るWeb会議ツールです。

Zoomを今からはじめるなら必見!基本の使い方や注意点を画像付きで解説 | Zoom活用ノウハウ

と、Zoomを「ツール」として紹介しているサイトもあり、Zoomも使用禁止対象の「ツール」に入る可能性も考えられる。

制約違反の提出の結果が不明瞭

Regulationには、

ジャッジシステムはテストケースを与えて判定を行ったり問題につけられた制約を確認したりする.

とあり、さらにジャッジシステムが返す結果として以下が挙げられている。

AC(Accepted): 正答.
WA(Wrong Answer): テストケースの出力が正しくない.
WC(Wrong Compile error): 本来正しいプログラムにコンパイルエラーを出した.
AE(Assembler Error): アセンブラのエラー.
LE(Linker Error): リンカのエラー.
RE(Runtime Error): 実行時のエラー.
TLE(Time Limit Exceeded): 実行制限時間オーバー.
Pending: ジャッジ待ち.
SystemError: 上記以外のエラー.運営によるリジャッジが行われる可能性がある.

「問題につけられた制約を確認」という操作が入るようだが、ここで制約に違反していると判定された場合にどのような結果になるかがわからない。
また、WCとは逆に「本来コンパイルエラーになるプログラムをアセンブリに変換した」場合の結果もわからない。
これらの場合、テストケースは関係ないだろうからWAではないだろうし、アセンブラ・リンカ・実行にも渡さないだろうからAE・LE・RE・TLEでもないだろう。
「上記以外のエラー」であるSystemErrorになるのだろうか?

許可と禁止のどっちが優先されるかが不明瞭

Regulationの「許可事項」には

3. 指定された検証環境を使ってローカルでデバッグを行う.
4. 検証環境内での生成した実行ファイルに対するgdbの使用.

などが挙げられている。一方、「禁止事項」には

1. 既存のコンパイラを使用すること.
2. その他既存のツールを使用すること.

が挙げられている。
gdbはデバッガであり、前述の通り「ツール」にはデバッガが含まれる可能性がある。

さて、許可と禁止のどっちが優先だろうか?
すなわち、「禁止事項にあっても許可事項にあればOK」なのか、「許可事項にあっても禁止事項にあればNG」なのか?
たとえばApacheのアクセス制御では、「Order Allow,Deny」(拒否優先) か、「Order Deny,Allow」(許可優先) なのかを指定できる。
指定できるということは、逆に言えば指定しなければわからないということである。
他にも、たとえば「許可/禁止にかかわらず、先に指定(マッチ)した項目を優先」という仕様も考えられる。

Webページの閲覧が禁止、しかもQ&Aで唐突に

「Q&A」に、以下の記述がある。

? コンテスト中にWebページを閲覧しても良いですか?

C99規格書, System V Application Binaryを除き禁止しています.

「禁止しています」とのことだが、Regulationにはそのような記述は見当たらない。
すなわち、Regulationに無いルールをQ&Aでいきなり追加しているということになる。
しかも、Webページの閲覧は普通に行いそうなことなので、Regulationだけを見てQ&Aを見ていないと気付かずに違反してしまう可能性がある。

さらに、禁止からの除外の対象に競技システム(ジャッジシステム)やその他回答(原文ママ)の提出のためのサイトは含まれていない。
したがって、これらのサイトの閲覧も禁止されるということである。
禁止されるのは閲覧だけのようなので、たとえばcurlを用いて閲覧せずに提出を行うのは禁止されないかもしれない。(curlが使用が禁止される「既存のツール」に含まれないのであれば)

もちろん、競技システム(ジャッジシステム)や提出方法がそもそもWebページではない、という可能性がある。
Regulationには「回答が正しいかどうかはジャッジシステムで行う.」と書かれているだけで、そのジャッジシステムが具体的にどのようなものでどのようにアクセスするかは書かれていない。
たとえばZoomのチャットで提出し、結果はチャットや運営から配信される映像で返される、という可能性も考えられる。

ちなみに、チュートリアルには

競技中に参照してもOKです.

と書かれている。
ここは閲覧の禁止から除外されるWebページには含まれず、Webページとしての閲覧は禁止されると考えられるので、参照したい場合は事前に git clone や Download ZIP などで保存しておくのがよいだろう。

やっていいことが不明瞭

たとえば、「許可事項」に「指定された検証環境を使ってローカルでデバッグを行う.」があるが、ローカル以外 (たとえばAWS EC2に立てたインスタンス上) でデバッグを行うのは許可される/禁止されないだろうか?

Q&AによればWebページの閲覧は禁止とのことだが、Webページ以外の資料、たとえば

  • 紙の参考書

  • ローカルに事前に保存した規格書のPDF

  • Webページを事前にWebブラウザの機能でHTML形式でローカルに保存したもの

  • インターネットから入手できるPDFファイルやWebページを事前に紙に印刷したもの

などの閲覧は許可される/禁止されないだろうか?

ルール(Regulation)に無い「Webページの閲覧は禁止」という条件がQ&Aで追加されたので、他にも隠れた禁止事項がある可能性が少なくなく感じる。

悪意のあるコードの提出は禁止ではない?

Regulationの「禁止事項」には、

5. 回答にジャッジサーバを攻撃するような処理を埋め込むこと.

が挙げられている。一方、Q&Aには、

? 悪意のあるコードを提出しても良いですか?

勘弁してください

とある。
Q&AではWebページの閲覧は「禁止しています」という表現だったが、ここではそうではない。
よって、禁止ではない可能性が考えられる。

「ジャッジサーバを攻撃するような処理」を埋め込むことは禁止だが、悪意はジャッジサーバへの攻撃とは限らず、第三者のサーバへの攻撃や閲覧者に対する誹謗中傷なども考えられる。

とはいえ、もちろん、犯罪やその他社会通念上ダメと考えられることはやらないべきである。

詳細のルールが確認できなそう

Q&Aに、以下の記述がある。

? 詳細のルールはどこから確認できますか?

こちらのリンクからご確認ください.

しかし、リンク先にはほとんど同じルールしか書かれておらず、詳細とはいえなそうである。
以下の情報が追加されているが、ルールとしてはあまり役立たなそうである。

  • SECCONCONのリンク

  • チュートリアル

  • 当日の流れ

  • Director

ただし、1件だけルールとして重要そうな情報として、許可事項の「人間による恣意的な最適化」が一部の問題では明示的に許可されない場合がある、ということが書かれていた。

さらに、こちらにはFAQが載っていない。
したがって、(ほとんどの)Webページの閲覧が禁止という情報も載っておらず、ルールとして情報が足りていない。

他のファイル、コミットメッセージ、Issues、Pull Requests も確認したが、詳細なルールは発見できなかった。
もしかしたらコミットの内容に隠れているかもしれないが、そこまで頑張って探さないと見つからないというのは不便であり不親切でありよくない。
「ご確認ください」というからには、確認するわかりやすい方法を用意するべきだと思う。

検証環境の気になる点

検証環境の Dockerfile が
GitHub - HumanCCompilerContest/HCCC_local_env
で公開されており、実行用のコマンドもここに載っている。

asm2bin はコンパイラである

検証環境では、以下の記述により asm2bin コマンドが追加されている。

RUN echo "alias asm2bin=\"f(){ \gcc \\\$1 -O0 -g -o tmp && ./tmp; }; f\"" >> $HOME/.bashrc

このコマンドはgccを呼び出して入力を処理し、その後処理結果を実行している。
試しにC言語のソースコードを入力として与えると、普通に実行できた。

root@f9b8479c6f05  ~
$ cat > kk.c
#include <stdio.h>

int main(void) {
        int i, j;
        for (i = 1; i <= 9; i++) {
                for (j = 1; j <= 9; j++) {
                        printf("%3d", i * j);
                }
                putchar('\n');
        }
        return 0;
}
root@f9b8479c6f05  ~
$ asm2bin kk.c
  1  2  3  4  5  6  7  8  9
  2  4  6  8 10 12 14 16 18
  3  6  9 12 15 18 21 24 27
  4  8 12 16 20 24 28 32 36
  5 10 15 20 25 30 35 40 45
  6 12 18 24 30 36 42 48 54
  7 14 21 28 35 42 49 56 63
  8 16 24 32 40 48 56 64 72
  9 18 27 36 45 54 63 72 81

さらに、「検証環境内での生成した実行ファイルに対するgdbの使用」は許可されている。
gdbを用いると、逆アセンブルによりアセンブリコードを入手することが可能である。

$ gdb ./tmp
GNU gdb (Debian 10.1-1.7) 10.1.90.20210103-git
(中略)
Reading symbols from ./tmp...
gdb-peda$ x/200i main
   0x401136 <main>:     push   rbp
   0x401137 <main+1>:   mov    rbp,rsp
   0x40113a <main+4>:   sub    rsp,0x10
   0x40113e <main+8>:   mov    DWORD PTR [rbp-0x4],0x1
   0x401145 <main+15>:  jmp    0x401180 <main+74>
   0x401147 <main+17>:  mov    DWORD PTR [rbp-0x8],0x1
   0x40114e <main+24>:  jmp    0x40116c <main+54>
   0x401150 <main+26>:  mov    eax,DWORD PTR [rbp-0x4]
   0x401153 <main+29>:  imul   eax,DWORD PTR [rbp-0x8]
   0x401157 <main+33>:  mov    esi,eax
   0x401159 <main+35>:  mov    edi,0x402004
   0x40115e <main+40>:  mov    eax,0x0
   0x401163 <main+45>:  call   0x401040 <printf@plt>
   0x401168 <main+50>:  add    DWORD PTR [rbp-0x8],0x1
   0x40116c <main+54>:  cmp    DWORD PTR [rbp-0x8],0x9
   0x401170 <main+58>:  jle    0x401150 <main+26>
   0x401172 <main+60>:  mov    edi,0xa
   0x401177 <main+65>:  call   0x401030 <putchar@plt>
   0x40117c <main+70>:  add    DWORD PTR [rbp-0x4],0x1
   0x401180 <main+74>:  cmp    DWORD PTR [rbp-0x4],0x9
   0x401184 <main+78>:  jle    0x401147 <main+17>
   0x401186 <main+80>:  mov    eax,0x0
   0x40118b <main+85>:  leave
   0x40118c <main+86>:  ret
   0x40118d:    nop    DWORD PTR [rax]
   0x401190 <__libc_csu_init>:  push   r15
(中略)
   0x4011f1:    Cannot access memory at address 0x4011f1
gdb-peda$ q

よって、この asm2bin コマンドはC言語のソースコードをアセンブリコードに変換するのに利用できるコンパイラであり、使用は禁止されると考えられる。

gccの潰し方が不適切

検証環境では、以下の記述によりgccを利用しにくくしている。

RUN echo "alias gcc=\"echo gcc command is disabled. use asm2bin command.\n$ asem2bin <asm file>\"" >> $HOME/.bashrc

実際に gcc コマンドを実行してみると、以下のように syntax error が出た。

root@0a53eb3589ce  ~
$ gcc
gcc command is disabled. use asm2bin command.
bash: syntax error near unexpected token `newline'

.bashrc の内容を確認すると、以下のようになっていた。

root@f9b8479c6f05  ~
$ tail .bashrc
#
# Some more alias to avoid making mistakes:
# alias rm='rm -i'
# alias cp='cp -i'
# alias mv='mv -i'
PS1='\u@\h  \w
$ '
alias asm2bin="f(){ \gcc \$1 -O0 -g -o tmp && ./tmp; }; f"
alias gcc="echo gcc command is disabled. use asm2bin command.
$ asem2bin <asm file>"

不適切な位置に改行が入っているのが、syntax error の原因のようである。
これを防ぐには、改行ではなく \n を .bashrc に書き込むため、\ をエスケープするべきである。
さらに引用符を追加し、\n を改行として出力するように echo に -e オプションを追加するろよい。
ついでに、asem2bin という謎の新しいコマンドではなく、その前に出力する文で言及している asm2bin を用いるべきであると考えられる。
すなわち、以下のようにするべきだっただろう。

RUN echo "alias gcc=\"echo -e \\\"gcc command is disabled. use asm2bin command.\\n$ asm2bin <asm file>\\\"\"" >> $HOME/.bashrc

RISC-Vに対応していなそう

Regulationによれば、今回の対象アーキテクチャにはRISC-Vが含まれる。
一方、検証環境内で

as --help

の出力を確認すると、以下の部分などのx86固有と思われる部分がみられた。

  -march=CPU[,+EXTENSION...]
                          generate code for CPU and EXTENSION, CPU is one of:
                           generic32, generic64, i386, i486, i586, i686,
                           pentium, pentiumpro, pentiumii, pentiumiii, pentium4,
                           prescott, nocona, core, core2, corei7, l1om, k1om,
                           iamcu, k6, k6_2, athlon, opteron, k8, amdfam10,
                           bdver1, bdver2, bdver3, bdver4, znver1, btver1,
                           btver2
                          EXTENSION is combination of:
                           8087, 287, 387, 687, mmx, sse, sse2, sse3, ssse3,
                           sse4.1, sse4.2, sse4, avx, avx2, avx512f, avx512cd,
                           avx512er, avx512pf, avx512dq, avx512bw, avx512vl,
                           vmx, vmfunc, smx, xsave, xsaveopt, xsavec, xsaves,
                           aes, pclmul, fsgsbase, rdrnd, f16c, bmi2, fma, fma4,
                           xop, lwp, movbe, cx16, ept, lzcnt, hle, rtm, invpcid,
                           clflush, nop, syscall, rdtscp, 3dnow, 3dnowa,
                           padlock, svme, sse4a, abm, bmi, tbm, adx, rdseed,
                           prfchw, smap, mpx, sha, clflushopt, prefetchwt1, se1,
                           clwb, avx512ifma, avx512vbmi, avx512_4fmaps,
                           avx512_4vnniw, avx512_vpopcntdq, avx512_vbmi2,
                           avx512_vnni, avx512_bitalg, clzero, mwaitx, ospke,
                           rdpid, ptwrite, cet, gfni, vaes, vpclmulqdq, no87,
                           no287, no387, no687, nommx, nosse, nosse2, nosse3,
                           nossse3, nosse4.1, nosse4.2, nosse4, noavx, noavx2,
                           noavx512f, noavx512cd, noavx512er, noavx512pf,
                           noavx512dq, noavx512bw, noavx512vl, noavx512ifma,
                           noavx512vbmi, noavx512_4fmaps, noavx512_4vnniw,
                           noavx512_vpopcntdq, noavx512_vbmi2, noavx512_vnni,
                           noavx512_bitalg
  -mtune=CPU              optimize for CPU, CPU is one of:
                           generic32, generic64, i8086, i186, i286, i386, i486,
                           i586, i686, pentium, pentiumpro, pentiumii,
                           pentiumiii, pentium4, prescott, nocona, core, core2,
                           corei7, l1om, k1om, iamcu, k6, k6_2, athlon, opteron,
                           k8, amdfam10, bdver1, bdver2, bdver3, bdver4, znver1,
                           btver1, btver2

PATHを確認すると、以下のようになっていた。

root@ded2738ef35a  ~
$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

ここに含まれるそれぞれのディレクトリの内容を ls で確認した結果、x86_64-linux-gnu-as などの x86_64 向けのソフトウェアは入っていたが、riscv32-unknown-elf-as などの RISC-V 向けのソフトウェアは入っていなそうであった。

GitHub - kamiyaowl/riscv-gnu-toolchain-docker: GNU toolchain for RISC-V, including GCC Dockerize
に掲載されている Expect のコードを asm2bin に食わせてみると、以下のようにエラーになった。

root@ded2738ef35a  ~
$ asm2bin hello.s
hello.s: Assembler messages:
hello.s:2: Error: unknown pseudo-op: `.option'
hello.s:13: Error: no such instruction: `addi sp,sp,-16'
hello.s:14: Error: no such instruction: `sw ra,12(sp)'
hello.s:15: Error: no such instruction: `sw s0,8(sp)'
hello.s:16: Error: no such instruction: `addi s0,sp,16'
hello.s:17: Error: no such instruction: `lui a5,%hi(.LC0)'
hello.s:18: Error: no such instruction: `addi a0,a5,%lo(.LC0)'
hello.s:20: Error: no such instruction: `li a5,0'
hello.s:21: Error: no such instruction: `mv a0,a5'
hello.s:22: Error: no such instruction: `lw ra,12(sp)'
hello.s:23: Error: no such instruction: `lw s0,8(sp)'
hello.s:24: Error: no such instruction: `addi sp,sp,16'
hello.s:25: Error: no such instruction: `jr ra'

検証環境の as に -march=rv32imc を指定してみると、エラーになった。

root@f9b8479c6f05  ~
$ touch test.s
root@f9b8479c6f05  ~
$ as -march=rv32imc test.s
Assembler messages:
Fatal error: invalid -march= option: `rv32imc'

さらに、今回はx86のEC2インスタンスを用いて検証を行ったが、検証環境で用いられていると思われるイメージのgccは、RISC-Vには対応していなそうである。

よって、この検証環境はRISC-Vには対応していなそうである。

まとめ

今回の「人間 Cコンパイラコンテスト」のルールには、以下の問題がみられる。

  • 対象アーキテクチャ「RISC-V」の種類やABIなどの扱いが不明瞭である

  • 使用できるソフトウェアや参考資料など、できること/できないことが不明瞭である

  • Regulationには無い「(ほとんどの)Webページの閲覧は禁止」という条件が、リポジトリ側には載っていないQ&Aで追加されている

  • 競技システムやジャッジシステムが仮にWebページである場合、閲覧が禁止されている

  • 問題につけられた制約に違反したり本来コンパイルエラーになるプログラムをアセンブリに変換した場合にジャッジシステムから返される結果が不明瞭である

  • 詳細のルールについて「こちらのリンクからご確認ください」とされているリンク先に、ほぼ同程度かそれ未満の情報しか載っておらず、詳細のルールが確認できない

また、今回用意された検証環境には、以下の問題がみられる。

  • 追加されたコマンドの asm2bin は、C言語のソースコードのアセンブリコードへの変換に用いることができるためコンパイラに該当し、使用が禁止されると考えられる

  • gccを使いにくくする設定が不適切である

  • 対象アーキテクチャの「RISC-V」に対応していなそうである

面白そうな競技であり、今回は時間的にも参加できる可能性が低くはないものの、このようにルールが怪しいという印象があるため、残念ながら今回の参加は見送ろうと思う。
次回以降でのルールや検証環境の改善に期待したい。

付録

「ツール」の定義に関するWikipedia以外の出典

Apacheのアクセス制御

AWS EC2 で検証環境を起動する

まず、t2.micro インスタンスを起動した。
「Ubuntu Server 22.04 LTS (HVM)」のAMIを用い、アーキテクチャは「64 ビット (x86)」とした。
ストレージは、今回の検証環境のみであればデフォルトの8GiBで大丈夫そうだったが、後述のRISC-Vのツールチェインを使うには足りなかったので24GiBとした。

起動したインスタンスにSSHログインし、
Install Docker Engine on Ubuntu | Docker Docs
の記述に従って以下のコマンドを実行した。

sudo apt-get update
sudo apt-get install ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo \
  "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

さらに、
ユーザをdockerグループに入れる - Qiita
の記述に沿い、以下のコマンドで ubuntu ユーザを docker グループに追加した。

sudo gpasswd -a ubuntu docker

そして、一旦ログアウトして再びログインした。するとDockerが使えるようになった。

検証環境は、検証環境のリポジトリに記載されている以下のコマンドで起動できた。

docker run --rm -it ghcr.io/humanccompilercontest/hccc_local_env:master

RISC-V向けのツールチェインは、
kamiyaowl/riscv-gnu-toolchain-docker - Docker Image | Docker Hub
を参考に、以下のコマンドで起動できた。

docker pull kamiyaowl/riscv-gnu-toolchain-docker
docker run -it kamiyaowl/riscv-gnu-toolchain-docker /bin/bash

この記事が気に入ったらサポートをしてみませんか?