From 76b565cce8f84207f36d329d6ebca4141193afc2 Mon Sep 17 00:00:00 2001 From: kronecker <2026692687@qq.com> Date: Fri, 9 Oct 2026 08:25:39 +0800 Subject: [PATCH] fix(contracts): reject unfulfillable output capture budgets --- backend/src/kf_oj/contracts/judging.py | 8 +- judge/crates/contracts/src/judging.rs | 7 +- ...anifest.invalid-semantic-capture-file.json | 78 +++++++++++++++++++ ...fest.invalid-semantic-capture-storage.json | 78 +++++++++++++++++++ 4 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 tests/fixtures/contracts/judge-manifest.invalid-semantic-capture-file.json create mode 100644 tests/fixtures/contracts/judge-manifest.invalid-semantic-capture-storage.json diff --git a/backend/src/kf_oj/contracts/judging.py b/backend/src/kf_oj/contracts/judging.py index d5adcb1..7b6aa27 100644 --- a/backend/src/kf_oj/contracts/judging.py +++ b/backend/src/kf_oj/contracts/judging.py @@ -109,7 +109,13 @@ class RunLimits(WireModel): @model_validator(mode="after") def consistent(self) -> Self: - if self.stack_bytes > self.memory_bytes or self.file_bytes > self.workdir_bytes: + if ( + self.stack_bytes > self.memory_bytes + or self.file_bytes > self.workdir_bytes + or self.stdout_bytes > self.file_bytes + or self.stderr_bytes > self.file_bytes + or self.stdout_bytes + self.stderr_bytes > self.workdir_bytes + ): raise ValueError("inconsistent resource limits") return self diff --git a/judge/crates/contracts/src/judging.rs b/judge/crates/contracts/src/judging.rs index c28450f..44784aa 100644 --- a/judge/crates/contracts/src/judging.rs +++ b/judge/crates/contracts/src/judging.rs @@ -291,7 +291,12 @@ impl RunLimits { ] { bounded(value, min, max)?; } - if self.stack_bytes > self.memory_bytes || self.file_bytes > self.workdir_bytes { + if self.stack_bytes > self.memory_bytes + || self.file_bytes > self.workdir_bytes + || self.stdout_bytes > self.file_bytes + || self.stderr_bytes > self.file_bytes + || self.stdout_bytes + self.stderr_bytes > self.workdir_bytes + { return Err("inconsistent resource limits"); } Ok(()) diff --git a/tests/fixtures/contracts/judge-manifest.invalid-semantic-capture-file.json b/tests/fixtures/contracts/judge-manifest.invalid-semantic-capture-file.json new file mode 100644 index 0000000..eaef721 --- /dev/null +++ b/tests/fixtures/contracts/judge-manifest.invalid-semantic-capture-file.json @@ -0,0 +1,78 @@ +{ + "schema_version": 1, + "kind": "judge.manifest", + "evaluation_id": "aaaaaaaa-aaaa-4aaa-8aaa-000000000001", + "submission_id": "aaaaaaaa-aaaa-4aaa-8aaa-000000000002", + "problem_version_id": "aaaaaaaa-aaaa-4aaa-8aaa-000000000003", + "language": "cpp20", + "toolchain": { + "toolchain_id": "gnu-cxx-v1", + "image_kind": "linux_gnu_cxx_x86_64_v1", + "image_revision": 1 + }, + "source": { + "digest": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "bytes": 124 + }, + "cases": [ + { + "case_id": "aaaaaaaa-aaaa-4aaa-8aaa-000000000004", + "input": { + "digest": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "bytes": 0 + }, + "expected": { + "digest": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", + "bytes": 0 + } + }, + { + "case_id": "aaaaaaaa-aaaa-4aaa-8aaa-000000000005", + "input": { + "digest": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "bytes": 1 + }, + "expected": { + "digest": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", + "bytes": 1 + } + }, + { + "case_id": "aaaaaaaa-aaaa-4aaa-8aaa-000000000006", + "input": { + "digest": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "bytes": 2 + }, + "expected": { + "digest": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", + "bytes": 2 + } + } + ], + "comparison": "exact_bytes", + "compile_limits": { + "cpu_time_us": 10000000, + "wall_time_us": 30000000, + "memory_bytes": 536870912, + "stack_bytes": 16777216, + "processes": 32, + "open_files": 128, + "stdout_bytes": 1048576, + "stderr_bytes": 1048576, + "file_bytes": 67108864, + "workdir_bytes": 268435456 + }, + "run_limits": { + "cpu_time_us": 1000000, + "wall_time_us": 3000000, + "memory_bytes": 67108864, + "stack_bytes": 8388608, + "processes": 8, + "open_files": 64, + "stdout_bytes": 1048576, + "stderr_bytes": 1048576, + "file_bytes": 524288, + "workdir_bytes": 67108864 + }, + "job_wall_time_us": 60000000 +} diff --git a/tests/fixtures/contracts/judge-manifest.invalid-semantic-capture-storage.json b/tests/fixtures/contracts/judge-manifest.invalid-semantic-capture-storage.json new file mode 100644 index 0000000..fef0c3e --- /dev/null +++ b/tests/fixtures/contracts/judge-manifest.invalid-semantic-capture-storage.json @@ -0,0 +1,78 @@ +{ + "schema_version": 1, + "kind": "judge.manifest", + "evaluation_id": "aaaaaaaa-aaaa-4aaa-8aaa-000000000001", + "submission_id": "aaaaaaaa-aaaa-4aaa-8aaa-000000000002", + "problem_version_id": "aaaaaaaa-aaaa-4aaa-8aaa-000000000003", + "language": "cpp20", + "toolchain": { + "toolchain_id": "gnu-cxx-v1", + "image_kind": "linux_gnu_cxx_x86_64_v1", + "image_revision": 1 + }, + "source": { + "digest": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "bytes": 124 + }, + "cases": [ + { + "case_id": "aaaaaaaa-aaaa-4aaa-8aaa-000000000004", + "input": { + "digest": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "bytes": 0 + }, + "expected": { + "digest": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", + "bytes": 0 + } + }, + { + "case_id": "aaaaaaaa-aaaa-4aaa-8aaa-000000000005", + "input": { + "digest": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "bytes": 1 + }, + "expected": { + "digest": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", + "bytes": 1 + } + }, + { + "case_id": "aaaaaaaa-aaaa-4aaa-8aaa-000000000006", + "input": { + "digest": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "bytes": 2 + }, + "expected": { + "digest": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", + "bytes": 2 + } + } + ], + "comparison": "exact_bytes", + "compile_limits": { + "cpu_time_us": 10000000, + "wall_time_us": 30000000, + "memory_bytes": 536870912, + "stack_bytes": 16777216, + "processes": 32, + "open_files": 128, + "stdout_bytes": 1048576, + "stderr_bytes": 1048576, + "file_bytes": 67108864, + "workdir_bytes": 268435456 + }, + "run_limits": { + "cpu_time_us": 1000000, + "wall_time_us": 3000000, + "memory_bytes": 67108864, + "stack_bytes": 8388608, + "processes": 8, + "open_files": 64, + "stdout_bytes": 1048576, + "stderr_bytes": 1048576, + "file_bytes": 1048576, + "workdir_bytes": 1048576 + }, + "job_wall_time_us": 60000000 +}