code-423n4 / 2023-08-dopex-findings

3 stars 3 forks source link

[M-06] Integer Overflow and Underflow PerpetualAtlanticVaultLP #27

Closed code423n4 closed 11 months ago

code423n4 commented 1 year ago

Lines of code

https://github.com/code-423n4/2023-08-dopex/blob/b174dcd7b68a5372d7b9a97c9dd50895e742689c/contracts/perp-vault/PerpetualAtlanticVaultLP.sol#L132 https://github.com/code-423n4/2023-08-dopex/blob/b174dcd7b68a5372d7b9a97c9dd50895e742689c/contracts/perp-vault/PerpetualAtlanticVaultLP.sol#L164 https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/perp-vault/PerpetualAtlanticVaultLP.sol#L181 https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/perp-vault/PerpetualAtlanticVaultLP.sol#L192-L195 https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/perp-vault/PerpetualAtlanticVaultLP.sol#L201-L204 https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/perp-vault/PerpetualAtlanticVaultLP.sol#L210-L213 https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/perp-vault/PerpetualAtlanticVaultLP.sol#L280-L281

Vulnerability details

Impact

Detailed description of the impact of this finding. There are eight functions that are vulnerable to overflow and underflow. The functions are deposit, redeem, lockCollateral, unlockLiquidity, subtractLoss, addProceeds, addRdpx and convertToShares. I can reduce the contract balance to zero by deploying an integer overflow attack. The vulnerability is in the functions because they are not using safe math. The vulnerable functions use unsafe add and unsafe subtract. The deposit function calls a Deposit function on overflow. And the redeem function calls a Withdraw function on underflow. The others call similar functions like transfer and others use ERC/IERC.

Proof of Concept

Provide direct links to all referenced code in GitHub.

// https://github.com/code-423n4/2023-08-dopex/blob/b174dcd7b68a5372d7b9a97c9dd50895e742689c/contracts/perp-vault/PerpetualAtlanticVaultLP.sol#L132
    _totalCollateral += assets;
// https://github.com/code-423n4/2023-08-dopex/blob/b174dcd7b68a5372d7b9a97c9dd50895e742689c/contracts/perp-vault/PerpetualAtlanticVaultLP.sol#L164
    _rdpxCollateral -= rdpxAmount;

Add screenshots, logs, or any other relevant proof that illustrates the concept.

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.19;

import {PerpetualAtlanticVaultLP} from "./PerpetualAtlanticVaultLP.sol";

contract PerpetualAtlanticVaultLPAttackC {

    PerpetualAtlanticVaultLP public to;

    function attack(PerpetualAtlanticVaultLP _to) external payable {

        to = PerpetualAtlanticVaultLP(_to);

        to.deposit(uint256(1)+uint256(115792089237316195423570985008687907853269984665640564039457584007913129639935),address(this));
        to.redeem(uint256(1)-uint256(2),address(this),address(to));
        to.unlockLiquidity(uint256(1)-uint256(2));
        to.subtractLoss(uint256(1)-uint256(2));
    }

}

Exploit Foundry

// Arithmetic over/underflow
    vm.startPrank(address(3), address(3));
    userBalance = vaultLp.balanceOf(address(3));
      vm.expectRevert(
        stdError.arithmeticError
        );    vaultLp.deposit(uint256(1)+uint256(115792089237316195423570985008687907853269984665640564039457584007913129639935),address(this));
        vaultLp.redeem(uint256(1)-uint256(2),address(this),address(3));
        vaultLp.unlockLiquidity(uint256(1)-uint256(2));
        vaultLp.subtractLoss(uint256(1)-uint256(2));
    vm.stopPrank();

vm.startPrank(address(3), address(3));
    userBalance = vaultLp.balanceOf(address(3));
    userBalance;
vm.expectRevert(
        stdError.arithmeticError
        );    vaultLp.lockCollateral(uint256(1)+uint256(115792089237316195423570985008687907853269984665640564039457584007913129639935));
    userBalance;
    vm.stopPrank();

vm.startPrank(address(3), address(3));
    userBalance = vaultLp.balanceOf(address(3));
    userBalance;
vm.expectRevert(
        stdError.arithmeticError
        );    vaultLp.addProceeds(uint256(1)+uint256(115792089237316195423570985008687907853269984665640564039457584007913129639935));
    userBalance;
    vm.stopPrank();

 vm.startPrank(address(3), address(3));
    userBalance = vaultLp.balanceOf(address(3));
    userBalance;
vm.expectRevert(
        stdError.arithmeticError
        );
    vaultLp.subtractLoss(uint256(1)-uint256(2));
    userBalance;
    vm.stopPrank();

