全体ミーティング (3/11)

33
全全全全全全 (3/11) 全全全全 1

Upload: wirt

Post on 17-Jan-2016

63 views

Category:

Documents


0 download

DESCRIPTION

全体ミーティング (3/11). 野尻隆宏. 紹介する論文. Sufficient Preconditions for Modular Assertion Checking Yannick Moy VMCAI 2008. 扱いたい 問題. ソースコードの安全性の検証を行いたい n ull ポインタの参照 配列境界外のアクセス 整数オーバーフロー Assertion Checking ができればよい ptr != NULL 0

TRANSCRIPT

Page 1: 全体ミーティング  (3/11)

全体ミーティング (3/11)

野尻隆宏

1

Page 2: 全体ミーティング  (3/11)

紹介する論文• Sufficient Preconditions for Modular Assertion

Checking– Yannick Moy– VMCAI 2008

2

Page 3: 全体ミーティング  (3/11)

扱いたい問題• ソースコードの安全性の検証を行いたい

null ポインタの参照配列境界外のアクセス整数オーバーフロー

• Assertion Checking ができればよいptr != NULL0 <= index < length(array)min_integer <= i + j <= max_integer

3

Page 4: 全体ミーティング  (3/11)

Assertion Checking を行うために• プログラムの一部分(関数など)だけを

みて、assertion が満たされることを証明したい

• しかし、どのような context で、その部分が実行されるか分からない

• 通常は、証明するのに十分な事前条件をユーザが与える必要がある

• それらを自動的に計算して与えたい4

Page 5: 全体ミーティング  (3/11)

Running Example

5

void foo(int s, int n) { int i = 0; while (1) { if (i < n) { assert(0 <= i && i < s); } i = i + 1; }}

{requires n <= 0 || n <= s}

Page 6: 全体ミーティング  (3/11)

これまでの手法 1

• Induction-Iteration– Suzuki and Ishihara, POPL ’77

• 最弱事前条件を用いる• ループに関する事前条件では、ループ変数

を全称量化する• Quantifier Elimination の手続きを使って、

量化子を消す

6

Page 7: 全体ミーティング  (3/11)

Induction-Iteration:Backwards from Assertion

7

void foo(int s, int n) { int i = 0; while (1) {

if (i < n) {

assert(0 <= i && i < s); } i = i + 1;

}}

{0 <= i && i < s}

{W(0) ≡ i < n ==> (0 <= i && i < s)}

Page 8: 全体ミーティング  (3/11)

Induction-Iteration:Backwards from Assertion

• W(0) が while ループについて不変であれば、 assertion の正当性をいえる

• そこで、次に W(0) が不変であるかどうかを確かめる– W(0) を仮定した下で、ループを一回実行した

ら、実行後にも W(0) が成り立っているか?

8

Page 9: 全体ミーティング  (3/11)

Induction-Iteration:Loop Invariant Generation

9

void foo(int s, int n) { int i = 0; while (1) {

if (i < n) { assert(0 <= i && i < s); }

i = i + 1;

}}

{W(0)}

{i+1 < n ==> (0 <= i+1 && i+1 < s)}

{W(1) ≡ i+1 < n ==> (0 <= i+1 && i+1 < s)}

Page 10: 全体ミーティング  (3/11)

Induction-Iteration:Loop Invariant Generation

• W(0) ==> W(1) が成り立つか?• 成り立つ場合、 W(0) がループ不変式と分

かる• 成り立たない場合、– 式を一般化する: ∀ i. W(0) ==> W(1)– 量化子を消す– これを新たな不変式の候補として、繰り返す

10

Page 11: 全体ミーティング  (3/11)

Induction-Iteration の問題点• 今回の例の場合、 n <= s が得られる

n<=0 || n<=s と比べて、強い条件• ループ不変式の計算の際に、ループの外の

情報を使わないので、条件が強くなった– ループの外から得られる ” i >= 0” という情報

を考慮していなかったので、 n <= 0 が得られなかった

11

Page 12: 全体ミーティング  (3/11)

これまでの手法 2

• Abstract Debugging– Bourdoncle, ESEC ’93

• 抽象解釈 (Abstract Interpretation) を用いる– プログラムの各点で、各変数の取りうる値を

知りたいが、厳密な計算は難しい– 区間 [l, h] で表して近似的に計算する、など

• 前進伝播、後進伝播の計算を行う

12

Page 13: 全体ミーティング  (3/11)

Abstract Debugging:Initial Forward Propagation

13

void foo(int s, int n) { int i = 0;

while (1) { if (i < n) {

assert(0 <= i && i < s); } i = i + 1; }}

{i == 0}

{0 <= i && i < n}

Page 14: 全体ミーティング  (3/11)

Abstract Debugging:Backward Propagation

14

void foo(int s, int n) {

int i = 0;

while (1) {

if (i < n) {

assert(0 <= i && i < s); } i = i + 1; }}

{0<=i && i<n && i<s}

{(0<=i && n<=i) || (0<=i && i<n && i<s)}

{(i==0 && n<=0) || (i==0 && 0<n && 0<s)}

{n<=0 || (0<n && 0<s)}

Page 15: 全体ミーティング  (3/11)

Abstract Debugging の問題点• 事前条件として n <= 0 || (0 < n && 0 < s)

が得られる– 必要だが、十分な条件ではない

• 抽象解釈での近似による限界– 区間による近似では、分岐の合流を扱いにく

い– x != y などの表現は扱いにくい

15

Page 16: 全体ミーティング  (3/11)

提案する手法• 抽象解釈と最弱事前条件を組み合わせる– ループの外からの情報も使いながら、

最弱事前条件を計算する

16

Page 17: 全体ミーティング  (3/11)

1. 前方抽象解釈を行う• 各点で変数の取りうる範囲を、

