Repository navigation
verify: Certify Lean proofs from their compiled modules, against proofs in bad faith - #60
Merged
Merged
Conversation
…kernel without elaborating user source.
… results per function.
…n certifier test.
…eports per function.
This was referenced Oct 22, 2026
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.
Certification compiled
Check.lean, which imports the user's modules, and read the text that itsexample : G := G_proofand#print axioms G_proofprinted. A proof author in bad faith could make it print anything. For this file, whereoneclaims a false postcondition (Z3 finds the counterexampleresult = 1):one's postconditionmaintheorem …_proof : True := trivial, and inUser.leantwomacro_rulesthat turn everyexampleinto#eval pure ()and every#print axiomsinto a line saying "does not depend on any axioms"Certified: oneUnresolved: … the proof of fn_5f5a336f6e6576::postcondition@8:37: cppverify_one_99bcee_postcondition_646d2761_proof proves True, not cppverify_one_99bcee_postcondition_646d2761run_cmdthat adds the theorem with the goal's type and the valueTrue.intro, underset_option debug.skipKernelTC true, so no kernel checks itCertified: one… the kernel rejects CppVerify.Proofs.…: while replaying declaration '…_proof': (kernel) declaration type mismatch, '…_proof' has type True but it is expected to have type …cheat, in a module of the project,CppVerify/Lemmas.lean, andtheorem …_proof : … := cheatCertified: one… the kernel rejects CppVerify.Lemmas: while replaying declaration 'cheat': …def …_646d2761 : Prop := True, and proves thatUnresolvedfor both functions (Check.leanimports both definitions)Certified: fine, andoneunresolved:cppverify_one_99bcee_postcondition_646d2761 is not the goal CppVerify.Generated definesaxiom escape {p : Prop} : pbehind a lemmaUnresolvedfor both functionsCertified: fine, andoneunresolved:… depends on the axiom escape, which is not one of Lean's foundationssorryUnresolvedfor both functionsCertified: fine, andoneunresolved:… declaration uses 'sorry'In every case the proofs of
fineare sound. They are now certified whatever happens toone's.Design
Decision 1 of the Lean plan:
Certifiedholds against a proof author in bad faith, so it may rest on nothing the user's Lean source can influence. Vermilion searches its proof files forsorryas text;leanchecker, shipped with the pinned toolchain, replays a module's declarations through the kernel to catch environment hacking. This adds the rest:A checker that reads compiled modules (
Driver/LeanCertifier.h, written into the project as.cppverify/Certify.leanand run withlake env lean --run). It elaborates none of the user's source, so no macro, notation, or command of theirs changes what it checks. For each goal:G_proofmust be a theorem whose type is exactly the constantG, a definition ofCppVerify.Generated, declared in that module;propext,Quot.sound, andClassical.choice;.oleanis not the pinned toolchain's own is replayed through the kernel against its imports, asleanchecker'sreplayFromImportsdoes, once per run.The checker never frees imported data, because a later import in the same process reads it. Freeing it, as
withImportModulesdoes, crashed the second import.Per-function results. Each proof file is built on its own (
lean -EhasSorry), and a proof that does not build fails only its own goal. Each Lean result records its goals (VerifyResult::LeanGoals), and a function isCertifiedwhen every one of them passes. Otherwise it isUnresolved, naming each failing obligation and why.Check.leanis still generated, solake build CppVerify.Checkbuilds every proof at once, but certification no longer reads it.Tests
suite/lean_certifier.test: the six cases in the table, each in its own copy of one project, withfinecertified beside each.lean_certify.cppexpects the new message for its axiom case.mainand before this PR: the same RUN lines with verify: State each Lean goal as named hypotheses, with a bridge the kernel checks #59's binary printCertified: one [backend=Lean]for the rewritten checks, the theorem past the kernel, and the imported one, andUnresolvedforfinewithsorryor the axiom.Mutants of
LeanCertifier.h,Verifier.cpp, andVerifyBackend.cpp, each rebuilt and run against the certification tests: 11 of 13 killed.no kernel replay; only the toolchain's modules replayed; only the proof's own module replayed;
the statement unchecked; the goal's module unchecked (killed by the shadowed goal, which I added after this mutant first survived); axioms allowed; the axioms of the lemmas a proof uses missed;
a function's failed goals ignored; exports recording no goals; a proof that does not build not reported;
sorryallowed in proof files.Two survive, both equivalent:
G_proofis declared in the goal's own proof file. It is not needed for soundness: a proof declared in another module of the project is still replayed by the kernel and has the goal's type. It keeps each goal's proof in its own file, which LM3's moves rely on.Docs
--lean-certifyrow says what certification checks and that results are per function.Checked
lean_certifier.testamong them.docs_examples.testanddocs_coverage.testpass, with the ch17 example marked as failingone; the site builds with-W.VerifyResult::LeanGoals.