vm.startPrank(address(3), address(3));
    userBalance = vaultLp.balanceOf(address(3));
    userBalance;
vm.expectRevert(
        stdError.arithmeticError
        );    vaultLp.addRdpx(uint256(1)+uint256(115792089237316195423570985008687907853269984665640564039457584007913129639935));
    userBalance;
    vm.stopPrank();

vm.startPrank(address(3), address(3));
    userBalance = vaultLp.balanceOf(address(3));
    userBalance;
vm.expectRevert(
        stdError.arithmeticError
        );    vaultLp.convertToShares(uint256(1)+uint256(115792089237316195423570985008687907853269984665640564039457584007913129639935));
    userBalance;
    vm.stopPrank();

Test Case Foundry

  1. Deploy vaultLP functions above to the perp-vault/integration.t.sol contract in the testIntegration function.
  2. In the terminal run: forge test -vvv --match-path "tests/perp-vault/Integration.t.sol" --match-test "testIntegration"
  3. The balance is updated.
  4. Done.

    Log Foundry

    
    2023-08-dopex % forge test -vvvv --match-path "tests/perp-vault/Integration.t.sol" --match-test "testAttack0"
    [⠑] Compiling...
    [⠃] Compiling 1 files with 0.8.19
    [⠰] Solc 0.8.19 finished in 5.35s
    Compiler run successful with warnings:
    Warning (2072): Unused local variable.
    --> tests/perp-vault/Integration.t.sol:350:5:
    |
    350 |     uint256 userBalance = vaultLp.balanceOf(address(1));
    |     ^^^^^^^^^^^^^^^^^^^

Running 1 test for tests/perp-vault/Integration.t.sol:Integration [PASS] testAttack0() (gas: 617572) Logs: Balance Before 5000000000000000000

