# 卒研経過報告 #7 Rustによる組み込みプログラミング マイコンWio terminalを用いたRustとC言語の信頼性,安全性の比較検証、考察 [先週のメモ](https://hackmd.io/@Fteam/HyHb3MSBt) ## 研究の目的 この研究ではこれまで主に組込みソフトウエア開発に使われてきたC/C++と それに替わる有効な選択肢として、注目されているRust言語との 信頼性や安全性など様々な面から比較検証を行う。 ## memo C言語だと危険な共有書き込みの具体例とrustによる解決例 ## 検証 #### 1.無限再帰 深すぎる再帰,無限再帰から抜け出せなくなるエラー :::spoiler コード ```c= #include <stdio.h> void loop(){ loop(); } int main(){ loop(); return 0; } ``` ``` Segmentation fault ``` ```rust= fn loop_1(){ loop_1(); } fn main(){ loop_1(); } ``` ``` Compiling playground v0.0.1 (/playground) warning: function cannot return without recursing --> src/main.rs:1:1 | 1 | fn loop_1(){ | ^^^^^^^^^^^ cannot return without recursing 2 | loop_1(); | -------- recursive call site | = note: `#[warn(unconditional_recursion)]` on by default = help: a `loop` may express intention better if this is on purpose warning: `playground` (bin "playground") generated 1 warning Finished dev [unoptimized + debuginfo] target(s) in 1.03s Running `target/debug/playground` thread 'main' has overflowed its stack fatal runtime error: stack overflow timeout: the monitored command dumped core /playground/tools/entrypoint.sh: line 11: 8 Aborted timeout --signal=KILL ${timeout} "$@" ``` ::: このコードでは再帰関数を用いているが,プログラマの誤りによって無限ループに陥っていることが理解できる.c言語では文法的な誤りがないため,コンパイル時に警告を受けることなく実行し*Segmentation fault*に陥ってしまう.一方rustではコンパイル時にスタックオーバーフローというエラーが発生する.このコードではloop_1()の呼び出しするたびに再帰関数が使用するメモリ領域が増加し,使用できるメモリ領域をすべて使い果たしオーバーフローが起こる. #### 2.配列外の値へ不正アクセス 配列番号の外の値を指定し,メモリ破壊を起こすエラー :::spoiler コード ```c= #include<stdio.h> int main(void){ int a[3] = {0, 1, 2}; int b[3] = {3, 4, 5}; a[3] = 99; // bug for(int i = 0; i < 3; i++) printf("a[%d] = %d\n", i, a[i]); for(int i = 0; i < 3; i++) printf("b[%d] = %d\n", i, b[i]); return 0; } ``` ``` a[0] = 0 a[1] = 1 a[2] = 2 b[0] = 99 b[1] = 4 b[2] = 5 ``` ```rust= fn main(){ let mut a = [0, 1, 2]; let /*mut*/ b = [3, 4, 5]; a[3] = 99; for i in 0..3{println!("a[{}] = {}", i, a[i]);} for i in 0..3{println!("b[{}] = {}", i, b[i]);} } ``` ``` Compiling playground v0.0.1 (/playground) error: this operation will panic at runtime --> src/main.rs:5:5 | 5 | a[3] = 99; | ^^^^ index out of bounds: the length is 3 but the index is 3 | = note: `#[deny(unconditional_panic)]` on by default error: could not compile `playground` due to previous error ``` ::: まずc言語のソースコードを確認すると,宣言されていない配列にアクセスしようとしておりメモリ破壊が起こることが容易に想像できる.しかしc言語のコンパイラはエラーをださず,結果として他配列に影響を及ぼしてしまっていることが確認できる.これが重要なシステムであると仮定するといかに危険な行為であるか理解できる.rustではこのような事態に陥る前に予防線を張っており,配列番号の外の数字を指定すると停止してくれる. #### 3.コピー先とコピー元が重なった時のエラー strcpy()の停止条件'\0'がコピーによって消えるため、無限ループに陥り,不正なアドレスへの書き込みを行う. :::spoiler コード ```c= #include<stdio.h> void *strcpy(char *dst, char *src){ int i = 0; while(src[i] != '\0'){ dst[i] = src[i]; i++; } } int main(void){ char str[] = "Hello!"; strcpy(str +1, str); printf("%s\n", str); return 0; } ``` ``` Segmentation fault ``` ```rust= fn main(){ //let str1: &str = "Hello!"; //let str2 = String::from("Alice"); //let str2 = str1.clone(); //println!("str1:{},{:p}", str1, &str1); //println!("str2:{},{:p}", str2, &str2); let mut msg = String::from("My name is Koyasu."); //let slice1 = &msg[..7]; //let slice2 = &msg[11..msg.len()]; let msg2 = &msg[8..msg.len()]; msg = msg2.to_string(); //スライスが参照するデータの代入 println!("msg:{}", msg); } ``` ::: **疑問** - スライスではrustで重複コピーした際にcの例のように一部を重複させることはできず参照するデータの代入しかできないのではないか - 生ポインタはrustの所有権システムで実現できないような柔軟な構造を作ることができるが,unsafe()ブロックを用いることが必須であり,このブロック内で記述したコードはプログラマの責任となるためrustによる安全性は保障されないのではないか [](https://codezine.jp/article/detail/4700) ## 参考文献 [1] rustプログラミング入門 オーム社 [2] プログラミングrust オライリー・ジャパン [](https://www.zakioka.net/blog/memory-management-for-rust/) [](https://minus9d.hatenablog.com/entry/20140121/1390314231) [](http://www.momo.cs.okayama-u.ac.jp/~sasakura/jikken/2013/debug/) [](https://task4233.dev/article/20191226.html#segmentation-fault%E3%81%A8%E3%81%AF) [](https://chomado.com/programming/call-by-value-or-by-reference/) [](https://elwoodblues.hatenablog.com/entry/20090205/1233828242) [](https://www.kushiro-ct.ac.jp/yanagawa/C-2015/12-0527/index.html) [](新しく作ったファイルを、前のファイルに上書きする) :::spoiler 蛇足 可変な参照渡し ```c= #include<stdio.h> int main(void){ int a; int *p1, *p2; a = 10; p1 = &a; p2 = &a; printf("%d\n", *p1); printf("%d\n", *p2); return 0; } ``` ``` 10 10 ``` ```rust= fn main() { let mut a = 10; let b = &mut x; let c = &mut x; println!("{}", b); println!("{}", c); } ``` ``` Compiling playground v0.0.1 (/playground) error[E0499]: cannot borrow `x` as mutable more than once at a time --> src/main.rs:5:13 | 4 | let y = &mut x; | ------ first mutable borrow occurs here 5 | let z = &mut x; | ^^^^^^ second mutable borrow occurs here 6 | 7 | println!("{}", y); | - first borrow later used here For more information about this error, try `rustc --explain E0499`. error: could not compile `playground` due to previous error ``` :::