--- tags: minecraft --- # b1.7.3-1.2.5: Exploiting `updateFurnaceBlockState` (April 2022, by Jan & Spheres a.k.a. v3rtices) See [this document](https://hackmd.io/@pa-2w-2MT5iGybHuegbruw/BkaSMRHV9) for some information on TE mechanics. --- <details><summary>pseudo-code</summary> ``` updateFurnaceBlockState burning, blockPos: metadata = getBlockMetadata blockPos tileEntity = getBlockTileEntity blockPos # flag that prevents destroyed furnaces from dropping items set keepFurnaceInventory if burning then setBlockWithNotify blockPos litFurnace else setBlockWithNotify blockPos furnace clear keepFurnaceInventory setBlockMetadataWithNotify blockPos metadata # ONLY b1.8+ -- if tileEntity == null then return # -- validateTileEntity tileEntity setBlockTileEntity pos tileEntity ``` </details> --- 1. Update-furnace-block-state &rarr; move furnace with piston &xrArr; Optionally, permanent lit furnace (if update-furnace-block-state was for burning). ___\[tested in b1.7.3, b1.8 works, 1.0 fixed\]___ _This exploit only works in beta. The piston mobility check in 1.0 will check for the container block instead of the TE._ _\[test for precise version range\]_ `BlockFurnace.updateFurnaceBlockState` always gets executed in the TE phase with TE postponing enabled. The set-block operation (for replacing a furnace with a lit furnace or vice versa) triggers the on-removal procedure of the furnace. This procedure calls `World.removeBlockTileEntity`, which invalidates the original furnace TE (TE postponing behaviour). The on-added procedure of the new furnace in turn calls `World.setBlockTileEntity`, which only adds the new furnace TE to the postponed TE list (not chunk TE map or loaded TE list). When the block updates are emitted, the original (now invalidated) furnace TE is in the chunk TE map. If we are pushing the furnace, the piston will call `World.getBlockTileEntity` during its can-extend check; `World.getBlockTileEntity` will remove the invalid tile entity from the chunk TE map and return `null`. Then `World.getBlockTileEntity` gets called again during the pistons try-extend check; this time it will find a `null` TE. This will trigger the on-added procedure of the furnace (in order to create a new TE and fix the furnace. This does not update the piston again, since the ignore-updates flag is set.) However, TE postponing behaviour prevents a new TE being put in the chunk TE map right away. The furnace has no TE and will pass the piston's mobility check. If we are pulling the furnace, only one `World.getBlockTileEntity` call will take place. --- ## Illegal TEs 1. Update-furnace-block-state &rarr; destroy furnace using headless piston / (beta only) by pulling &xrArr; Air block with furnace TE (__only loaded TE list__), optionally replace air with any other desired block. ___\[tested in b1.7.3, b1.8, 1.2.5 working\]___ _Since this setup relies on instant block events, it should not work in 1.3+_ _\[test for precise version range\]_ Note that the `World.getBlockTileEntity` call on the invalid TE removes it from the chunk TE map. The new empty furnace TE is postponed and after that the old TE gets validated again and its placement is postponed too. The new empty furnace TE later gets added to the loaded TE list, but not the chunk TE map, since there is no container block there. The original TE never gets removed from the loaded TE list. 2. Update-furnace-block-state &rarr; destroy furnace using door _\[add details\]_ &xrArr; Air block with furnace TE (chunk TE map & loaded TE list), optionally replace air with any other desired non-container block. ___\[tested in b1.7.3, b1.8, 1.2.5, 1.7.10 working\]___ _\[test for precise version range\]_ Same as above, but avoiding the `World.getBlockTileEntity` call. 3. Get air block with burning furnace TE (only loaded TE list needed, method described above), place desired container block `c`, wait for update-furnace-block-state (when furnace TE stops burning) &xrArr; Furnace block with `c.getBlockEntity()` TE (chunk TE map & loaded TE list) ___\[tested in b1.7.3, b1.8, 1.2.5, 1.7.10 working\]___ 4. Get air block with burning furnace TE (only loaded TE list needed, method described above), place desired container block `c`, wait for update-furnace-block-state (when furnace TE stops burning) &rarr; destroy furnace using headless piston / (beta only) by pulling &xrArr; Air block with `c.getBlockEntity()` TE (__only loaded TE list__), optionally replace air with any other desired block. ___\[tested in b1.7.3, b1.8, 1.2.5, 1.7.10 working\]___ 5. Get air block with burning furnace TE (only loaded TE list needed, method described above), place desired container block `c`, wait for update-furnace-block-state (when furnace TE stops burning) &rarr; destroy furnace using door _\[add details\]_ &xrArr; Air block with `c.getBlockEntity()` TE (chunk TE map & loaded TE list), optionally replace air with any other desired non-container block. ___\[tested in b1.7.3, b1.8, 1.2.5, 1.7.10 working\]___ --- ## Block transmutation 1. Get air block with burning furnace TE (only loaded TE list needed, method described above), place any block (probably wool) with desired metadata value `m`, wait for update-furnace-block-state (when furnace TE stops burning) &xrArr; Furnace block with metadata value `m`. ___\[tested in b1.7.3, b1.8, 1.2.5, 1.7.10 working\]___ _\[test for precise version range\]_ --- ## Crashes 1. Get air with furnace TE (only loaded TE list, method described above), wait for update-furnace-block-state (when furnace TE stops burning) &xrArr; Update-furnace-block-state throws `java.lang.NullPointerException`. ___\[tested in b1.7.3 working, b1.8 fixed\]___ 2. Get furnace with arbitrary TE that is not furnace TE (method described above), destroy furnace &xrArr; Furnace on-removal throws `java.lang.ClassCastException` (assuming the keep-furnace-inventory flag is off.) ___\[tested in b1.7.3, b1.8, 1.2.5, 1.7.10 working\]___ _\[test for precise version range\]_ 3. Powered piston (any kind) facing a furnace + something to prevent piston from moving furnace: Smelt in furnace &xrArr; Game gets into an infinite loop, crashes with `java.lang.StackOverflowError`. ___\[tested in b1.7.3 working, b1.8 fixed\]___ _\[test for precise version range\]_ Update-furnace-block-state updates the piston. The piston's can-extend calls `World.getBlockTileEntity`, which removes the invalid tile entity from the chunk TE map and return `null`. The can-extend check fails overall. Then the piston is updated again from update-furnace-block-state (this time the metadata is being set.) This time the `World.getBlockTileEntity` finds a `null` TE and executes the on-added procedure of the furnace. The on-added procedure sets the furnaces orientation via metadata; it fails to assign a new TE due to TE postponing. This updates the piston again, repeating the process indefinitely.