Skip to content

Commit

Permalink
Fix arbitrary item creation and server freeze vulnerability
Browse files Browse the repository at this point in the history
  • Loading branch information
pau101 committed Aug 11, 2016
1 parent 5689608 commit 32a0163
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 31 deletions.
11 changes: 5 additions & 6 deletions src/main/java/mezz/jei/network/packets/PacketRecipeTransfer.java
Expand Up @@ -8,7 +8,6 @@
import java.util.Map;

import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.network.PacketBuffer;

import mezz.jei.network.IPacketId;
Expand All @@ -17,7 +16,7 @@

public class PacketRecipeTransfer extends PacketJEI {

private Map<Integer, ItemStack> recipeMap;
private Map<Integer, Integer> recipeMap;
private List<Integer> craftingSlots;
private List<Integer> inventorySlots;
private boolean maxTransfer;
Expand All @@ -26,7 +25,7 @@ public PacketRecipeTransfer() {

}

public PacketRecipeTransfer(@Nonnull Map<Integer, ItemStack> recipeMap, @Nonnull List<Integer> craftingSlots, @Nonnull List<Integer> inventorySlots, boolean maxTransfer) {
public PacketRecipeTransfer(@Nonnull Map<Integer, Integer> recipeMap, @Nonnull List<Integer> craftingSlots, @Nonnull List<Integer> inventorySlots, boolean maxTransfer) {
this.recipeMap = recipeMap;
this.craftingSlots = craftingSlots;
this.inventorySlots = inventorySlots;
Expand All @@ -44,7 +43,7 @@ public void readPacketData(PacketBuffer buf, EntityPlayer player) throws IOExcep
recipeMap = new HashMap<>(recipeMapSize);
for (int i = 0; i < recipeMapSize; i++) {
int slotIndex = buf.readVarIntFromBuffer();
ItemStack recipeItem = buf.readItemStackFromBuffer();
int recipeItem = buf.readVarIntFromBuffer();
recipeMap.put(slotIndex, recipeItem);
}

Expand All @@ -70,9 +69,9 @@ public void readPacketData(PacketBuffer buf, EntityPlayer player) throws IOExcep
@Override
public void writePacketData(PacketBuffer buf) throws IOException {
buf.writeVarIntToBuffer(recipeMap.size());
for (Map.Entry<Integer, ItemStack> recipeMapEntry : recipeMap.entrySet()) {
for (Map.Entry<Integer, Integer> recipeMapEntry : recipeMap.entrySet()) {
buf.writeVarIntToBuffer(recipeMapEntry.getKey());
buf.writeItemStackToBuffer(recipeMapEntry.getValue());
buf.writeVarIntToBuffer(recipeMapEntry.getValue());
}

buf.writeVarIntToBuffer(craftingSlots.size());
Expand Down
24 changes: 18 additions & 6 deletions src/main/java/mezz/jei/transfer/BasicRecipeTransferHandler.java
Expand Up @@ -81,7 +81,7 @@ public IRecipeTransferError transferRecipe(@Nonnull Container container, @Nonnul
return handlerHelper.createInternalError();
}

List<ItemStack> availableItemStacks = new ArrayList<>();
Map<Integer, ItemStack> availableItemStacks = new HashMap<>();
int filledCraftSlotCount = 0;
int emptySlotCount = 0;

Expand All @@ -92,13 +92,13 @@ public IRecipeTransferError transferRecipe(@Nonnull Container container, @Nonnul
return handlerHelper.createInternalError();
}
filledCraftSlotCount++;
availableItemStacks.add(slot.getStack().copy());
availableItemStacks.put(slot.slotNumber, slot.getStack().copy());
}
}

for (Slot slot : inventorySlots.values()) {
if (slot.getHasStack()) {
availableItemStacks.add(slot.getStack().copy());
availableItemStacks.put(slot.slotNumber, slot.getStack().copy());
} else {
emptySlotCount++;
}
Expand All @@ -124,15 +124,15 @@ public IRecipeTransferError transferRecipe(@Nonnull Container container, @Nonnul
Collections.sort(inventorySlotIndexes);

// check that the slots exist and can be altered
for (Map.Entry<Integer, ItemStack> entry : matchingItemsResult.matchingItems.entrySet()) {
for (Map.Entry<Integer, Integer> entry : matchingItemsResult.matchingItems.entrySet()) {
int craftNumber = entry.getKey();
int slotNumber = craftingSlotIndexes.get(craftNumber);
if (slotNumber >= container.inventorySlots.size()) {
Log.error("Recipes Transfer Helper {} references slot {} outside of the inventory's size {}", transferHelper.getClass(), slotNumber, container.inventorySlots.size());
return handlerHelper.createInternalError();
}
Slot slot = container.getSlot(slotNumber);
ItemStack stack = entry.getValue();
ItemStack stack = container.getSlot(entry.getValue()).getStack();
if (slot == null) {
Log.error("The slot number {} does not exist in the container.", slotNumber);
return handlerHelper.createInternalError();
Expand All @@ -154,10 +154,22 @@ public IRecipeTransferError transferRecipe(@Nonnull Container container, @Nonnul
/**
* Called server-side to actually put the items in place.
*/
public static void setItems(@Nonnull EntityPlayer player, @Nonnull Map<Integer, ItemStack> slotMap, @Nonnull List<Integer> craftingSlots, @Nonnull List<Integer> inventorySlots, boolean maxTransfer) {
public static void setItems(@Nonnull EntityPlayer player, @Nonnull Map<Integer, Integer> slotIdMap, @Nonnull List<Integer> craftingSlots, @Nonnull List<Integer> inventorySlots, boolean maxTransfer) {
Container container = player.openContainer;
StackHelper stackHelper = Internal.getStackHelper();

// grab items from slots
Map<Integer, ItemStack> slotMap = new HashMap<>(slotIdMap.size());
for (Map.Entry<Integer, Integer> entry : slotIdMap.entrySet()) {
Slot slot = container.getSlot(entry.getValue());
if (slot == null || !slot.getHasStack()) {
return;
}
ItemStack stack = slot.getStack().copy();
stack.stackSize = 1;
slotMap.put(entry.getKey(), stack);
}

// remove required recipe items
int removedSets = removeSetsFromInventory(container, slotMap.values(), craftingSlots, inventorySlots, maxTransfer);
if (removedSets == 0) {
Expand Down
147 changes: 128 additions & 19 deletions src/main/java/mezz/jei/util/StackHelper.java
Expand Up @@ -8,6 +8,7 @@
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
Expand Down Expand Up @@ -76,7 +77,7 @@ public String getOreDictEquivalent(@Nonnull Collection<ItemStack> itemStacks) {
* Returns a result that contains missingItems if there are not enough items in availableItemStacks.
*/
@Nonnull
public MatchingItemsResult getMatchingItems(@Nonnull List<ItemStack> availableItemStacks, @Nonnull Map<Integer, ? extends IGuiIngredient<ItemStack>> ingredientsMap) {
public MatchingItemsResult getMatchingItems(@Nonnull Map<Integer, ItemStack> availableItemStacks, @Nonnull Map<Integer, ? extends IGuiIngredient<ItemStack>> ingredientsMap) {
MatchingItemsResult matchingItemResult = new MatchingItemsResult();

int recipeSlotNumber = -1;
Expand All @@ -92,16 +93,17 @@ public MatchingItemsResult getMatchingItems(@Nonnull List<ItemStack> availableIt
if (requiredStacks.isEmpty()) {
continue;
}

ItemStack matching = containsStack(availableItemStacks, requiredStacks);
Integer matching = containsAnyStackIndexed(availableItemStacks, requiredStacks);
if (matching == null) {
matchingItemResult.missingItems.add(key);
} else {
ItemStack matchingSplit = matching.splitStack(1);
if (matching.stackSize == 0) {
ItemStack matchingStack = availableItemStacks.get(matching);
matchingStack.stackSize--;
if (matchingStack.stackSize == 0) {
availableItemStacks.remove(matching);
}
matchingItemResult.matchingItems.put(recipeSlotNumber, matchingSplit);
matchingItemResult.matchingItems.put(recipeSlotNumber, matching);
}
}

Expand All @@ -122,15 +124,19 @@ public Slot getSlotWithStack(@Nonnull Container container, @Nonnull Iterable<Int
return null;
}

public boolean containsSameStacks(@Nonnull Collection<ItemStack> stacks, @Nonnull Collection<ItemStack> contains) {
return containsSameStacks(new MatchingIterable(stacks), new MatchingIterable(contains));
}

/** Returns true if all stacks from "contains" are found in "stacks" and the opposite is true as well. */
public boolean containsSameStacks(@Nonnull Iterable<ItemStack> stacks, @Nonnull Iterable<ItemStack> contains) {
for (ItemStack stack : contains) {
public <R> boolean containsSameStacks(@Nonnull Iterable<ItemStackMatchable<R>> stacks, @Nonnull Iterable<ItemStackMatchable<R>> contains) {
for (ItemStackMatchable stack : contains) {
if (containsStack(stacks, stack) == null) {
return false;
}
}

for (ItemStack stack : stacks) {
for (ItemStackMatchable stack : stacks) {
if (containsStack(contains, stack) == null) {
return false;
}
Expand All @@ -139,15 +145,26 @@ public boolean containsSameStacks(@Nonnull Iterable<ItemStack> stacks, @Nonnull
return true;
}

public Integer containsAnyStackIndexed(@Nullable Map<Integer, ItemStack> stacks, @Nullable Iterable<ItemStack> contains) {
return containsStackMatchable(stacks == null ? null : new MatchingIndexed(stacks), contains == null ? null : (Iterable<ItemStackMatchable<?>>) (Iterable<?>) new MatchingIterable(contains));
}

public ItemStack containsStack(@Nullable Iterable<ItemStack> stacks, @Nullable ItemStack contains) {
return containsAnyStack(stacks == null ? null : stacks, contains == null ? null : Collections.singletonList(contains));
}

public ItemStack containsAnyStack(@Nullable Iterable<ItemStack> stacks, @Nullable Iterable<ItemStack> contains) {
return containsStackMatchable(stacks == null ? null : new MatchingIterable(stacks), contains == null ? null : (Iterable<ItemStackMatchable<?>>) (Iterable<?>) new MatchingIterable(contains));
}

/* Returns an ItemStack from "stacks" if it isEquivalent to an ItemStack from "contains" */
@Nullable
public ItemStack containsStack(@Nullable Iterable<ItemStack> stacks, @Nullable Iterable<ItemStack> contains) {
public <R> R containsStackMatchable(@Nullable Iterable<ItemStackMatchable<R>> stacks, @Nullable Iterable<ItemStackMatchable<?>> contains) {
if (stacks == null || contains == null) {
return null;
}

for (ItemStack containStack : contains) {
ItemStack matchingStack = containsStack(stacks, containStack);
for (ItemStackMatchable<?> containStack : contains) {
R matchingStack = containsStack(stacks, containStack);
if (matchingStack != null) {
return matchingStack;
}
Expand All @@ -157,15 +174,14 @@ public ItemStack containsStack(@Nullable Iterable<ItemStack> stacks, @Nullable I
}

/* Returns an ItemStack from "stacks" if it isEquivalent to "contains" */
@Nullable
public ItemStack containsStack(@Nullable Iterable<ItemStack> stacks, @Nullable ItemStack contains) {
public <R> R containsStack(@Nullable Iterable<ItemStackMatchable<R>> stacks, @Nullable ItemStackMatchable<?> contains) {
if (stacks == null || contains == null) {
return null;
}

for (ItemStack stack : stacks) {
if (isEquivalent(contains, stack)) {
return stack;
for (ItemStackMatchable<R> stack : stacks) {
if (isEquivalent(contains.getStack(), stack.getStack())) {
return stack.getResult();
}
}
return null;
Expand Down Expand Up @@ -476,8 +492,101 @@ public int addStack(@Nonnull Container container, @Nonnull Collection<Integer> s

public static class MatchingItemsResult {
@Nonnull
public final Map<Integer, ItemStack> matchingItems = new HashMap<>();
public final Map<Integer, Integer> matchingItems = new HashMap<>();
@Nonnull
public final List<Integer> missingItems = new ArrayList<>();
}

private interface ItemStackMatchable<R> {
@Nonnull
ItemStack getStack();
@Nonnull
R getResult();
}

private static abstract class DelegateIterator<T, R> implements Iterator<R> {
@Nonnull
protected final Iterator<T> delegate;

public DelegateIterator(@Nonnull Iterator<T> delegate) {
this.delegate = delegate;
}

@Override
public boolean hasNext() {
return delegate.hasNext();
}

@Override
public void remove() {
delegate.remove();
}
}

private static class MatchingIterable implements Iterable<ItemStackMatchable<ItemStack>> {
@Nonnull
private final Iterable<ItemStack> list;

public MatchingIterable(@Nonnull Iterable<ItemStack> list) {
this.list = list;
}

@Nonnull
@Override
public Iterator<ItemStackMatchable<ItemStack>> iterator() {
Iterator<ItemStack> stacks = list.iterator();
return new DelegateIterator<ItemStack, ItemStackMatchable<ItemStack>>(stacks) {
@Override
public ItemStackMatchable<ItemStack> next() {
final ItemStack stack = delegate.next();
return new ItemStackMatchable<ItemStack>() {
@Nonnull
@Override
public ItemStack getStack() {
return stack;
}

@Nonnull
@Override
public ItemStack getResult() {
return stack;
}
};
}
};
}
}

private static class MatchingIndexed implements Iterable<ItemStackMatchable<Integer>> {
@Nonnull
private final Map<Integer, ItemStack> map;

public MatchingIndexed(@Nonnull Map<Integer, ItemStack> map) {
this.map = map;
}

@Nonnull
@Override
public Iterator<ItemStackMatchable<Integer>> iterator() {
return new DelegateIterator<Map.Entry<Integer, ItemStack>, ItemStackMatchable<Integer>>(map.entrySet().iterator()) {
@Override
public ItemStackMatchable<Integer> next() {
final Map.Entry<Integer, ItemStack> entry = delegate.next();
return new ItemStackMatchable<Integer>() {
@Nonnull
@Override
public ItemStack getStack() {
return entry.getValue();
}

@Nonnull
@Override
public Integer getResult() {
return entry.getKey();
}
};
}
};
}
}
}

0 comments on commit 32a0163

Please sign in to comment.