Traces: [617572] Integration::testAttack0() ├─ [0] VM::startPrank(0x0000000000000000000000000000000000000001, 0x0000000000000000000000000000000000000001) │ └─ ← () ├─ [24681] MockToken::approve(PerpetualAtlanticVault: [0x1d1499e622D69689cdf9004d05Ec547d650Ff211], 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77]) │ ├─ emit Approval(owner: 0x0000000000000000000000000000000000000001, spender: PerpetualAtlanticVault: [0x1d1499e622D69689cdf9004d05Ec547d650Ff211], amount: 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77]) │ └─ ← true ├─ [24681] MockToken::approve(PerpetualAtlanticVaultLP: [0xA4AD4f68d0b91CFD19687c881e50f3A00242828c], 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77]) │ ├─ emit Approval(owner: 0x0000000000000000000000000000000000000001, spender: PerpetualAtlanticVaultLP: [0xA4AD4f68d0b91CFD19687c881e50f3A00242828c], amount: 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77]) │ └─ ← true ├─ [24681] MockToken::approve(PerpetualAtlanticVault: [0x1d1499e622D69689cdf9004d05Ec547d650Ff211], 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77]) │ ├─ emit Approval(owner: 0x0000000000000000000000000000000000000001, spender: PerpetualAtlanticVault: [0x1d1499e622D69689cdf9004d05Ec547d650Ff211], amount: 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77]) │ └─ ← true ├─ [24681] MockToken::approve(PerpetualAtlanticVaultLP: [0xA4AD4f68d0b91CFD19687c881e50f3A00242828c], 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77]) │ ├─ emit Approval(owner: 0x0000000000000000000000000000000000000001, spender: PerpetualAtlanticVaultLP: [0xA4AD4f68d0b91CFD19687c881e50f3A00242828c], amount: 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77]) │ └─ ← true ├─ [0] VM::stopPrank() │ └─ ← () ├─ [0] VM::startPrank(0x0000000000000000000000000000000000000002, 0x0000000000000000000000000000000000000002) │ └─ ← () ├─ [24681] MockToken::approve(PerpetualAtlanticVault: [0x1d1499e622D69689cdf9004d05Ec547d650Ff211], 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77]) │ ├─ emit Approval(owner: 0x0000000000000000000000000000000000000002, spender: PerpetualAtlanticVault: [0x1d1499e622D69689cdf9004d05Ec547d650Ff211], amount: 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77]) │ └─ ← true ├─ [24681] MockToken::approve(PerpetualAtlanticVaultLP: [0xA4AD4f68d0b91CFD19687c881e50f3A00242828c], 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77]) │ ├─ emit Approval(owner: 0x0000000000000000000000000000000000000002, spender: PerpetualAtlanticVaultLP: [0xA4AD4f68d0b91CFD19687c881e50f3A00242828c], amount: 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77]) │ └─ ← true ├─ [24681] MockToken::approve(PerpetualAtlanticVault: [0x1d1499e622D69689cdf9004d05Ec547d650Ff211], 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77]) │ ├─ emit Approval(owner: 0x0000000000000000000000000000000000000002, spender: PerpetualAtlanticVault: [0x1d1499e622D69689cdf9004d05Ec547d650Ff211], amount: 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77]) │ └─ ← true ├─ [24681] MockToken::approve(PerpetualAtlanticVaultLP: [0xA4AD4f68d0b91CFD19687c881e50f3A00242828c], 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77]) │ ├─ emit Approval(owner: 0x0000000000000000000000000000000000000002, spender: PerpetualAtlanticVaultLP: [0xA4AD4f68d0b91CFD19687c881e50f3A00242828c], amount: 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77]) │ └─ ← true ├─ [0] VM::stopPrank() │ └─ ← () ├─ [0] VM::startPrank(0x0000000000000000000000000000000000000003, 0x0000000000000000000000000000000000000003) │ └─ ← () ├─ [24681] MockToken::approve(PerpetualAtlanticVault: [0x1d1499e622D69689cdf9004d05Ec547d650Ff211], 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77]) │ ├─ emit Approval(owner: 0x0000000000000000000000000000000000000003, spender: PerpetualAtlanticVault: [0x1d1499e622D69689cdf9004d05Ec547d650Ff211], amount: 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77]) │ └─ ← true ├─ [24681] MockToken::approve(PerpetualAtlanticVaultLP: [0xA4AD4f68d0b91CFD19687c881e50f3A00242828c], 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77]) │ ├─ emit Approval(owner: 0x0000000000000000000000000000000000000003, spender: PerpetualAtlanticVaultLP: [0xA4AD4f68d0b91CFD19687c881e50f3A00242828c], amount: 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77]) │ └─ ← true ├─ [24681] MockToken::approve(PerpetualAtlanticVault: [0x1d1499e622D69689cdf9004d05Ec547d650Ff211], 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77]) │ ├─ emit Approval(owner: 0x0000000000000000000000000000000000000003, spender: PerpetualAtlanticVault: [0x1d1499e622D69689cdf9004d05Ec547d650Ff211], amount: 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77]) │ └─ ← true ├─ [24681] MockToken::approve(PerpetualAtlanticVaultLP: [0xA4AD4f68d0b91CFD19687c881e50f3A00242828c], 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77]) │ ├─ emit Approval(owner: 0x0000000000000000000000000000000000000003, spender: PerpetualAtlanticVaultLP: [0xA4AD4f68d0b91CFD19687c881e50f3A00242828c], amount: 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77]) │ └─ ← true ├─ [0] VM::stopPrank() │ └─ ← () ├─ [34093] MockToken::mint(0x0000000000000000000000000000000000000001, 5000000000000000000 [5e18]) │ ├─ emit Transfer(from: 0x0000000000000000000000000000000000000000, to: 0x0000000000000000000000000000000000000001, amount: 5000000000000000000 [5e18]) │ └─ ← () ├─ [25293] MockToken::mint(0x0000000000000000000000000000000000000002, 20000000000000000000 [2e19]) │ ├─ emit Transfer(from: 0x0000000000000000000000000000000000000000, to: 0x0000000000000000000000000000000000000002, amount: 20000000000000000000 [2e19]) │ └─ ← () ├─ [25293] MockToken::mint(0x0000000000000000000000000000000000000003, 25000000000000000000 [2.5e19]) │ ├─ emit Transfer(from: 0x0000000000000000000000000000000000000000, to: 0x0000000000000000000000000000000000000003, amount: 25000000000000000000 [2.5e19]) │ └─ ← () ├─ [0] VM::startPrank(0x0000000000000000000000000000000000000001, 0x0000000000000000000000000000000000000001) │ └─ ← () ├─ [143192] PerpetualAtlanticVaultLP::deposit(5000000000000000000 [5e18], 0x0000000000000000000000000000000000000001) │ ├─ [7644] PerpetualAtlanticVault::getUnderlyingPrice() [staticcall] │ │ ├─ [2324] MockRdpxEthPriceOracle::getRdpxPriceInEth() [staticcall] │ │ │ └─ ← 20000000 [2e7] │ │ └─ ← 20000000 [2e7] │ ├─ [51946] PerpetualAtlanticVault::updateFunding() │ │ ├─ [7315] MockToken::transfer(PerpetualAtlanticVaultLP: [0xA4AD4f68d0b91CFD19687c881e50f3A00242828c], 0) │ │ │ ├─ emit Transfer(from: PerpetualAtlanticVault: [0x1d1499e622D69689cdf9004d05Ec547d650Ff211], to: PerpetualAtlanticVaultLP: [0xA4AD4f68d0b91CFD19687c881e50f3A00242828c], amount: 0) │ │ │ └─ ← true │ │ ├─ [4031] PerpetualAtlanticVaultLP::addProceeds(0) │ │ │ ├─ [583] MockToken::balanceOf(PerpetualAtlanticVaultLP: [0xA4AD4f68d0b91CFD19687c881e50f3A00242828c]) [staticcall] │ │ │ │ └─ ← 0 │ │ │ └─ ← () │ │ ├─ emit FundingPaid(sender: PerpetualAtlanticVaultLP: [0xA4AD4f68d0b91CFD19687c881e50f3A00242828c], amount: 0, latestFundingPaymentPointer: 0) │ │ └─ ← () │ ├─ [18961] MockToken::transferFrom(0x0000000000000000000000000000000000000001, PerpetualAtlanticVaultLP: [0xA4AD4f68d0b91CFD19687c881e50f3A00242828c], 5000000000000000000 [5e18]) │ │ ├─ emit Transfer(from: 0x0000000000000000000000000000000000000001, to: PerpetualAtlanticVaultLP: [0xA4AD4f68d0b91CFD19687c881e50f3A00242828c], amount: 5000000000000000000 [5e18]) │ │ └─ ← true │ ├─ emit Transfer(from: 0x0000000000000000000000000000000000000000, to: 0x0000000000000000000000000000000000000001, amount: 5000000000000000000 [5e18]) │ ├─ emit Deposit(caller: 0x0000000000000000000000000000000000000001, owner: 0x0000000000000000000000000000000000000001, assets: 5000000000000000000 [5e18], shares: 5000000000000000000 [5e18]) │ └─ ← 0x0000000000000000000000000000000000000000000000004563918244f40000 ├─ [0] VM::stopPrank() │ └─ ← () ├─ [0] VM::startPrank(0x0000000000000000000000000000000000000002, 0x0000000000000000000000000000000000000002) │ └─ ← () ├─ [36826] PerpetualAtlanticVaultLP::deposit(20000000000000000000 [2e19], 0x0000000000000000000000000000000000000002) │ ├─ [1144] PerpetualAtlanticVault::getUnderlyingPrice() [staticcall] │ │ ├─ [324] MockRdpxEthPriceOracle::getRdpxPriceInEth() [staticcall] │ │ │ └─ ← 20000000 [2e7] │ │ └─ ← 20000000 [2e7] │ ├─ [11472] PerpetualAtlanticVault::updateFunding() │ │ ├─ [3315] MockToken::transfer(PerpetualAtlanticVaultLP: [0xA4AD4f68d0b91CFD19687c881e50f3A00242828c], 0) │ │ │ ├─ emit Transfer(from: PerpetualAtlanticVault: [0x1d1499e622D69689cdf9004d05Ec547d650Ff211], to: PerpetualAtlanticVaultLP: [0xA4AD4f68d0b91CFD19687c881e50f3A00242828c], amount: 0) │ │ │ └─ ← true │ │ ├─ [2031] PerpetualAtlanticVaultLP::addProceeds(0) │ │ │ ├─ [583] MockToken::balanceOf(PerpetualAtlanticVaultLP: [0xA4AD4f68d0b91CFD19687c881e50f3A00242828c]) [staticcall] │ │ │ │ └─ ← 5000000000000000000 [5e18] │ │ │ └─ ← () │ │ ├─ emit FundingPaid(sender: PerpetualAtlanticVaultLP: [0xA4AD4f68d0b91CFD19687c881e50f3A00242828c], amount: 0, latestFundingPaymentPointer: 0) │ │ └─ ← () │ ├─ [3041] MockToken::transferFrom(0x0000000000000000000000000000000000000002, PerpetualAtlanticVaultLP: [0xA4AD4f68d0b91CFD19687c881e50f3A00242828c], 20000000000000000000 [2e19]) │ │ ├─ emit Transfer(from: 0x0000000000000000000000000000000000000002, to: PerpetualAtlanticVaultLP: [0xA4AD4f68d0b91CFD19687c881e50f3A00242828c], amount: 20000000000000000000 [2e19]) │ │ └─ ← true │ ├─ emit Transfer(from: 0x0000000000000000000000000000000000000000, to: 0x0000000000000000000000000000000000000002, amount: 20000000000000000000 [2e19]) │ ├─ emit Deposit(caller: 0x0000000000000000000000000000000000000002, owner: 0x0000000000000000000000000000000000000002, assets: 20000000000000000000 [2e19], shares: 20000000000000000000 [2e19]) │ └─ ← 0x000000000000000000000000000000000000000000000001158e460913d00000 ├─ [0] VM::stopPrank() │ └─ ← () ├─ [0] VM::startPrank(0x0000000000000000000000000000000000000003, 0x0000000000000000000000000000000000000003) │ └─ ← () ├─ [36826] PerpetualAtlanticVaultLP::deposit(25000000000000000000 [2.5e19], 0x0000000000000000000000000000000000000003) │ ├─ [1144] PerpetualAtlanticVault::getUnderlyingPrice() [staticcall] │ │ ├─ [324] MockRdpxEthPriceOracle::getRdpxPriceInEth() [staticcall] │ │ │ └─ ← 20000000 [2e7] │ │ └─ ← 20000000 [2e7] │ ├─ [11472] PerpetualAtlanticVault::updateFunding() │ │ ├─ [3315] MockToken::transfer(PerpetualAtlanticVaultLP: [0xA4AD4f68d0b91CFD19687c881e50f3A00242828c], 0) │ │ │ ├─ emit Transfer(from: PerpetualAtlanticVault: [0x1d1499e622D69689cdf9004d05Ec547d650Ff211], to: PerpetualAtlanticVaultLP: [0xA4AD4f68d0b91CFD19687c881e50f3A00242828c], amount: 0) │ │ │ └─ ← true │ │ ├─ [2031] PerpetualAtlanticVaultLP::addProceeds(0) │ │ │ ├─ [583] MockToken::balanceOf(PerpetualAtlanticVaultLP: [0xA4AD4f68d0b91CFD19687c881e50f3A00242828c]) [staticcall] │ │ │ │ └─ ← 25000000000000000000 [2.5e19] │ │ │ └─ ← () │ │ ├─ emit FundingPaid(sender: PerpetualAtlanticVaultLP: [0xA4AD4f68d0b91CFD19687c881e50f3A00242828c], amount: 0, latestFundingPaymentPointer: 0) │ │ └─ ← () │ ├─ [3041] MockToken::transferFrom(0x0000000000000000000000000000000000000003, PerpetualAtlanticVaultLP: [0xA4AD4f68d0b91CFD19687c881e50f3A00242828c], 25000000000000000000 [2.5e19]) │ │ ├─ emit Transfer(from: 0x0000000000000000000000000000000000000003, to: PerpetualAtlanticVaultLP: [0xA4AD4f68d0b91CFD19687c881e50f3A00242828c], amount: 25000000000000000000 [2.5e19]) │ │ └─ ← true │ ├─ emit Transfer(from: 0x0000000000000000000000000000000000000000, to: 0x0000000000000000000000000000000000000003, amount: 25000000000000000000 [2.5e19]) │ ├─ emit Deposit(caller: 0x0000000000000000000000000000000000000003, owner: 0x0000000000000000000000000000000000000003, assets: 25000000000000000000 [2.5e19], shares: 25000000000000000000 [2.5e19]) │ └─ ← 0x0000000000000000000000000000000000000000000000015af1d78b58c40000 ├─ [0] VM::stopPrank() │ └─ ← () ├─ [564] PerpetualAtlanticVaultLP::balanceOf(0x0000000000000000000000000000000000000001) [staticcall] │ └─ ← 5000000000000000000 [5e18] ├─ [0] VM::startPrank(0x0000000000000000000000000000000000000003) │ └─ ← () ├─ [564] PerpetualAtlanticVaultLP::balanceOf(0x0000000000000000000000000000000000000001) [staticcall] │ └─ ← 5000000000000000000 [5e18] ├─ [0] console::9710a9d0(00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000004563918244f40000000000000000000000000000000000000000000000000000000000000000000f42616c616e6365204265666f7265200000000000000000000000000000000000) [staticcall] │ └─ ← () ├─ [0] VM::expectRevert(Arithmetic over/underflow) │ └─ ← () └─ ← "Arithmetic over/underflow"

Test result: ok. 1 passed; 0 failed; finished in 831.75ms


## Tools Used
VS Code.
Foundry.
Mythx.
## Recommended Mitigation Steps
Use safeMath add and sub.

## Assessed type

Under/Overflow
c4-pre-sort commented 1 year ago

bytes032 marked the issue as low quality report

bytes032 commented 1 year ago

The warden is not showing any vulnerabilities beside stating that some functions might overflow

c4-judge commented 11 months ago

GalloDaSballo marked the issue as unsatisfactory: Invalid