Solidity

Reentrancy Guard

A reentrancy guard is a lock that prevents a protected function from being entered again while it is already executing.

The contract marks itself busy at function entry and rejects nested calls until the function finishes.

Reentrancy Guard Explained in Detail

A reentrancy guard blocks nested execution of protected functions. The common OpenZeppelin pattern uses a nonReentrant modifier that reverts if the same guard is already active.

The guard reduces reentrancy risk, but it does not fix bad accounting by itself.

Smart contract example

import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

contract Vault is ReentrancyGuard {
    mapping(address => uint256) public balances;

    function withdraw() external nonReentrant {
        uint256 amount = balances[msg.sender];
        require(amount > 0, "empty");

        balances[msg.sender] = 0;

        (bool ok,) = msg.sender.call{value: amount}("");
        require(ok, "send failed");
    }
}

Reentrancy Guard in Auditing

Reentrancy guards are common, but they are not complete reentrancy protection. A function can be guarded while another unguarded function still touches the same balances, reserves, shares, or debt.

Auditors should review the protected state, not only the protected function.

Red flags in code

  • Only the withdrawal function is guarded while deposit, claim, borrow, or repay touches the same state.

  • nonReentrant functions call each other directly.

  • Public getters expose temporarily inconsistent values.

  • Guarded code performs external calls before all dependent state is finalized.

  • The guard protects one contract while another contract can mutate shared state.

  • A custom guard resets too early or only guards one branch.

How to test or review it

  • Try same-function callbacks from a malicious receiver.

  • Try cross-function callbacks into every function touching the same state.

  • Check whether view functions return unsafe values during active state transitions.

  • Verify custom guard state changes on every success and revert path.

  • Confirm guarded external functions use private helpers when they need to share logic.

  • Combine guard tests with invariant tests for balances, shares, debt, and reserves.

Sources