近似的に求める(各点での不変式になる)

17

void foo(int s, int n) { int i = 0; while (1) { if (i < n) {

assert(0 <= i && i < s); } i = i + 1; }}

{0<=i && i<n}

Page 18: 全体ミーティング  (3/11)

2. assertion を不変式で弱める

18

void foo(int s, int n) { int i = 0; while (1) { if (i < n) { {(0<=i && i<n) } assert(0 <= i && i < s); } i = i + 1; }}

==> i<s

Page 19: 全体ミーティング  (3/11)

3. 最弱事前条件を計算する

19

void foo(int s, int n) { int i = 0; while (1) {

if (i < n) {

assert(0 <= i && i < s); } i = i + 1; }}

{(0<=i && i<n) ==> i<s}

{(0<=i && i<n) ==> i<s}

Page 20: 全体ミーティング  (3/11)

4. ループ変数を全称量化する

20

void foo(int s, int n) { int i = 0;

while (1) {

if (i < n) { assert(0 <= i && i < s); } i = i + 1; }}

{(0<=i && i<n) ==> i<s}

{∀i. (0<=i && i<n) ==> i<s}

Page 21: 全体ミーティング  (3/11)

5. 量化された変数を削除する

21

void foo(int s, int n) {

int i = 0;

while (1) { if (i < n) { assert(0 <= i && i < s); } i = i + 1; }}

{n<=0 || n<=s}

{n<=0 || n<=s}

{requires n<=0 || n<=s}

Page 22: 全体ミーティング  (3/11)

得られた事前条件• 十分条件 n <= 0 || n <= s が得られた

• Induction-Iteration より優れている点– ループの外からの情報を使える

• Abstract Debugging より優れている点– 最弱事前条件の計算により、

十分条件が得られる

22

Page 23: 全体ミーティング  (3/11)

ポインタが存在する場合• エイリアシングを考慮する必要がある

23

void bar(int *p, int *n) { int i = 0; while (1) { if (i < *n) { *(p + i) = 0; } i = i + 1; }}

n と p+i が同じ場所を指している

かもしれない

Page 24: 全体ミーティング  (3/11)

エイリアシングへの対処• 実際に人がプログラムを記述する際には、

エイリアスしていないことを前提としている部分も多い

• そこで、このような前提を仮定として用いる

• 前の例では、 n と p+i はエイリアスしていない、と仮定してしまう 24

Page 25: 全体ミーティング  (3/11)

エイリアシングへの対処• 具体的には、プログラムは次の規則に

従うものとして扱う

25

ポインタ変数 x を通したメモリへの書き込みと読み込みの間に、別のメモリへの書き込みがある場合、その書き込みは x とは別の場所を指している

Page 26: 全体ミーティング  (3/11)

エイリアシングへの対処

26

*x = 0;…*y = 10;…t = *x;

x と y が同じ場所を指していた場合、

初めの書き込みを無効化してしまう

• このような規則を満たすものは、前と同様の手法を用いて事前条件を計算できることが示せる

そこで、 x と y は異なる場所を指している

と考える

Page 27: 全体ミーティング  (3/11)

扱いやすくするために• 実際には、この規則を満たしているかど

うか確かめるのは難しいので、さらに強めた次の条件を用いる

separated(p, q) と書くことにする27

• 書き込みが行われる領域は互いに重ならない• 読み込みだけが行われる領域と、 書き込みが行われる領域は互いに重ならない

Page 28: 全体ミーティング  (3/11)

扱いやすくするために• 前の例 ( 関数 bar) では separated(p, n)

• これを仮定して関数 bar を解析する• separated(p, n) を仮定して事前条件の計算

をしたので、 bar の呼出時には、この制約も確かめる必要がある

28

p n

Page 29: 全体ミーティング  (3/11)

実験結果• Minix 3 Operating System 上で実装された

C の標準 string 関数に対して実行• 20 の関数について、メモリ安全性を示す

のに十分な事前条件を推論できた• strcat での例:

29

separated(dest[…],src[…])&& offset_min(dest) <= 0 <= strlen(dest) <= offset_max(dest)&& offset_min(src) <= 0 <= strlen(src) <= offset_max(src)&& strlen(dest) + strlen(src) <= offset_max(dest)

Page 30: 全体ミーティング  (3/11)

実験結果• Verisec buffer overflow benchmark に適用– 実際のアプリケーション (sendmail, wu-ftpd な

ど )に存在した脆弱性からとられたベンチマーク

– 多くのものに対して十分な事前条件を推論できた

• バイナリサーチにおいて、整数オーバーフローを起こさないための十分な事前条件を推論できた

30

Page 31: 全体ミーティング  (3/11)

関連研究 (1/2)

• 配列境界検査・ループ不変式推論– Abstract Interpretation

[Cousot and Halbwachs, POPL ’78]– Induction-Iteration [Suzuki and Ishihara, POPL ’77]

• Modular Program Verification and Precondition Inference– Abstract Debugging [Bourdoncle, ESEC ’93]

31

Page 32: 全体ミーティング  (3/11)

関連研究 (2/2)

• Memory Separation andPrecondition Inference– Pointer Separation

[Aiken et al., PLDI ’03] [Koes et al., MSP ’04] など• 抽象解釈と演繹的証明の組み合わせ– 抽象解釈と定理証明器を組み合わせて

ループ不変式を作る[Leino and Logozzo, APLAS ’05]

32

Page 33: 全体ミーティング  (3/11)

まとめ• assertion が成り立つことを証明するため

に十分な事前条件を推論する方法を提案した

• 従来の方法を組み合わせて構成した– 抽象解釈– 最弱事前条件– 量化子の削除

• あるルールに従う、ポインタを含むプログラムに適用できることを示した 33