lean-0001: Kernel accepts wrong-structure projections, allowing an axiom-free proof of False
A soundness bug in the kernel's handling of nested inductive types: when phantom parameters were dropped from the generated auxiliary type, an ill-typed argument let the kernel accept a proof of False with no axioms. Reachable only through metaprogramming that sends inductive declarations directly to the kernel.
| Status | fixed |
|---|---|
| Fixed in | v4.32.2 |
| Subsystem | kernel |
| Guarantee broken | |
| Mechanism | |
| Symptom | proves-False |
| Severity | unsoundness |
| Fools | |
| Discovery | — |
| Features | |
| References |
Reproduction
Shows the bug on Lean v4.32.1. Adapted from #14576. bugs/lean-0001.lean
/-
On v4.32.1 Lean accepts this file, and `#print axioms boom` reports no axioms.
On v4.32.2 and later the kernel rejects `E` with "invalid projection".
From the issue, as reported.
-/
import Lean
open Lean Elab Command
inductive P : Prop where | mk (b : Bool)
structure C where b : Bool
inductive W : Type where | mk (p : P)
inductive L (α : Type) (b : Bool) : Type where | mk
inductive T : Bool → Prop where | mk : T true
def pad (e : Expr) (n : Nat) : Expr :=
mkApp (mkLambda `x .default (mkConst ``Nat) e) (.lit (.natVal n))
meta def build : CommandElabM Unit := do
let f := pad (mkConst ``Bool.false) 78670
let t := pad (mkConst ``Bool.true) 24083
unless f.hash == t.hash && f.approxDepth == t.approxDepth do
throwError "hash collision failed"
let fw := mkApp (mkConst ``W.mk) (mkApp (mkConst ``P.mk) f)
let tw := mkApp (mkConst ``W.mk) (mkApp (mkConst ``P.mk) t)
let w := mkBVar 0
let Ew := mkApp (mkConst `E) w
let b := mkProj ``C 0 (mkProj ``C 0 w)
let l := mkApp2 (mkConst ``L) Ew b
let Et := mkForall `w .default (mkConst ``W) (mkSort 1)
let ct := mkForall `w .default (mkConst ``W) <|
mkForall `l .default l (mkApp (mkConst `E) (mkBVar 1))
liftCoreM <| addDecl <| .inductDecl [] 1 [{
name := `E, type := Et, ctors := [{ name := `E.mk, type := ct }] }] false
let Et := mkApp (mkConst `E) tw
let l := mkApp2 (mkConst ``L.mk) Et (mkConst ``Bool.true)
liftCoreM <| addDecl <| .defnDecl {
name := `e, levelParams := [], type := Et,
value := mkApp2 (mkConst `E.mk) tw l,
hints := .abbrev, safety := .safe }
liftCoreM <| addDecl <| .defnDecl {
name := `good', levelParams := [],
type := mkApp (mkConst ``T) t, value := mkConst ``T.mk,
hints := .abbrev, safety := .safe }
let Ef := mkApp (mkConst `E) fw
let Et := mkApp (mkConst `E) tw
let a := mkApp2 (mkConst `E.mk) fw (mkProj `E 0 (mkConst `e))
let tl := mkApp2 (mkConst ``L.mk) Et (mkConst ``Bool.true)
let b := mkApp2 (mkConst `E.mk) tw tl
let cT := mkApp2 (mkConst ``L) Et t
let c := mkApp (mkLambda `l .default cT (mkConst ``Unit.unit)) tl
let fl := mkApp2 (mkConst ``L.mk) Ef f
let d := mkApp2 (mkConst `E.mk) fw fl
let v := Expr.letE `a Ef a
(.letE `b Et b
(.letE `c (mkConst ``Unit) c
(.letE `d Ef d (mkConst `good') true) true) true) true
liftCoreM <| addDecl <| .thmDecl {
name := `bad, levelParams := [],
type := mkApp (mkConst ``T) f, value := v }
elab "mkbug" : command => build
mkbug
theorem boom : False := nomatch (bad : T false)
#print axioms bad
#print axioms boom