15
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Rustのunsafeは安全じゃない

Last updated at Posted at 2019-09-12

毎日がエブリデイ。最近Rustに入門しました。

Rustのunsafeは「下手するとメモリリークかSEGV起こすだけでしょ、間違ったロジック組めば実行時エラー出るだろうしヘーキヘーキ」的に思ってたけど、
それ以上に害悪になりうる挙動を見つけたのでメモ

安全とは何かについてはTaPLの一章に書いてあってなるほどと思ったのを記憶からざっくり引用すると
その言語が提供する抽象化を損なうか損なわないか、みたいなことらしい

以下、損なう例

std::mem::transmute

同じサイズの型を無理やりキャストする便利な関数

バイト列をコピーせずに特定のデータ型に変換する用途で使うのがメジャーな使い方 の気がする

    let string = unsafe { std::mem::transmute::<&[u8], &str>(&[0x41, 0x42, 0x43]) };
    println!("{}", string); // => ABC
    
    let n = unsafe { std::mem::transmute::<[u8; 4], u32>([0,0,0,1]) };
    println!("{}", n); // => 16777216 リトルエンディアンなので

メモリのレイアウトがわかっている場合、safeなコードでは冗長になったり、パフォーマンス的にデメリットが発生するような変換ももらくらく
(structをタプルに変換している↓)

struct A {
    a: u8,
    b: u8,
    c: u8,
    d: u8
}

fn main() {
    let (i1, i2, i3, i4) = unsafe { std::mem::transmute::<A, (u8, u8, u8, u8)>(A { a: 82, b: 117, c: 115, d: 116 }) };
}

使い方を間違えると、
(Sizedな型の参照は同じサイズ(64bitなら8, 32bitなら4)なことを利用して)

    println!("{}", unsafe { std::mem::transmute::<&[u8; 10000], &u8>(&[1; 10000]) } );
スクリーンショット 2019-09-11 23.33.38.png

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=b4a28423b597e50567e7f9f95eed69e7
由来不明の値を出力させてみたり、

スクリーンショット 2019-09-12 11.00.24-1.png

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=a091b98bd3877560f0e5a286b75923f6
うまくやるとセグフォさせることができる

(この逆をやるとメモリリークするような気がするけど観測方法がわからないので今後の課題とする)

Booleanの奇妙な話

ところでu8とboolはprimitive typeなのでasで相互に変換でき・・・

    println!("{}", true as u8); // => 1
    println!("{}", false as u8); // => 0
    println!("{}", 1u8 as bool); // error[E0054]: cannot cast as `bool`

なかった

boolは1byteだしu8も1byteなので

    println!("{}", std::mem::size_of::<bool>()); // => 1
    println!("{}", std::mem::size_of::<u8>()); // => 1

transmuteで無理やり変換してみる

スクリーンショット 2019-09-11 23.54.38.png

0はfalse、0以外はtrueっぽい

!?

スクリーンショット 2019-09-11 23.56.11.png

trueと比較すると、
奇数(下位のビットが1)から生成されたboolはtrueを返し、
偶数から生成されたboolはfalseを返すっぽい

!?!?

スクリーンショット 2019-09-11 23.58.36.png

trueとの比較を2回やると結果が変わったりする

ちなみにreleaseビルドかdebugビルドかによっても出力が変わる

boolは2値であることや truthyなboolのオブジェクトは'true'と印字されること、みたいな抽象が損なわれていていかにも安全ではなさそうです

このようにunsafeなコードは落ちるだけでなく、合法っぽく見えても未定義動作を引き起こしうるので、全知全能でない人は蓋然性がない場合使わないようにしましょう


ここでRustのドキュメントから至言を引用すると、

transmute is incredibly unsafe. There are a vast number of ways to cause undefined behavior with this function. transmute should be the absolute last resort.

15
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?