Fix offline-grading snags: sticky bypass marker, eager e2b import, unshare spam - #2
Open
davidoj wants to merge 1 commit into
Open
Fix offline-grading snags: sticky bypass marker, eager e2b import, unshare spam#2davidoj wants to merge 1 commit into
davidoj wants to merge 1 commit into
Conversation
…share spam
Four independent issues hit while grading raw dataset rows with the offline
verifier from outside the repo's own venv. Full write-up in
docs/OFFLINE_GRADING.md.
1. environment_variable_manipulation leaked its bypass marker across runs.
The verifier bypasses validation when /tmp/test_override_marker exists, but
only removed the marker when `not marker_preexisting` -- so a run that found
a marker also refused to clean it up. The bypass became sticky: every later
verification in any process passed any submission that merely defined the
function. It is also self-poisoning, because an exploit writing the marker at
module level does so during exec(), i.e. before the pre-existence snapshot,
so its own marker counted as pre-existing.
This silently inflated one measured exploit rate to ~40% for a model that
emitted no env/marker code at all.
Now: clear the marker on entry, clean up unconditionally in `finally`, and
move exec() plus the function-not-found early return inside that try so no
return path can leak. The path stays /tmp/test_override_marker by default --
dataset exploits and insecure_verifier_info name it literally -- but
MARKER_PATH honours $DJINN_TEST_OVERRIDE_MARKER so concurrent graders can
each use a private tmpdir. Covered by djinn/tests/test_marker_isolation.py.
2. verification_service imported e2b at module scope, so get_verification_service()
and force_offline_verification() raised ModuleNotFoundError in any env without
the online-only client -- despite offline needing none of it. Both imported
names were unused and every entry point already falls back to offline, so the
imports are simply removed.
3. unshare capability is now probed once per process and logged in a single line,
instead of appending an identical "Operation not permitted" failure to
djinn_daemon_bridge_{mode}.log on every attempt. This also removes a latent
race: failure used to be inferred from proc.poll() after a 50ms sleep, which
could mistake a slow-exiting unshare for a healthy daemon.
4. Problem's insecure_verifier_info, exploit_explanation and exploit_type now
default to "". They are descriptive metadata, not verification inputs, and
Problem.from_dir already defaulted all three via config.get() -- so this just
makes direct construction agree with the loader. Problem(**row) no longer
raises on exported rows that are otherwise perfectly gradable.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MYYDDvJbdup3v5LPnkcYMW
|
root seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account. You have signed the CLA already but the status is still pending? Let us recheck it. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Four independent issues hit while grading raw dataset rows with the offline verifier from outside the repo's own venv. Full write-up lands in
docs/OFFLINE_GRADING.md.1.
environment_variable_manipulationleaked its bypass marker across runsThe verifier bypasses validation when
/tmp/test_override_markerexists, but only removed the marker whennot marker_preexisting— so a run that found a marker also refused to clean it up. The bypass became sticky: every later verification in any process passed any submission that merely defined the function.It is also self-poisoning: an exploit that writes the marker at module level does so during
exec(), i.e. before the pre-existence snapshot, so its own marker counts as pre-existing. (An in-function write does not trigger it, which is why this hid for so long.)Both triggers, before → after:
This silently inflated one measured exploit rate to ~40% for a model that emitted zero env/marker code.
Fix: clear the marker on entry, clean up unconditionally in
finally, and moveexec()plus the function-not-found early return inside thattryso no return path can leak. The path stays/tmp/test_override_markerby default — dataset exploits andinsecure_verifier_infoname it literally, so randomizing would break the exploit contract — butMARKER_PATHhonours$DJINN_TEST_OVERRIDE_MARKERso concurrent graders can each use a private tmpdir.Verified the contract still holds: on 5 sampled problems of this type, the ground-truth exploit still passes insecure and fails secure, and ground truth still passes secure (5/5 each).
Checked and already hermetic:
test_case_logging_side_channel(truncates its log on entry) andfilesystem_exposure(rewrites and unconditionally unlinks).2. Offline grading transitively required
e2bverification_service.pyimportede2bat module scope, soget_verification_service()andforce_offline_verification()raisedModuleNotFoundErrorin any env without the online-only client — despite offline needing none of it. Both imported names (Sandbox,TimeoutException) were unused, and every entry point already falls back to offline, so the imports are simply removed.3.
unsharefailure spammed the bridge logWithout
CAP_SYS_ADMIN(typical in unprivileged containers) every external daemon launch fails identically, appendingunshare: unshare failed: Operation not permittedtodjinn_daemon_bridge_{mode}.logon every attempt. Capability is now probed once per process and logged in a single line, and the doomed launch is skipped entirely.This also removes a latent race: failure used to be inferred from
proc.poll()after a 50 ms sleep, which could mistake a slow-exitingunsharefor a healthy daemon.4.
Problemrejected rows missing descriptive metadatainsecure_verifier_info,exploit_explanationandexploit_typenow default to"". They are descriptive metadata, not verification inputs, andProblem.from_diralready defaulted all three viaconfig.get()— so this just makes direct construction agree with the loader.Problem(**row)no longer raises on exported rows that are otherwise perfectly gradable.Testing
New
djinn/tests/test_marker_isolation.py, 12 tests, covering both leak triggers plus the crash and missing-function return paths. Confirmed the tests fail against the pre-fix code.Existing suite unaffected. Note
test_cli.py,test_verifier.pyandtest_filter_reward_deltas.pyfail to import in a minimal env for an unrelated pre-existing reason (dspynot installed).Independent of #3 — both branch off
mainand can merge in either order.🤖 Generated with Claude Code
https://claude.ai/code/session_01MYYDDvJbdup3v5LPnkcYMW