UniswapFeature: Unchecked returndatasize in low-level external calls
`UniswapFeature` makes a number of external calls from low-level assembly code. Two of these calls rely on the `CALL` opcode to copy the returndata to memory without checking that the call returned the expected amount of data. Because the `CALL` opcode does not zero memory if the call returns less data than expected, this can lead to usage of dirty memory under the assumption that it is data returned from the most recent call.\\nCall to `UniswapV2Pair.getReserves()`\\n```\\n// Call pair.getReserves(), store the results at `0xC00`\\nmstore(0xB00, UNISWAP\\_PAIR\\_RESERVES\\_CALL\\_SELECTOR\\_32)\\nif iszero(staticcall(gas(), pair, 0xB00, 0x4, 0xC00, 0x40)) {\\n    bubbleRevert()\\n}\\n```\\n\\nCall to `ERC20.allowance()`\\n```\\n// Check if we have enough direct allowance by calling\\n// `token.allowance()``\\nmstore(0xB00, ALLOWANCE\\_CALL\\_SELECTOR\\_32)\\nmstore(0xB04, caller())\\nmstore(0xB24, address())\\nlet success := call(gas(), token, 0, 0xB00, 0x44, 0xC00, 0x20)\\n```\\n
Instead of providing a memory range for `call()` to write returndata to, explicitly check `returndatasize()` after the call is made and then copy the data into memory using `returndatacopy()`.\\n```\\nif lt(returndatasize(), EXPECTED\\_SIZE) {\\n    revert(0, 0) \\n}\\nreturndatacopy(0xC00, 0x00, EXPECTED\\_SIZE)\\n```\\n
```\\n// Call pair.getReserves(), store the results at `0xC00`\\nmstore(0xB00, UNISWAP\\_PAIR\\_RESERVES\\_CALL\\_SELECTOR\\_32)\\nif iszero(staticcall(gas(), pair, 0xB00, 0x4, 0xC00, 0x40)) {\\n    bubbleRevert()\\n}\\n```\\n