diff --git a/changelogs/4.2.md b/changelogs/4.2.md index 96f849148..1e10b6041 100644 --- a/changelogs/4.2.md +++ b/changelogs/4.2.md @@ -107,3 +107,15 @@ Released 17th April 2022. - Fixed a memory leak in RakLib which could result in a server crash when players stay online for a long time. - Fixed server crash when attempting to load a corrupted empty resource pack. - Fixed users with the same name with differerently cased letters being able to duplicate items (userdata is matched by case-insensitive name). + +# 4.2.9 +Released 19th April 2022. + +## Fixes +- Fixed several potential crashes when deserializing item NBT (due to insufficient validation of input data). + +# 4.2.10 +Released 20th April 2022. + +## Fixes +- Fixed performance issue when chat messages received from the client contain many newlines. This security vulnerability was disclosed publicly necessitating a priority fix. diff --git a/changelogs/4.3.md b/changelogs/4.3.md new file mode 100644 index 000000000..6faf3a229 --- /dev/null +++ b/changelogs/4.3.md @@ -0,0 +1,14 @@ +**For Minecraft: Bedrock Edition 1.18.30** + +### Note about API versions +Plugins which don't touch the protocol and compatible with any previous 4.x.y version will also run on these releases and do not need API bumps. +Plugin developers should **only** update their required API to this version if you need the changes in this build. + +**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do. + +# 4.3.0 +Released 20th April 2022. + +## General +- Added support for Minecraft: Bedrock Edition 1.18.30. +- Removed support for older versions. diff --git a/composer.json b/composer.json index 7beb344cb..bb23fa71f 100644 --- a/composer.json +++ b/composer.json @@ -34,8 +34,8 @@ "adhocore/json-comment": "^1.1", "fgrosse/phpasn1": "^2.3", "netresearch/jsonmapper": "^4.0", - "pocketmine/bedrock-data": "~1.6.0+bedrock-1.18.10", - "pocketmine/bedrock-protocol": "~8.0.2+bedrock-1.18.10", + "pocketmine/bedrock-data": "~1.7.0+bedrock-1.18.30", + "pocketmine/bedrock-protocol": "~9.0.0+bedrock-1.18.30", "pocketmine/binaryutils": "^0.2.1", "pocketmine/callback-validator": "^1.0.2", "pocketmine/classloader": "^0.2.0", diff --git a/composer.lock b/composer.lock index 35a3bafb2..bfe4c7a22 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "312b0119034654b3c161ef945b29488e", + "content-hash": "c44633bdafdfb38f4c1490ed01136b60", "packages": [ { "name": "adhocore/json-comment", @@ -249,42 +249,42 @@ }, { "name": "pocketmine/bedrock-data", - "version": "1.6.0+bedrock-1.18.10", + "version": "1.7.0+bedrock-1.18.30", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockData.git", - "reference": "e98c511584a7bd58a95986374d2df4b04c6a2ba0" + "reference": "c8f323ff0cbdb36a5d95e7e4a23969f562445be0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/e98c511584a7bd58a95986374d2df4b04c6a2ba0", - "reference": "e98c511584a7bd58a95986374d2df4b04c6a2ba0", + "url": "https://api.github.com/repos/pmmp/BedrockData/zipball/c8f323ff0cbdb36a5d95e7e4a23969f562445be0", + "reference": "c8f323ff0cbdb36a5d95e7e4a23969f562445be0", "shasum": "" }, "type": "library", "notification-url": "https://packagist.org/downloads/", "license": [ - "LGPL-3.0" + "CC0-1.0" ], "description": "Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP", "support": { "issues": "https://github.com/pmmp/BedrockData/issues", - "source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.18.10" + "source": "https://github.com/pmmp/BedrockData/tree/bedrock-1.18.30" }, - "time": "2022-02-08T19:13:47+00:00" + "time": "2022-04-20T12:40:59+00:00" }, { "name": "pocketmine/bedrock-protocol", - "version": "8.0.2+bedrock-1.18.10", + "version": "9.0.0+bedrock-1.18.30", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockProtocol.git", - "reference": "d1f1afdbb4ea61ea52eb511a79ee1ca561da349c" + "reference": "76cf00af0070dbb3f63a3e7fe74039d011a5475e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/d1f1afdbb4ea61ea52eb511a79ee1ca561da349c", - "reference": "d1f1afdbb4ea61ea52eb511a79ee1ca561da349c", + "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/76cf00af0070dbb3f63a3e7fe74039d011a5475e", + "reference": "76cf00af0070dbb3f63a3e7fe74039d011a5475e", "shasum": "" }, "require": { @@ -298,7 +298,7 @@ "ramsey/uuid": "^4.1" }, "require-dev": { - "phpstan/phpstan": "1.5.3", + "phpstan/phpstan": "1.5.4", "phpstan/phpstan-phpunit": "^1.0.0", "phpstan/phpstan-strict-rules": "^1.0.0", "phpunit/phpunit": "^9.5" @@ -316,9 +316,9 @@ "description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP", "support": { "issues": "https://github.com/pmmp/BedrockProtocol/issues", - "source": "https://github.com/pmmp/BedrockProtocol/tree/8.0.2+bedrock-1.18.10" + "source": "https://github.com/pmmp/BedrockProtocol/tree/9.0.0+bedrock-1.18.30" }, - "time": "2022-04-01T21:55:10+00:00" + "time": "2022-04-20T12:44:11+00:00" }, { "name": "pocketmine/binaryutils", diff --git a/src/block/tile/ContainerTrait.php b/src/block/tile/ContainerTrait.php index 2abe134f2..ba8ab084a 100644 --- a/src/block/tile/ContainerTrait.php +++ b/src/block/tile/ContainerTrait.php @@ -44,7 +44,7 @@ trait ContainerTrait{ abstract public function getRealInventory(); protected function loadItems(CompoundTag $tag) : void{ - if(($inventoryTag = $tag->getTag(Container::TAG_ITEMS)) instanceof ListTag){ + if(($inventoryTag = $tag->getTag(Container::TAG_ITEMS)) instanceof ListTag && $inventoryTag->getTagType() === NBT::TAG_Compound){ $inventory = $this->getRealInventory(); $listeners = $inventory->getListeners()->toArray(); $inventory->getListeners()->remove(...$listeners); //prevent any events being fired by initialization diff --git a/src/entity/Human.php b/src/entity/Human.php index 771696c41..a0b566542 100644 --- a/src/entity/Human.php +++ b/src/entity/Human.php @@ -55,6 +55,7 @@ use pocketmine\network\mcpe\protocol\types\DeviceOS; use pocketmine\network\mcpe\protocol\types\entity\EntityIds; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties; use pocketmine\network\mcpe\protocol\types\entity\StringMetadataProperty; +use pocketmine\network\mcpe\protocol\types\GameMode; use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper; use pocketmine\network\mcpe\protocol\types\PlayerListEntry; use pocketmine\player\Player; @@ -479,6 +480,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ $this->location->yaw, $this->location->yaw, //TODO: head yaw ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($this->getInventory()->getItemInHand())), + GameMode::SURVIVAL, $this->getAllNetworkData(), AdventureSettingsPacket::create(0, 0, 0, 0, 0, $this->getId()), //TODO [], //TODO: entity links diff --git a/src/item/Banner.php b/src/item/Banner.php index 18d0f1081..ea7320b79 100644 --- a/src/item/Banner.php +++ b/src/item/Banner.php @@ -29,6 +29,7 @@ use pocketmine\block\utils\BannerPatternLayer; use pocketmine\block\utils\DyeColor; use pocketmine\data\bedrock\BannerPatternTypeIdMap; use pocketmine\data\bedrock\DyeColorIdMap; +use pocketmine\nbt\NBT; use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\ListTag; use function count; @@ -98,7 +99,7 @@ class Banner extends ItemBlockWallOrFloor{ $colorIdMap = DyeColorIdMap::getInstance(); $patternIdMap = BannerPatternTypeIdMap::getInstance(); $patterns = $tag->getListTag(self::TAG_PATTERNS); - if($patterns !== null){ + if($patterns !== null && $patterns->getTagType() === NBT::TAG_Compound){ /** @var CompoundTag $t */ foreach($patterns as $t){ $patternColor = $colorIdMap->fromInvertedId($t->getInt(self::TAG_PATTERN_COLOR)) ?? DyeColor::BLACK(); //TODO: missing pattern colour should be an error diff --git a/src/item/Item.php b/src/item/Item.php index b490ac6b1..9c1119db3 100644 --- a/src/item/Item.php +++ b/src/item/Item.php @@ -306,7 +306,7 @@ class Item implements \JsonSerializable{ $this->canPlaceOn = []; $canPlaceOn = $tag->getListTag("CanPlaceOn"); - if($canPlaceOn !== null){ + if($canPlaceOn !== null && $canPlaceOn->getTagType() === NBT::TAG_String){ /** @var StringTag $entry */ foreach($canPlaceOn as $entry){ $this->canPlaceOn[$entry->getValue()] = $entry->getValue(); @@ -314,7 +314,7 @@ class Item implements \JsonSerializable{ } $this->canDestroy = []; $canDestroy = $tag->getListTag("CanDestroy"); - if($canDestroy !== null){ + if($canDestroy !== null && $canDestroy->getTagType() === NBT::TAG_String){ /** @var StringTag $entry */ foreach($canDestroy as $entry){ $this->canDestroy[$entry->getValue()] = $entry->getValue(); diff --git a/src/network/mcpe/convert/RuntimeBlockMapping.php b/src/network/mcpe/convert/RuntimeBlockMapping.php index 75835be0f..a6344eb79 100644 --- a/src/network/mcpe/convert/RuntimeBlockMapping.php +++ b/src/network/mcpe/convert/RuntimeBlockMapping.php @@ -48,9 +48,16 @@ final class RuntimeBlockMapping{ /** @var CompoundTag[] */ private $bedrockKnownStates; - private function __construct(){ + private static function make() : self{ + return new self( + Path::join(\pocketmine\BEDROCK_DATA_PATH, "canonical_block_states.nbt"), + Path::join(\pocketmine\BEDROCK_DATA_PATH, "r12_to_current_block_map.bin") + ); + } + + public function __construct(string $canonicalBlockStatesFile, string $r12ToCurrentBlockMapFile){ $stream = PacketSerializer::decoder( - Utils::assumeNotFalse(file_get_contents(Path::join(\pocketmine\BEDROCK_DATA_PATH, "canonical_block_states.nbt")), "Missing required resource file"), + Utils::assumeNotFalse(file_get_contents($canonicalBlockStatesFile), "Missing required resource file"), 0, new PacketSerializerContext(GlobalItemTypeDictionary::getInstance()->getDictionary()) ); @@ -60,15 +67,15 @@ final class RuntimeBlockMapping{ } $this->bedrockKnownStates = $list; - $this->setupLegacyMappings(); + $this->setupLegacyMappings($r12ToCurrentBlockMapFile); } - private function setupLegacyMappings() : void{ + private function setupLegacyMappings(string $r12ToCurrentBlockMapFile) : void{ $legacyIdMap = LegacyBlockIdToStringIdMap::getInstance(); /** @var R12ToCurrentBlockMapEntry[] $legacyStateMap */ $legacyStateMap = []; $legacyStateMapReader = PacketSerializer::decoder( - Utils::assumeNotFalse(file_get_contents(Path::join(\pocketmine\BEDROCK_DATA_PATH, "r12_to_current_block_map.bin")), "Missing required resource file"), + Utils::assumeNotFalse(file_get_contents($r12ToCurrentBlockMapFile), "Missing required resource file"), 0, new PacketSerializerContext(GlobalItemTypeDictionary::getInstance()->getDictionary()) ); diff --git a/src/network/mcpe/serializer/ChunkSerializer.php b/src/network/mcpe/serializer/ChunkSerializer.php index 0fe5d891a..06e60cb40 100644 --- a/src/network/mcpe/serializer/ChunkSerializer.php +++ b/src/network/mcpe/serializer/ChunkSerializer.php @@ -78,7 +78,7 @@ final class ChunkSerializer{ //TODO: right now we don't support 3D natively, so we just 3Dify our 2D biomes so they fill the column $encodedBiomePalette = self::serializeBiomesAsPalette($chunk); - $stream->put(str_repeat($encodedBiomePalette, 25)); + $stream->put(str_repeat($encodedBiomePalette, 24)); $stream->putByte(0); //border block array count //Border block entry format: 1 byte (4 bits X, 4 bits Z). These are however useless since they crash the regular client. diff --git a/src/player/Player.php b/src/player/Player.php index 3826b0601..69bdbaa34 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -1377,8 +1377,14 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ public function chat(string $message) : bool{ $this->removeCurrentWindow(); + //Fast length check, to make sure we don't get hung trying to explode MBs of string ... + $maxTotalLength = $this->messageCounter * (self::MAX_CHAT_BYTE_LENGTH + 1); + if(strlen($message) > $maxTotalLength){ + return false; + } + $message = TextFormat::clean($message, false); - foreach(explode("\n", $message) as $messagePart){ + foreach(explode("\n", $message, $this->messageCounter + 1) as $messagePart){ if(trim($messagePart) !== "" && strlen($messagePart) <= self::MAX_CHAT_BYTE_LENGTH && mb_strlen($messagePart, 'UTF-8') <= self::MAX_CHAT_CHAR_LENGTH && $this->messageCounter-- > 0){ if(strpos($messagePart, './') === 0){ $messagePart = substr($messagePart, 1); diff --git a/src/world/particle/FloatingTextParticle.php b/src/world/particle/FloatingTextParticle.php index 1fe3ee008..a8358ce81 100644 --- a/src/world/particle/FloatingTextParticle.php +++ b/src/world/particle/FloatingTextParticle.php @@ -36,6 +36,7 @@ use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties; use pocketmine\network\mcpe\protocol\types\entity\FloatMetadataProperty; use pocketmine\network\mcpe\protocol\types\entity\LongMetadataProperty; +use pocketmine\network\mcpe\protocol\types\GameMode; use pocketmine\network\mcpe\protocol\types\inventory\ItemStack; use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper; use pocketmine\network\mcpe\protocol\types\PlayerListEntry; @@ -117,6 +118,7 @@ class FloatingTextParticle implements Particle{ 0, 0, ItemStackWrapper::legacy(ItemStack::null()), + GameMode::SURVIVAL, $actorMetadata, AdventureSettingsPacket::create(0, 0, 0, 0, 0, $this->entityId), [],