Một trong các phương pháp được cộng đồng quan tâm là phân tích chương trình tĩnh thông qua việc giải quyết các biểu thức ràng buộc.. Tôi cũng sẽ có một chương trình demo, tuy còn đơn giả
Trang 1TRƯỜNG ĐẠI HỌC CÔNG NGHỆ - ĐHQGHN
KHOA CÔNG NGHỆ THÔNG TIN
CÔNG TRÌNH DỰ THI GIẢI THƯỞNG “SINH VIÊN NGHIÊN CỨU KHOA HỌC”
NĂM 2014
Tên công trình: Static program analysis
Họ và tên sinh viên: Nguyễn Tuấn Anh
Lớp: K55CA
Người hướng dẫn: PGS.TS Nguyễn Việt Hà
Th.S Vũ Quang Dũng
Nam, Nữ: Nam Khoa: Công nghệ thông tin
Trang 2Tổng quát
Ngày nay, cùng với sự phát triển của công nghệ, các thiết bị di động là sự phát triển lớn mạnh của các phần mềm Các chương trình không chỉ nhiều hơn mà cũng phức tạp hơn Do đó việc xuất hiện lỗi trong phần mềm là không thể tránh khỏi Một lỗi nhỏ trong chương trình cũng
có thể gây ra những tác động khó lường Vì vậy việc kiểm soát lỗi là đặc biệt quan trọng Từ
đó nảy sinh nhu cầu có những phương pháp để phân tích chương trình, từ đó có thể phát hiện lỗi Hiện nay trên thế giới cũng đã có một số phương pháp, nhưng chưa được nhiều và mạnh Các hướng nghiên cứu vẫn còn trùng lặp nhau Một trong các phương pháp được cộng đồng quan tâm là phân tích chương trình tĩnh thông qua việc giải quyết các biểu thức ràng buộc Trong bài báo này, tôi xin trình bày nghiên cứu của mình về phương pháp này Tôi cũng sẽ
có một chương trình demo, tuy còn đơn giản, nhưng đã cho cái nhìn tổng quan của phương pháp phân tích chương trình tĩnh thông qua việc giải quyết các biểu thức ràng buộc
Từ khóa: phân tích chương trình, phân tích tĩnh, giải quyết ràng buộc
Trang 3Mục lục
I, Đặt vấn đề 5
II, Phân tích chương trình tĩnh dựa trên việc giải quyết các ràng buộc 6
1, Tổng quan về việc phân tích chương trình 6
2, Hướng đi dựa trên ràng buộc 6
2.1 Abstract 0-CFA Analysis 6
2.2 The Analysis 7
III, Thực nghiệm 9
1, Tổng quát 9
2, The framework JavaCC 9
IV, Kết luận và cải tiến trong tương lai 11
1, Kết luận 11
2, Cải tiến trong tương lai 11
III, Tham khảo 12
Trang 4Danh sách các hình ảnh
Hình 1 : The nature of approximation: erring on the safe side 6 Hình 2 Tổng quan về dự án 9
Danh sách các mã giả
Mã giả 1 Một ví dụ về chương trình 9
Mã giả 2 Một ví dụ về đầu ra 9
Mã giả 3 Toán tử điều kiện 10
Trang 5I, Đặt vấn đề
Ngày nay, cùng với sự phát triển của công nghệ thông tin, các thiết bị phần cứng, các thiết bị
di động là sự phát triển lớn mạnh của các phần mềm Các chương trình không chỉ nhiều hơn
mà cũng phức tạp hơn Do đó việc xuất hiện lỗi, đặc biệt là các lỗi tinh vi trong phần mềm là không thể tránh khỏi Một lỗi nhỏ trong chương trình cũng có thể gây ra những tác động khó lường Những lỗi này có thể gây ra những hậu quả nghiêm trọng cả về tiền bạc, thời gian thậm chí mạng sống của con người Ví dụ như vào ngày 04/06/1996, tên lửa Ariance 5 trị giá
500 triệu đô la đã phát nổ chỉ sau không đầy một phút được phóng lên vì một lỗi lưu trữ số Lỗi trong mã điều khiển máy bức xạ Therac-25 là nguyên nhân trực tiếp dẫn đến việc tử vong của vài bệnh nhân vào năm 1980s Vào năm 2002, một nghiên cứu thực hiện bởi Viện tiêu chuẩn và công nghệ thương mại Mỹ đã chỉ ra rằng lỗi phần mềm là rất phổ biến, có hại lớn ước tính thiệt hại cho nền kinh tế Mỹ 59 tỉ đô la tức khoảng 0.6% của GDP [1] Vì vậy việc kiểm soát lỗi là đặc biệt quan trọng Từ đó nảy sinh nhu cầu có những phương pháp để phân tích chương trình, giúp cảnh báo những lỗi có thể xảy ra
Phân tích chương trình không chỉ giúp ta tìm ra lỗi, mà còn giúp cho việc hiểu rõ phần mềm hơn, tối ưu chất lượng của chương trình, của compiler, nhận ra những nguy cơ bảo mật tiềm
ẩn Hiện nay trên thế giới có hai hướng đi là phân tích tĩnh (static analysis) và phân tích động (dynamic analysis) Trong phân tích tĩnh lại có 5 phương pháp chính là: phân tích luồng dữ liệu (Data Flow Analysis), phân tích dựa vào các ràng buộc (Constraint Based Analysis), diễn dịch trừu tượng (Abstract Interpretation), Type and Effect Systems và Scalable
interprocedural analysis Và trong bài báo này tôi sẽ tập trung vào phương pháp phân tích dựa trên các ràng buộc, cụ thể tôi sẽ trình bày các kiến thức nền tảng của việc phân tích chương trình, Sau đó tôi sẽ đi sâu hơn vào phương pháp Ở phần cuối sẽ là một chương trình demo
mà tôi đang thực hiện
Trang 6II, Phân tích chương trình tĩnh dựa trên việc giải quyết các ràng buộc
1, Tổng quan về việc phân tích chương trình
Trong việc phân tích chương trình, phân tích động thì sẽ cần phải chạy các chỉ thị của người dùng trong quá trình phân tích do đó sẽ thêm chi phí vào thời gian chạy, lượng sử dụng bộ nhớ Trong khi đó việc phân tích tĩnh hướng tới việc hiểu một vài tính chất của chương trình
mà không cần chạy nó Kĩ thuật phân tích tĩnh này được dùng với mục đính dự đoán an toàn
và tính toán xấp xỉ trên tập hợp các giá trị hay hành vi xuất hiện động trong lúc chạy
Tuy rằng có 5 phương pháp chính trong kĩ thuật phân tích chương trình tĩnh, một điểm chung của tất cả các hướng đi là để duy trì khả năng tính toán thì chỉ có thể đưa ra câu trả lời gần đúng
true answer
{d1, …, dn} {dn+1, …, dN }
{d1, …, dn, …dn+m}
safe anwser
Hình 1 : The nature of approximation: erring on the safe side
Tựu chung lại, ta hi vọng việc phân tích chương trình sẽ sinh ra một tập các khả năng lớn hơn những gì sẽ có thể xảy ra trong quá trình thực thi chương trình
2, Hướng đi dựa trên ràng buộc
Tư tưởng chính của thuật toán là trích xuất một số lượng các biểu thức so sánh hoặc các ràng buộc từ chương trình Sau đó sẽ dùng các máy giải (SMT solver) riêng để giải quyết các ràng buộc này
2.1 Abstract 0-CFA Analysis
Tôi sẽ giới thiệu ngôn ngữ FUN, ta sẽ tập trung vào một ngôn ngữ lập trình hàm nhỏ: the untyped lambda calculus mở rộng với các toán tử đệ quy, điều kiện và định nghĩa địa
phương Mục đích của việc phân tích theo luồng (cũng giống như phân tích theo ràng buộc)
là sẽ tính toán cho từng biểu thức con của tập các hàm có thể đánh giá và nhấn mạnh đây là
Trang 7quan trọng khi ta có cả thể dán nhãn tất cả cá đoạn chương trình Vì vậy ta sẽ sử dụng các cấu trúc ngữ pháp sau:
e Exp expressions (or labelled terms)
t Term terms (or unlabelled expressions)
f, x Var variables
c Const constants
op Op binary operators
l Lab labels
Sự trừu tượng hóa ngữ pháp của ngôn ngữ được cho như sau:
e ::= tl
t ::= c | x | fn x => e0 | fun f x => e0 | e1 e2
| if e0 then e1 else e2 | let x = e1 in e2 | e1 op e2
Ở đây fun f x => e0 là một định nghĩa hàm trong đó fun f x => e0 is là một biến thể đệ quy của fn x => e0 khi mà tất cả sự xuất hiện tự do của f trong e0 dẫn đến fun f x => e0 chính nó Biểu thức xây dựng let x = e1 in e2 là một định nghĩa địa phương không đệ quy mà thực tế tương đương với (fn x => e2)(e1) Chúng ta sẽ cần thêm khái niệm free variables của
expressions và terms nên ta định nghĩa hàm:
FV : (Term Exp) P(Var) theo một chuẩn sau
FV(fn x => e0) = FV(e0)\{x}
FV(fun f x => e0) = FV(e0)\{f, x}
FV(let x = e1 in e2) = FV(e1) (FV(e2)\{x})
2.2 Sự phân tích
Miền trừu tượng: Bây giờ chúng ta sẽ chỉ rõ 0-CFA analysis Đây có thể coi như dạng đơn
giản nhất có thể của Control Flow Analysis trong trường hợp không có thông tin gì khác kèm theo
Kết quả của 0-CFA analysis là một cặp (𝐶̂, 𝑝̂) trong đó:
𝐶̂ là abstract cache liên kết với giá trị trừu tượng với từng điểm đã gắn nhãn chương
trình
𝑝̂ là abstract environment liên kết với giá trị trừu tượng với từng biến
Trang 8Điều trên được làm rõ bởi:
𝑣̂ 𝑉𝑎𝑙̂ = P(Term) abstract values
𝑝̂ 𝐸𝑛𝑣̂ = Var 𝑉𝑎𝑙̂ abstract environments
𝐶̂ 𝐶𝑎𝑐ℎ𝑒̂ = Lab 𝑉𝑎𝑙̂ abstract caches
Trong đó một giá trị trừu tượng 𝑣̂ là một sự trừu tượng hóa của một tập các hàm: đó là một tập các term ở dạng fn x => e0 or fun f x => e0
Trang 9III, Thực nghiệm
1, Tổng quát
Để làm rõ cho việc phân tích chương trình tĩnh bằng việc giải quyết các ràng buộc, tôi đang phát triển một chương trình nhỏ có cấu trúc như sau:
Hình 2 Tổng quan về dự án Trong bài báo này cũng như trong project, tôi sẽ chỉ tập trung vào việc phân tách các ràng buộc Việc giải các biểu thức ràng buộc có thể dùng SMT solver
Ví dụ, ta có một chương trình:
Mã giả 1 Một ví dụ về chương trình Thì chương trình sẽ đưa ra kết quả
Mã giả 2 Một ví dụ về đầu ra
2, The framework JavaCC
Trong dự án này, tôi sẽ dùng framework JavaCC để hỗ trợ trong quá trình phát triển
JavaCC dùng để sinh ra các parser và lexical analyzer Parsers and lexical analysers chính là các thành phần dùng để xử lý với đầu vào là chuỗi các kí tự
constraints
Using off-the-shelf constraint solvers
if (constraint A)
… // do some things
…
while (constraint B)
… // do some things
(constraint A) (constraint B)
Trang 10Hiện tại, tôi đang chỉ thực hiện việc bóc tách ràng buộc trên các chương trình viết bằng ngôn ngữ lập trình Java Với sự hỗ trợ của JavaCC, tôi cần tìm tất cả các câu lệnh “if” và “while” sau đó lấy các biểu thức so sánh bên trong dấu ngoặc tròn Trong tương lai tôi sẽ thêm các biểu thức ràng buộc dạng:
Mã giả 3 Toán tử điều kiện (contraint C) ? (…//do something) : (…//do something)
Trang 11IV, Kết luận và cải tiến trong tương lai
1, Kết luận
Trong bài báo này tôi đã trình bày những định nghĩa cơ bản của việc phân tích chương trình tĩnh bằng việc giải quyết các ràng buộc Hiểu được điều này là rất quan trọng trong việc tiếp tục nghiên cứu trong lĩnh vực phân tích chương trình Mặc dù đây chỉ là những kiến thức nền tảng và việc thực thi dự án vẫn chưa hoàn thành, tôi sẽ tiếp tục hoàn thiện công việc
2, Cải tiến trong tương lai
+ Đi sâu hơn vào nghiên cứu phương pháp phân tích chương trình tĩnh như là giải quyết các ràng buộc
+ Hiểu một số ý tưởng chính của một số phương pháp khác
+ Hoàn thành dự án của tôi và mở rộng cho ngôn ngữ C/C++
Trang 12III, Tham khảo
[1] http://en.wikipedia.org/wiki/Software_bug
[2] http://www.ics.uci.edu/~guoqingx/courses/253/sp2013/
[3] Sumit Gulwani, Saurabh Srivastava, Ramarathnam Venkatesan, “Program Analysis
as Contraint Solving”
[4] Flemming Nielson, Hanne Riis Nielson, Chris Hankin, “Principles of program analysis”, corrected 2nd printing 2005