# 卒研経過報告 #10 Rustによる組み込みプログラミング マイコンWio terminalを用いたRustとC言語の信頼性,安全性の比較検証、考察 [先週のレジュメ](https://hackmd.io/@Fteam/ryo2VIA8Y) ## 研究の目的 この研究ではこれまで主に組込みソフトウエア開発に使われてきたC/C++と それに替わる有効な選択肢として、注目されているRust言語との 信頼性や安全性など様々な面から比較検証を行う。 ## 検証 #### 1.コピー先とコピー元が重なった時のエラー :::spoiler コード ```c= #include <stdio.h> #include <string.h> void* memcpy(void *dst,const void *src,size_t n){ size_t i; char *p1=dst; const char *p2=src; for(i=0;i<n;++i){ *p1=*p2; ++p1; ++p2; } return dst; } int main(void){ char buf[]={0,1,2,3,4}; char buf2[5]; memcpy(buf2,buf,sizeof(buf)); for(int i=0;i<sizeof(buf2);++i){ printf("%d\n",buf2[i]); } return 0; } ``` 確保しているメモリからはみ出しているため ``` 1 1 2 3 4 *** stack smashing detected ***: <unknown> terminated Aborted (core dumped) ``` 修正版 ```rust= fn main(){ let mut buf =[1,2,3,4,5]; let mut buf2=[0;5]; memcpy(&mut buf2,&mut buf,5); println!("{:?}",buf2); } fn memcpy(dst:&mut[u8],src:&mut[u8],n:usize){ for i in 0..n{ dst[i]=src[i]; } } ``` ただこれだとc言語のようにアドレスずらしてコピーする方法がわからない ➡cみたいにbuf+1ができない(エラーになる ::: #### 2.バッファオーバーフロー(修正) c言語における危険な関数`gets()` ,`scanf()` :::spoiler コード gets()の例 ```c= #include<stdio.h> int main(void){ char s[5]; gets(s); printf("%s",s); } ``` scanf()の例 ```c=4 char s[5]; scanf("%s", s); ``` ``` aaaaaaaa *** stack smashing detected ***: <unknown> terminated 中止 ``` ```rust= fn main(){ let mut array: [i32; 3] = [1, 2, 3]; array = [1, 2, 3, 4]; for i in 0..3{ println!("array[{}]={}", i,array[i]); } } ``` ``` Compiling playground v0.0.1 (/playground) error[E0308]: mismatched types --> src/lib.rs:3:13 | 3 | array = [1, 2, 3, 4]; | ^^^^^^^^^^^^ expected an array with a fixed size of 3 elements, found one with 4 elements 3要素の固定サイズの配列を期待したところ、4要素の配列が見つかりました。 For more information about this error, try `rustc --explain E0308`. error: could not compile `playground` due to previous error ``` ::: これらの関数は変数の要素数を間違えるとバッファオーバーフローが発生してしまい,改善策はプログラマが気を付けてコーディングする他ない.rustでは変数に文字列を代入せず,スタック領域に確保した文字列のメモリへのポインタを示している. #### 3.NULLポインタの参照(修正) NULLポインタを参照する. :::spoiler コード ```c= #include<stdio.h> int main(void){ int *p = NULL; int a; a = p[0]; return 0; } ``` ``` Segmentation fault ``` ```rust= fn main(){ //let x = Some(10); let x = None; let v = match x { Some(i) => i, None => -1, }; println!("{}", v); } ``` ``` -1 ``` 型を使ってNULLを参照しない仕組みを採用 通常の状態は1. 1. Option<T>型「NULLかもしれない変数」 パターンマッチングで2,3に型変換される 3. T型「確実にNULLではない変数」 5. Nill型「NULLを表す変数」 2,3から変数を呼び出すと型エラーが発生 1を経由してメソッドにアクセスすると型エラー ::: #### 4. 配列外の値へ不正アクセス(ありそうな例へ修正) 配列番号の外の値を指定し,メモリ破壊を起こすエラー :::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; } ``` 簡単でありそうな例 ```c= #include<stdio.h> int main(void){ char a[5]; char b[] = "Hello!"; printf("名前入力\n"); scanf("%s", a); printf("%s,%s\n", a, b); return 0; } ``` ``` 名前入力 koya koya,Hello! ``` ``` 名前入力 koyasujunya koyasujunya,ujunya ``` ```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]);} } ``` まずc言語のソースコードを確認すると,宣言されていない配列にアクセスしようとしておりメモリ破壊が起こることが容易に想像できる.しかしc言語のコンパイラはエラーをださず,結果として他配列に影響を及ぼしてしまっていることが確認できる.これが重要なシステムであると仮定するといかに危険な行為であるか理解できる.rustではこのような事態に陥る前に予防線を張っており,配列番号の外の数字を指定すると停止してくれる. ::: ## 参考文献 [1] rustプログラミング入門 オーム社 [2] rust入門 増田智明著 [公式ドキュメント](https://doc.rust-jp.rs/book-ja/ch08-02-strings.html) :::spoiler メモ [](https://qiita.com/nsnonsugar/items/be8a066c6627ab5b052a) ```=rust use std::thread; use std::time::Duration; static count:i8=0; fn main() { let i=0; thread::spawn(|| { for i in 1..100 { count+=1; thread::sleep(Duration::from_millis(1)); } }); thread::spawn(|| { for i in 1..100 { count+=1; thread::sleep(Duration::from_millis(1)); } }); println!("{}",count); } ``` ```=C++ #include <thread> #include <cstdio> #include <cstdint> uint32_t count; void ThreadA(){ for(int i=0;i<10;++i){ ++count; } } void ThreadB(){ for(int i=0;i<100;++i){ ++count; } } int main(){ count = 0; std::thread th_a(ThreadA); std::thread th_b(ThreadB); th_a.join(); th_b.join(); printf("count=%d\n",count); return 0; } ``` :::