|
1 | 1 | import base64 |
2 | 2 | import os |
| 3 | +import platform |
3 | 4 | import subprocess |
4 | 5 | import tempfile |
5 | 6 | from pathlib import Path |
@@ -461,6 +462,121 @@ def test_real_reproduce_pov(libjpeg_oss_fuzz_task_rw: ChallengeTask, libjpeg_cra |
461 | 462 | assert result.did_crash(), "Reproduce POV failed" |
462 | 463 |
|
463 | 464 |
|
| 465 | +@pytest.mark.integration |
| 466 | +@pytest.mark.parametrize( |
| 467 | + "oss_fuzz_commit,description", |
| 468 | + [ |
| 469 | + ("eb47f56bd15626ab4ae8727bc435148dd2f9857c^", "before_bug"), |
| 470 | + ("eb47f56bd15626ab4ae8727bc435148dd2f9857c", "bug_introduced"), |
| 471 | + ("HEAD", "current_head"), |
| 472 | + ], |
| 473 | +) |
| 474 | +def test_helper_py_patching_across_commits( |
| 475 | + tmp_path: Path, libjpeg_crash_testcase: Path, oss_fuzz_commit: str, description: str |
| 476 | +): |
| 477 | + """Test helper.py patching across different OSS-Fuzz commits. |
| 478 | +
|
| 479 | + This test validates that helper.py patching works correctly across different |
| 480 | + OSS-Fuzz commits: |
| 481 | + - Before the bug: eb47f56^ (should work without needing reproduce_impl patch) |
| 482 | + - Bug introduced: eb47f56bd15626ab4ae8727bc435148dd2f9857c (needs patching) |
| 483 | + - Current HEAD: Latest OSS-Fuzz (needs patching) |
| 484 | +
|
| 485 | + The patching should ensure reproduce_pov runs successfully on all commits. |
| 486 | + """ |
| 487 | + # Detect current architecture |
| 488 | + current_arch = platform.machine() |
| 489 | + # Normalize architecture names |
| 490 | + if current_arch in ("arm64", "aarch64"): |
| 491 | + current_arch = "aarch64" |
| 492 | + elif current_arch in ("x86_64", "amd64"): |
| 493 | + current_arch = "x86_64" |
| 494 | + |
| 495 | + # Only test ARM64 on ARM64 platforms, x86_64 on x86_64 platforms |
| 496 | + # For this test we use the current architecture |
| 497 | + |
| 498 | + # Create task directory structure |
| 499 | + task_dir = tmp_path / f"libjpeg-turbo-{description}" |
| 500 | + task_dir.mkdir(parents=True) |
| 501 | + |
| 502 | + oss_fuzz_dir = task_dir / "fuzz-tooling" |
| 503 | + oss_fuzz_dir.mkdir(parents=True) |
| 504 | + source_dir = task_dir / "src" |
| 505 | + source_dir.mkdir(parents=True) |
| 506 | + |
| 507 | + # Clone OSS-Fuzz at specific commit |
| 508 | + subprocess.run(["git", "-C", str(oss_fuzz_dir), "clone", "https://github.com/google/oss-fuzz.git"], check=True) |
| 509 | + subprocess.run( |
| 510 | + ["git", "-C", str(oss_fuzz_dir / "oss-fuzz"), "checkout", oss_fuzz_commit], |
| 511 | + check=True, |
| 512 | + ) |
| 513 | + |
| 514 | + # Clone libjpeg-turbo source (same commit as existing test) |
| 515 | + libjpeg_url = "https://github.com/libjpeg-turbo/libjpeg-turbo" |
| 516 | + subprocess.run(["git", "-C", str(source_dir), "clone", libjpeg_url], check=True) |
| 517 | + subprocess.run( |
| 518 | + ["git", "-C", str(source_dir / "libjpeg-turbo"), "checkout", "6d91e950c871103a11bac2f10c63bf998796c719"], |
| 519 | + check=True, |
| 520 | + ) |
| 521 | + |
| 522 | + # Create task metadata |
| 523 | + TaskMeta( |
| 524 | + project_name="libjpeg-turbo", |
| 525 | + focus="libjpeg-turbo", |
| 526 | + task_id=f"task-id-libjpeg-turbo-{description}", |
| 527 | + metadata={"task_id": f"task-id-libjpeg-turbo-{description}", "round_id": "testing", "team_id": "tob"}, |
| 528 | + ).save(task_dir) |
| 529 | + |
| 530 | + # Create challenge task |
| 531 | + task = ChallengeTask( |
| 532 | + read_only_task_dir=task_dir, |
| 533 | + ) |
| 534 | + |
| 535 | + # Get RW copy and test |
| 536 | + with task.get_rw_copy(None) as local_task: |
| 537 | + # Build image and fuzzers |
| 538 | + result = local_task.build_image(pull_latest_base_image=False, architecture=current_arch) |
| 539 | + assert result.success is True, f"Build image failed: {result.error}" |
| 540 | + |
| 541 | + result = local_task.build_fuzzers(engine="libfuzzer", sanitizer="address", architecture=current_arch) |
| 542 | + assert result.success is True, f"Build fuzzers failed: {result.error}" |
| 543 | + |
| 544 | + # Reproduce the POV - this should work with patching (the fuzzer should run successfully) |
| 545 | + # Note: We're testing that reproduce_pov runs without errors, not necessarily that |
| 546 | + # this specific crash test case triggers a crash (compatibility may vary by version) |
| 547 | + result = local_task.reproduce_pov( |
| 548 | + fuzzer_name="libjpeg_turbo_fuzzer", |
| 549 | + crash_path=libjpeg_crash_testcase, |
| 550 | + ) |
| 551 | + |
| 552 | + # The key is that reproduce_pov should RUN successfully (did_run returns True) |
| 553 | + # This confirms the patching fixed the reproduce_impl issue |
| 554 | + assert result.did_run(), ( |
| 555 | + f"Reproduce POV did not run for commit {oss_fuzz_commit} ({description}). " |
| 556 | + f"Patching may not have worked correctly. Output: {result.command_result.output[:500] if result.command_result.output else 'None'}" |
| 557 | + ) |
| 558 | + |
| 559 | + # Verify that helper.py was patched correctly |
| 560 | + helper_path = local_task.get_oss_fuzz_path() / "infra" / "helper.py" |
| 561 | + assert helper_path.exists(), "helper.py not found" |
| 562 | + |
| 563 | + helper_content = helper_path.read_text() |
| 564 | + |
| 565 | + # Check for the common fix: architecture=architecture instead of err_result |
| 566 | + # The fix should change: return run_function(run_args, err_result) |
| 567 | + # To: return run_function(run_args, architecture=architecture) |
| 568 | + assert "architecture=architecture" in helper_content, ( |
| 569 | + f"helper.py patching failed: 'architecture=architecture' not found in helper.py for commit {oss_fuzz_commit}" |
| 570 | + ) |
| 571 | + |
| 572 | + # On ARM64, verify ARM64-specific patches were applied |
| 573 | + if current_arch == "aarch64": |
| 574 | + # Check for ARM64 manifest tags |
| 575 | + assert ":manifest-arm64v8" in helper_content, ( |
| 576 | + f"helper.py patching failed: ARM64 manifest tags not found for commit {oss_fuzz_commit}" |
| 577 | + ) |
| 578 | + |
| 579 | + |
464 | 580 | def test_copy_task(challenge_task_readonly: ChallengeTask, mock_subprocess): |
465 | 581 | """Test copying a challenge task to a temporary directory.""" |
466 | 582 | with patch.object(ChallengeTask, "_check_python_path"): |
|
0 commit comments