Bài 5-6: Phân tích, Kiểm thử Phần mềm & Fuzzing Cơ bản
1. Kiểm thử Phần mềm (Software Testing)
1.1 Định nghĩa
Kiểm thử phần mềm là quá trình đánh giá và xác minh xem phần mềm hoặc ứng dụng có hoạt động đúng như mong đợi hay không. Mục tiêu chính:
- Phát hiện và sửa chữa các lỗi (bug)
- Đảm bảo chất lượng sản phẩm
- Thẩm định và xác minh phần mềm đáp ứng đúng yêu cầu đặt ra
- Cải thiện độ tin cậy của phần mềm
1.2 Phân loại kiểm thử
Theo cách thức kiểm thử
| Loại | Mô tả |
|---|---|
| Black Box Testing | Chỉ quan tâm đầu vào/đầu ra, không biết mã nguồn bên trong |
| White Box Testing | Dựa vào mã nguồn để kiểm tra logic, cấu trúc bên trong |
| Gray Box Testing | Kết hợp cả hai: có một phần kiến thức về cấu trúc bên trong |
Theo giai đoạn kiểm thử
Unit Testing → Integration Testing → System Testing → Acceptance Testing- Unit Testing: Kiểm tra từng hàm/lớp nhỏ nhất một cách độc lập
- Integration Testing: Kiểm tra sự tương tác giữa các module sau khi ghép lại
- System Testing: Kiểm tra toàn bộ hệ thống hoàn chỉnh
- Acceptance Testing: Xác nhận phần mềm đáp ứng yêu cầu khách hàng, sẵn sàng triển khai
Theo mục tiêu kiểm thử
- Functional Testing: Kiểm tra các chức năng có hoạt động đúng yêu cầu không
- Non-functional Testing: Kiểm tra hiệu suất, bảo mật, độ tin cậy, khả năng sử dụng
- Regression Testing: Sau khi thay đổi code, đảm bảo các tính năng cũ không bị ảnh hưởng
- Security Testing: Kiểm tra khả năng chống tấn công xâm nhập, lỗ hổng bảo mật
- Performance Testing: Load test, stress test, scalability test
- Usability Testing: Đánh giá trải nghiệm người dùng, UI/UX
Theo mức độ tự động hóa
- Manual Testing: Tester thực hiện thủ công, phù hợp kiểm tra UI/UX
- Automated Testing: Dùng công cụ và script, phù hợp regression test, performance test
1.3 Quy trình kiểm thử (STLC)
Software Testing Life Cycle gồm 6 giai đoạn:
2. Kiểm thử Hộp Trắng (White Box Testing)
2.1 Chiến lược
Thiết kế test case dựa vào cấu trúc nội tại của đối tượng kiểm thử, đảm bảo tất cả câu lệnh, biểu thức điều kiện trong chương trình được thực hiện ít nhất một lần.
Gồm 3 kỹ thuật chính:
- Basis Path Testing
- Control-flow / Coverage Testing
- Data-flow Testing
2.2 Kiểm thử Đường dẫn Cơ sở (Basis Path Testing)
Được McCabe đề xuất năm 1976. Phương pháp này:
- Đảm bảo tất cả independent path (đường dẫn độc lập) đều được thực thi ít nhất một lần
- Cho biết số lượng test case tối thiểu cần thiết kế
Independent path: Bất kỳ path nào bổ sung ít nhất một tập lệnh hoặc một biểu thức điều kiện mới so với các path đã có.
Xây dựng Control Flow Graph (CFG)
- Node: đại diện cho một hoặc nhiều câu lệnh xử lý
- Edge: đại diện cho một luồng điều khiển giữa các node
- Predicate node: node đại diện cho biểu thức điều kiện (if/while/for)Tập path cơ sở cần test (ví dụ trên):
- Path 1: 1 → 2 → 5 (không còn record)
- Path 2: 1 → 2 → 3 → 4 → 2 → 5 (field1 == 0)
- Path 3: 1 → 2 → 3 → 6 → 7 → 4 → 2 → 5 (field2 == 0)
- Path 4: 1 → 2 → 3 → 6 → 8 → 7 → 4 → 2 → 5 (else)
Mỗi path tương ứng một test case → 4 test case tối thiểu.
2.3 Kiểm thử Coverage (Phủ)
Coverage = tỉ lệ các thành phần thực sự được kiểm thử so với tổng thể.
| Cấp | Tên | Mô tả |
|---|---|---|
| 0 | Không có chiến lược | Kiểm thử tùy tiện, không có trách nhiệm |
| 1 | Statement Coverage | Mỗi câu lệnh được thực thi ít nhất 1 lần |
| 2 | Branch Coverage | Mỗi nhánh (true/false) của điều kiện đều được đi qua |
| 3 | Condition Coverage | Mỗi điều kiện con trong biểu thức phức được test cả true lẫn false |
| 4 | Branch & Condition Coverage | Kết hợp cấp 2 và cấp 3 |
Ví dụ minh họa
float foo(int a, int b, int c, int d, float e) {
float e;
if (a == 0) // Predicate 1
return 0;
int x = 0;
if ((a == b) OR ((c == d) AND bug(a))) // Predicate 2
x = 1;
e = 1 / x;
return e;
}- Statement Coverage 100%:
foo(1,1,1,1,1)→ đi qua tất cả câu lệnh - Branch Coverage 100%: cần thêm
foo(1,2,1,2,1)để nhánh false của Predicate 2 được đi - Condition Coverage 100%: cần thêm
foo(1,2,1,1,1)vàfoo(3,2,1,1,1)để phủ từng điều kiện con
2.4 Kiểm thử Luồng Dữ liệu (Data-flow Testing)
Tập trung vào chu kỳ sống của biến: định nghĩa (define) → sử dụng (use) → hủy (kill).
Hệ thống ký hiệu
| Ký hiệu | Ý nghĩa |
|---|---|
d | defined – biến được khai báo/gán giá trị |
k | killed – biến bị hủy/ra khỏi scope |
u | used – biến được sử dụng |
c | c-use – dùng trong biểu thức tính toán |
p | p-use – dùng trong biểu thức điều kiện |
Các pattern anomaly
| Pattern | Ý nghĩa |
|---|---|
dd | Định nghĩa 2 lần không dùng ở giữa → potential bug |
du | Định nghĩa rồi dùng → bình thường |
ud | Dùng rồi định nghĩa lại → bình thường |
uk | Dùng rồi hủy → bình thường |
dk | Định nghĩa rồi hủy không dùng → potential bug |
~d | Dùng biến chưa định nghĩa → bug |
k~ | Biến đã hủy được dùng → bug |
DU Path và DU Pair
- DEF(S): tập biến được định nghĩa tại câu lệnh S
- USE(S): tập biến được sử dụng tại câu lệnh S
- DU Path của biến X từ S đến S’: đường đi trên CFG không có định nghĩa lại của X ở giữa
- DU Pair (S, S’): tồn tại ít nhất một DU path nối S và S'
Các chiến lược kiểm thử luồng dữ liệu
Mối quan hệ: chiến lược trên mạnh hơn chiến lược dưới (bao phủ nhiều hơn).
3. Kiểm thử Hộp Đen (Black Box Testing)
3.1 Đặc điểm
- Còn gọi là specification-based testing
- Tester chỉ quan tâm WHAT (phần mềm làm gì), không quan tâm HOW (làm như thế nào)
- Không cần truy cập mã nguồn
Quy trình:
Phân tích chức năng → Thiết kế test case → Thực thi → So sánh kết quả → Báo cáo3.2 Phân vùng Tương đương (Equivalence Partitioning – EP)
Ý tưởng: Nếu một giá trị đại diện trong nhóm đúng thì các giá trị còn lại trong nhóm cũng đúng và ngược lại → giảm thiểu số lượng test case không cần thiết.
Hai giá trị tương đương khi:
- Tương tự nhau về mặt trực giác
- Đặc tả mô tả chương trình xử lý chúng như nhau
- Chúng dẫn chương trình đi cùng một nhánh
- Cho cùng kết quả với các giả thiết đã đưa ra
3.3 Phân tích Giá trị Biên (Boundary Value Analysis – BVA)
Tập trung kiểm thử tại các giá trị biên giữa các phân vùng — nơi lỗi thường xảy ra nhất.
Standard BVA
Với biến x có miền [min, max], chọn 5 giá trị:
min | min+1 | nom (trung bình) | max-1 | maxSố test case = 4n + 1 (n = số biến).
Robust Testing (mở rộng Standard BVA)
Thêm 2 giá trị ngoài biên: min-1 và max+1 (invalid).
Số test case = 6n + 1.
Worst-case Testing
Kiểm tra đồng thời tất cả các biến tại biên. Số test case = 5^n.
Robust Worst-case Testing
Tương tự Worst-case nhưng thêm các giá trị invalid. Số test case = 7^n.
4. Fuzzing
4.1 Định nghĩa
Fuzzing là kỹ thuật phát hiện lỗi phần mềm bằng cách tự động hoặc bán tự động gửi dữ liệu đầu vào không hợp lệ, ngẫu nhiên, hoặc bất ngờ vào chương trình, sau đó theo dõi hành vi bất thường.
Dữ liệu đầu vào fuzzing thường là:
- Giá trị vượt quá biên
- Giá trị đặc biệt (null, 0, MAX_INT…)
- Chuỗi ký tự sai định dạng
- Dữ liệu không mong đợi theo protocol
4.2 Phân loại Fuzzing
Theo mức độ hiểu biết về chương trình
| Loại | Mô tả |
|---|---|
| Blackbox Fuzzing | Không biết cấu trúc bên trong, gửi input ngẫu nhiên |
| Whitebox Fuzzing | Có mã nguồn, kết hợp phân tích tĩnh/động |
| Greybox Fuzzing | Biết một phần cấu trúc (ví dụ: AFL dùng coverage feedback) |
Theo chiến lược tạo dữ liệu
| Chiến lược | Mô tả |
|---|---|
| Mutation-based | Lấy input mẫu hợp lệ rồi biến đổi ngẫu nhiên một phần |
| Generation-based | Sinh input từ đầu dựa trên đặc tả cú pháp/ngữ pháp (grammar) |
Theo phản hồi
- Feedback-based: Sử dụng thông tin thu được (coverage, trace) để hướng dẫn các lần thử tiếp theo
- No-feedback: Không quan tâm kết quả các lần trước, hoàn toàn ngẫu nhiên
4.3 Các kỹ thuật Fuzzing
Kỹ thuật Sinh mẫu (Sample Generation)
1. Biến đổi ngẫu nhiên (Random Mutation / Blackbox Fuzzing)
def RandomFuzzing(input_seed):
numWrites = random(len(seed) / 1000) + 1
newInput = seed
for i in range(numWrites):
loc = random(len(seed))
byte_value = random(255)
newInput[loc] = byte_value
result = ExecuteAppWith(newInput)
if result == crash:
print("bug found!")2. Biểu diễn cấu trúc ngữ pháp (Grammar Representation)
Sinh input theo cú pháp được định nghĩa trước, ví dụ HTTP request:
# SPIKE fuzzing code (grammar-based)
string("POST /api/blog/ HTTP/1.2\r\n")
string("Content-Length: ")
blocksize_string("blockA", 2)
block_start("blockA")
string('{"body": "')
string_variable("XXX")
string('"')
block_end("blockA")3. Thuật toán lập lịch (Scheduling Algorithms)
Chọn seed nào để fuzz tiếp theo dựa trên tiềm năng khám phá node chưa được thăm. Ví dụ: seed gần nhiều node chưa thăm → ưu tiên hơn.
Kỹ thuật Phân tích Động (Dynamic Analysis)
- Dynamic Symbolic Execution: Kết hợp thực thi ký hiệu để hướng dẫn fuzzing
- Coverage Feedback: Dùng thông tin coverage để chọn và biến đổi seed hiệu quả
- Dynamic Taint Analysis: Theo dõi luồng dữ liệu taint để xác định vùng nhạy cảm
Coverage-based Fuzzing:
Kỹ thuật Phân tích Tĩnh (Static Analysis)
Yêu cầu truy cập mã nguồn:
- Control-flow Analysis: Phân tích đồ thị luồng điều khiển
- Data-flow Slices: Cắt lát luồng dữ liệu để xác định vùng nguy hiểm
4.4 Ưu điểm và Nhược điểm
4.5 Một số công cụ Fuzzing phổ biến
| Công cụ | Mô tả |
|---|---|
| AFL (American Fuzzy Lop) | Coverage-guided greybox fuzzer, rất phổ biến |
| LibFuzzer | In-process fuzzer của LLVM, tích hợp sanitizer |
| Honggfuzz | Feedback-driven fuzzer hỗ trợ hardware coverage |
| OSS-Fuzz | Google’s continuous fuzzing service cho open source |
| ClusterFuzz | Distributed fuzzing infrastructure của Google |
| Address Sanitizer | Phát hiện memory errors, thường kết hợp với fuzzer |
| Valgrind | Dynamic analysis tool, phát hiện memory leak |
AFL đã tìm thấy lỗi trong hàng trăm dự án: OpenSSL, PHP, Mozilla Firefox, bash, nginx, tcpdump, ImageMagick, BIND, QEMU…
5. Symbolic Execution
5.1 Định nghĩa
Symbolic Execution (Thực thi Ký hiệu) là kỹ thuật phân tích chương trình trong đó các giá trị đầu vào không phải giá trị cụ thể mà là ký hiệu đại diện cho một tập hợp giá trị có thể có.
- Thay vì chạy
f(5), ta chạyf(sym_x)— sym_x đại diện cho mọi giá trị có thể - Tại mỗi nhánh rẽ (if/while), tạo ra path condition (điều kiện đường đi)
- Sử dụng SMT solver (Z3, CVC4, STP) để tìm giá trị cụ thể thỏa mãn điều kiện đó
5.2 Ví dụ minh họa
def check_value(x):
if x > 0:
y = x + 1
else:
y = x - 1
return ySymbolic execution với x = sym_x:
| Đường dẫn | Path Condition | Kết quả |
|---|---|---|
| Đường 1 | sym_x > 0 | y = sym_x + 1 |
| Đường 2 | sym_x <= 0 | y = sym_x - 1 |
void check_input(int x, int y) {
if (x > 10) {
if (y < 5) {
// Condition 1 satisfied!
} else {
// Condition 2 satisfied!
}
} else {
// Condition 3 satisfied!
}
}5.3 Giải constraint bằng Z3
from z3 import *
# Khởi tạo biến tượng trưng
x = Int('x')
y = Int('y')
# Điều kiện cho đường dẫn 1.1 (Condition 1 satisfied)
solver = Solver()
solver.add(x > 10, y < 5)
if solver.check() == sat:
model = solver.model()
print(f"Solution: x={model[x]}, y={model[y]}")
# Output: x=11, y=45.4 Symbolic Execution-guided Fuzzing
Tại sao kết hợp?
- Symbolic Execution đơn thuần: Path explosion — số đường đi tăng theo hàm mũ
- Fuzzing đơn thuần: Không thể chạm đến các nhánh deep với điều kiện phức tạp
- Kết hợp: Symbolic Execution tạo path condition → Solver tìm input → Fuzzing dùng input đó để kiểm thử
Lợi ích:
- Tăng coverage: khám phá nhiều đường đi hơn, kể cả đường hiếm
- Tìm lỗi sâu hơn: buffer overflow, integer overflow, logic bug
- Tối ưu hóa: giảm số lượng test case không hiệu quả
Hạn chế:
- Path explosion: số path tăng theo hàm mũ với số nhánh
- Tốn tài nguyên tính toán (solver, memory)
- Khó áp dụng cho hệ thống có nhiều side-effect, I/O phức tạp
Công cụ hỗ trợ:
| Loại | Công cụ |
|---|---|
| Symbolic Execution | KLEE (C/C++), Manticore (EVM/x86/LLVM) |
| Fuzzing | AFL, LibFuzzer |
| Kết hợp | S2E (Selective Symbolic Execution) |
6. Câu hỏi Trắc nghiệm
Câu 1. Mục tiêu chính của kiểm thử phần mềm là gì?
- A. Chứng minh phần mềm không có lỗi
- B. Phát hiện lỗi, đảm bảo chất lượng và xác minh phần mềm đáp ứng yêu cầu
- C. Tối ưu hóa hiệu năng phần mềm
- D. Viết lại phần mềm từ đầu nếu có lỗi
Câu 2. Black Box Testing khác White Box Testing ở điểm nào cơ bản nhất?
- A. Black Box nhanh hơn
- B. Black Box không cần biết mã nguồn, White Box cần
- C. White Box chỉ kiểm tra giao diện
- D. Black Box chỉ dành cho security testing
Câu 3. Thứ tự đúng của các giai đoạn kiểm thử theo giai đoạn phát triển là:
- A. System → Unit → Integration → Acceptance
- B. Unit → Integration → System → Acceptance
- C. Acceptance → System → Integration → Unit
- D. Integration → Unit → System → Acceptance
Câu 4. Regression Testing được thực hiện khi nào?
- A. Trước khi bắt đầu dự án
- B. Sau khi có thay đổi trong mã nguồn, để đảm bảo tính năng cũ không bị ảnh hưởng
- C. Chỉ khi phát hiện lỗi nghiêm trọng
- D. Sau khi triển khai sản phẩm lên production
Câu 5. Basis Path Testing do ai đề xuất và vào năm nào?
- A. Dijkstra, 1968
- B. McCabe, 1976
- C. Boehm, 1981
- D. Myers, 1979
Câu 6. Trong CFG (Control Flow Graph), “Predicate Node” đại diện cho gì?
- A. Một hàm được gọi
- B. Một biểu thức điều kiện (if/while/for)
- C. Điểm bắt đầu chương trình
- D. Một vòng lặp vô hạn
Câu 7. Statement Coverage (phủ câu lệnh) yêu cầu gì?
- A. Mỗi nhánh true/false đều được thực thi
- B. Mỗi câu lệnh được thực thi ít nhất một lần
- C. Mỗi path từ đầu đến cuối đều được thực thi
- D. Mỗi biến được định nghĩa ít nhất một lần
Câu 8. Với hàm foo(a, b, c, d, e), test case foo(0,0,0,0,0) đạt được bao nhiêu % Statement Coverage nếu câu lệnh if(a==0) return 0 ở đầu hàm?
- A. 100%
- B. 75%
- C. 42%
- D. 50%
Câu 9. Branch Coverage yêu cầu gì thêm so với Statement Coverage?
- A. Mỗi hàm phải được gọi ít nhất 2 lần
- B. Mỗi biểu thức điều kiện phải được đánh giá cả true lẫn false
- C. Tất cả paths phải được kiểm thử
- D. Mỗi vòng lặp phải chạy ít nhất 10 lần
Câu 10. Condition Coverage khác Branch Coverage ở điểm nào?
- A. Condition Coverage kiểm tra điều kiện đơn trong biểu thức phức
- B. Condition Coverage chỉ áp dụng cho vòng lặp
- C. Branch Coverage mạnh hơn Condition Coverage
- D. Không có sự khác biệt
Câu 11. Trong Data-flow Testing, ký hiệu p-use của biến nghĩa là gì?
- A. Biến được dùng trong phép tính (computation)
- B. Biến được dùng trong biểu thức điều kiện (predicate)
- C. Biến vừa được định nghĩa (pre-use)
- D. Biến được print ra màn hình
Câu 12. Pattern anomaly nào trong Data-flow Testing là potential bug?
- A.
du(define → use) - B.
uk(use → kill) - C.
dd(define → define lại không dùng ở giữa) - D.
ud(use → define lại)
Câu 13. DU Path của biến X từ câu lệnh S đến S’ là gì?
- A. Bất kỳ đường đi nào từ S đến S'
- B. Đường đi từ S đến S’ không có định nghĩa lại của X ở giữa
- C. Đường đi ngắn nhất từ S đến S'
- D. Đường đi từ S đến S’ qua tất cả các node
Câu 14. Chiến lược kiểm thử luồng dữ liệu nào mạnh nhất (bao phủ nhiều nhất)?
- A. All-Defs
- B. All-Uses
- C. All-DU-Paths
- D. All-P-Uses
Câu 15. Equivalence Partitioning (EP) giúp giải quyết vấn đề gì trong kiểm thử?
- A. Tăng tốc độ thực thi chương trình
- B. Giảm số lượng test case không cần thiết trong khi vẫn đảm bảo coverage
- C. Tự động sinh test case từ mã nguồn
- D. Phát hiện lỗi bảo mật trong mã nguồn
Câu 16. Khi xếp loại học lực theo điểm [0,10], phân vùng tương đương cho bài toán này có bao nhiêu phân vùng (gồm cả valid và invalid)?
- A. 5
- B. 6
- C. 7
- D. 8
Câu 17. Standard BVA với hàm có 3 biến độc lập tạo ra bao nhiêu test case?
- A. 9
- B. 13
- C. 15
- D. 25
Câu 18. Robust Testing mở rộng Standard BVA bằng cách nào?
- A. Thêm test case ngẫu nhiên
- B. Thêm giá trị
min-1vàmax+1(ngoài miền hợp lệ) cho mỗi biến - C. Test tất cả tổ hợp biên cùng lúc
- D. Tăng gấp đôi số test case
Câu 19. Worst-case Testing với n=2 biến tạo bao nhiêu test case?
- A. 9
- B. 25
- C. 49
- D. 13
Câu 20. BVA hiệu quả nhất trong trường hợp nào?
- A. Các biến đầu vào có quan hệ ràng buộc nhau phức tạp
- B. Các biến đầu vào độc lập nhau và mỗi biến có miền giá trị hữu hạn
- C. Chương trình không có điều kiện rẽ nhánh
- D. Chương trình xử lý chuỗi ký tự phức tạp
Câu 21. Fuzzing là gì?
- A. Kiểm thử phần mềm bằng cách đọc mã nguồn
- B. Tự động gửi dữ liệu không hợp lệ/ngẫu nhiên vào chương trình để phát hiện lỗi
- C. Phân tích tĩnh mã nguồn để tìm lỗ hổng
- D. Kiểm thử hiệu năng dưới tải cao
Câu 22. Fuzzing thuộc loại kiểm thử nào theo mặc định?
- A. White Box Testing
- B. Gray Box Testing
- C. Black Box Testing
- D. Unit Testing
Câu 23. Mutation-based Fuzzing khác Generation-based Fuzzing như thế nào?
- A. Mutation dùng AI, Generation dùng random
- B. Mutation biến đổi input mẫu có sẵn, Generation tạo input từ đặc tả/ngữ pháp
- C. Mutation chỉ dùng cho network protocol, Generation cho file format
- D. Không có sự khác biệt đáng kể
Câu 24. AFL (American Fuzzy Lop) sử dụng kỹ thuật nào để hướng dẫn fuzzing?
- A. Symbolic Execution
- B. Coverage-guided greybox fuzzing (feedback từ code coverage)
- C. Pure random mutation không feedback
- D. Static analysis
Câu 25. Loại lỗi nào Fuzzing kém hiệu quả nhất trong phát hiện?
- A. Buffer overflow
- B. Memory leak
- C. Logic error trong virus/worm không gây crash
- D. Null pointer dereference
Câu 26. Trong quy trình Fuzzing, thành phần nào có nhiệm vụ phân biệt lỗi thật và false positive?
- A. Test Case Generator
- B. Monitor
- C. Bug Filter
- D. Seed Corpus
Câu 27. Grammar-based Fuzzing (Generation-based) có lợi thế gì so với random mutation?
- A. Nhanh hơn nhiều lần
- B. Tạo ra input có cấu trúc đúng định dạng, dễ pass qua parser để test logic sâu hơn
- C. Không cần seed corpus
- D. Tự động phát hiện ngữ pháp của target
Câu 28. Address Sanitizer (ASan) thường được kết hợp với fuzzer để làm gì?
- A. Tăng tốc độ fuzzing
- B. Phát hiện memory errors (buffer overflow, use-after-free) trong quá trình fuzzing
- C. Tạo corpus seed tự động
- D. Phân tích mã nguồn trước khi fuzzing
Câu 29. Symbolic Execution khác với kiểm thử thông thường ở điểm cơ bản nào?
- A. Chạy chương trình nhanh hơn
- B. Biến đầu vào là ký hiệu (symbol) thay vì giá trị cụ thể, phân tích toàn bộ không gian đầu vào
- C. Không cần mã nguồn
- D. Chỉ áp dụng cho chương trình Python
Câu 30. “Path Condition” trong Symbolic Execution là gì?
- A. Số lượng đường dẫn trong chương trình
- B. Tập hợp các ràng buộc (constraints) phải thỏa mãn để thực thi một đường đi cụ thể
- C. Điều kiện để chương trình biên dịch thành công
- D. Đường dẫn file của chương trình đang kiểm thử
Câu 31. SMT Solver được sử dụng trong Symbolic Execution để làm gì?
- A. Biên dịch chương trình
- B. Tìm giá trị đầu vào cụ thể thỏa mãn path condition
- C. Tối ưu hóa mã nguồn
- D. Quản lý bộ nhớ chương trình
Câu 32. Với đoạn code sau, Symbolic Execution tạo ra bao nhiêu đường dẫn?
def f(x, y):
if x > 10:
if y < 5:
return 1
else:
return 2
else:
return 3- A. 2
- B. 3
- C. 4
- D. 5
Câu 33. Vấn đề “Path Explosion” trong Symbolic Execution nghĩa là gì?
- A. Chương trình bị crash trong quá trình phân tích
- B. Số lượng đường dẫn tăng theo hàm mũ với số điều kiện rẽ nhánh → không thể duyệt hết
- C. Bộ nhớ bị tràn khi chạy solver
- D. Các path condition mâu thuẫn nhau
Câu 34. Tại sao kết hợp Symbolic Execution với Fuzzing lại hiệu quả hơn từng phương pháp riêng lẻ?
- A. Vì hai phương pháp cùng ngôn ngữ lập trình
- B. Symbolic Execution tạo input cho các nhánh khó trigger, Fuzzing khám phá rộng nhanh → bù đắp nhược điểm nhau
- C. Vì chi phí thực hiện giảm đi
- D. Vì không cần SMT solver khi kết hợp
Câu 35. Công cụ KLEE được sử dụng cho mục đích gì?
- A. Fuzzing network protocols
- B. Symbolic Execution cho chương trình C/C++
- C. Static analysis cho Java
- D. Coverage measurement
Câu 36. Coverage-based Fuzzing (như AFL) hoạt động theo nguyên tắc nào?
- A. Luôn chọn seed ngắn nhất để fuzz
- B. Giữ lại và ưu tiên seed tạo ra code coverage mới, loại bỏ seed không tạo coverage mới
- C. Chọn seed ngẫu nhiên hoàn toàn
- D. Chỉ fuzz các hàm có annotation @fuzz
Câu 37. Concolic Execution là gì?
- A. Symbolic Execution chỉ cho các vòng lặp
- B. Kết hợp Concrete execution và Symbolic execution: chạy cụ thể nhưng theo dõi ký hiệu song song
- C. Symbolic Execution phân tán trên nhiều máy
- D. Symbolic Execution chỉ cho điều kiện đơn giản
Câu 38. Trong Data-flow Testing, “All-Uses” strategy yêu cầu gì?
- A. Tất cả các vòng lặp phải được thực thi
- B. Mỗi cặp DU (definition-use) của mỗi biến phải có ít nhất một test path
- C. Mỗi biến phải được sử dụng ít nhất một lần
- D. Tất cả các hàm phải được gọi
Câu 39. Tester có kinh nghiệm khác tester ít kinh nghiệm trong ví dụ kiểm thử ktNguyenTo ở chỗ nào?
- A. Tester kinh nghiệm test nhiều giá trị hơn
- B. Tester kinh nghiệm dùng phân vùng tương đương để thiết kế test case có hệ thống, bao gồm cả invalid input
- C. Tester kinh nghiệm chỉ test số nguyên tố
- D. Tester kinh nghiệm test bằng cách đọc mã nguồn
Câu 40. Phần mềm nào sau đây KHÔNG phải là một fuzzer?
- A. AFL (American Fuzzy Lop)
- B. Honggfuzz
- C. Valgrind
- D. LibFuzzer
Câu 41. Scheduling Algorithm trong Fuzzing giải quyết vấn đề gì?
- A. Lên lịch chạy test tự động hàng ngày
- B. Quyết định seed nào trong corpus nên được fuzz tiếp theo để tối đa hóa khả năng khám phá code mới
- C. Phân phối công việc fuzzing trên nhiều máy
- D. Tối ưu thứ tự các biến thể mutation
Câu 42. Dynamic Taint Analysis trong Fuzzing giúp gì?
- A. Phát hiện SQL injection tự động
- B. Theo dõi luồng dữ liệu từ input đến các điểm nhạy cảm, xác định phần nào của input ảnh hưởng đến điều kiện rẽ nhánh
- C. Mã hóa dữ liệu trong quá trình fuzzing
- D. Tạo báo cáo lỗi tự động
Câu 43. Số test case Robust Worst-case Testing với n=2 biến là bao nhiêu?
- A. 25
- B. 36
- C. 49
- D. 64
Câu 44. Câu nào đúng về mối quan hệ giữa Statement Coverage và Branch Coverage?
- A. 100% branch coverage đảm bảo 100% statement coverage
- B. 100% statement coverage đảm bảo 100% branch coverage
- C. Hai loại coverage không liên quan nhau
- D. Branch coverage luôn bằng statement coverage
Câu 45. Data-flow Testing phát hiện được những loại lỗi nào mà Control-flow Testing có thể bỏ qua?
- A. Lỗi chia cho 0
- B. Biến khai báo nhưng không dùng, dùng biến chưa khởi tạo, gán đè giá trị chưa dùng
- C. Buffer overflow
- D. SQL injection
Câu 46. Trong tương lai của Fuzzing, AI/ML được tích hợp chủ yếu để làm gì?
- A. Thay thế hoàn toàn lập trình viên
- B. Làm fuzzer thông minh hơn trong việc chọn seed, mutation strategy và hướng exploration
- C. Tự động vá lỗi phát hiện được
- D. Tạo báo cáo kiểm thử tự động
Câu 47. Với chương trình có biến unsigned 32-bit, nếu chỉ 1 trong 2^32 giá trị kích hoạt một nhánh đặc biệt, fuzzing ngẫu nhiên cần kỳ vọng bao nhiêu lần thử?
- A. ~1,000
- B. ~1,000,000
- C. ~2,147,483,648 (2^31)
- D. ~4,294,967,296 (2^32)
Câu 48. S2E (Selective Symbolic Execution) giải quyết vấn đề path explosion như thế nào?
- A. Giới hạn thời gian chạy solver
- B. Chỉ thực thi ký hiệu (symbolic) cho phần code quan tâm, phần còn lại chạy concrete
- C. Bỏ qua các vòng lặp
- D. Chạy song song nhiều instance
Câu 49. Điểm yếu lớn nhất của Black Box Testing là gì?
- A. Quá tốn thời gian
- B. Không thể phủ hết mọi trường hợp và khó thiết kế test case hiệu quả khi không biết cấu trúc bên trong
- C. Yêu cầu mã nguồn
- D. Chỉ dùng được cho ứng dụng web
Câu 50. Đâu là lý do chính khiến Fuzzing tìm được nhiều lỗi bảo mật quan trọng mà kiểm thử thông thường bỏ qua?
- A. Fuzzing chạy nhanh hơn kiểm thử thông thường
- B. Fuzzing tự động tạo ra hàng triệu input ngẫu nhiên, kể cả các trường hợp biên cực kỳ bất ngờ mà tester thủ công không nghĩ tới
- C. Fuzzing đọc được mã nguồn và hiểu logic
- D. Fuzzing dùng AI nên thông minh hơn
Câu 51. Trong Symbolic Execution, khi gặp điều kiện while(x > 0), vấn đề gì xảy ra?
- A. Chương trình dừng vô điều kiện
- B. Vòng lặp được unroll vô tận tạo ra vô số path → loop không thể handle tốt bởi pure symbolic execution
- C. Symbolic execution bỏ qua vòng lặp
- D. Symbolic execution convert vòng lặp thành đệ quy
Câu 52. Manticore được dùng để Symbolic Execution cho loại chương trình nào?
- A. Chỉ cho Python scripts
- B. EVM (Ethereum Virtual Machine), x86, LLVM IR
- C. Chỉ cho Java bytecode
- D. Chỉ cho Windows PE binaries
Câu 53. Phương pháp kiểm thử nào phù hợp nhất để kiểm tra xem các module đã hoạt động đúng với nhau chưa sau khi tích hợp?
- A. Unit Testing
- B. Acceptance Testing
- C. Integration Testing
- D. Usability Testing
Câu 54. Công thức Worst-case Testing tạo ra 125 test case ứng với bao nhiêu biến?
- A. 2
- B. 3
- C. 4
- D. 5
Câu 55. Tại sao performance testing (kiểm thử hiệu suất) được phân loại vào “non-functional testing”?
- A. Vì nó không cần mã nguồn
- B. Vì nó không kiểm tra chức năng logic mà kiểm tra thuộc tính hành vi như tốc độ, khả năng chịu tải
- C. Vì nó được thực hiện tự động
- D. Vì nó không cần test case