Skip to content

setAddressFrozen(address(0)) should be rejected #372

@rya-sge

Description

@rya-sge

Issue: setAddressFrozen(address(0)) should be rejected

Affects: CMTAT EnforcementModule / EnforcementModuleInternal
Severity: Informational
Reporter: Nethermind Audit Agent on CMTAT FHE


Summary

EnforcementModule.setAddressFrozen accepts address(0) without restriction.
Freezing the null address has no legitimate use case — address(0) holds no tokens
and has no private key — but it can be exploited to impose a global transfer block
that bypasses role separation between ENFORCER_ROLE and PAUSER_ROLE.


Affected Functions

EnforcementModule.setAddressFrozen(address account, bool freeze)
EnforcementModule.setAddressFrozen(address account, bool freeze, bytes calldata data)
EnforcementModule.batchSetAddressFrozen(address[] calldata accounts, bool[] calldata freezes)

All three delegate to EnforcementModuleInternal._addAddressToTheList, which writes
directly to the frozen mapping without any zero-address check.


Attack Vector

Many CMTAT integrations pass address(0) as a synthetic spender in transfer
validation paths. For example, a confidential token layer may call:

_canTransferisFrozen(address(0), from, to)

Because _canTransferisFrozen checks isFrozen(spender) || isFrozen(from) || isFrozen(to),
freezing address(0) makes the spender check always return true, blocking every
direct holder transfer without holding PAUSER_ROLE.

This lets a holder of ENFORCER_ROLE (intended only to freeze individual accounts)
effectively pause all transfers — a privilege they are explicitly not supposed to have.


Suggested Fix

Add a zero-address guard in EnforcementModuleInternal._addAddressToTheList (the
single internal entry point for all three public functions):

function _addAddressToTheList(
    EnforcementModuleInternalStorage storage $,
    address account,
    bool status,
    bytes memory data
) internal virtual {
    require(account != address(0), "EnforcementModule: zero address");
    $._list[account] = status;
}

Or, if a custom error is preferred (recommended for gas and clarity):

error EnforcementModule_ZeroAddress();

function _addAddressToTheList(...) internal virtual {
    if (account == address(0)) revert EnforcementModule_ZeroAddress();
    $._list[account] = status;
}

Placing the guard at the internal level covers all three public functions and any
future callers of _addAddressToTheList automatically.


References

  • CMTAT/contracts/modules/internal/EnforcementModuleInternal.sol_addAddressToTheList
  • CMTAT/contracts/modules/wrapper/core/EnforcementModule.solsetAddressFrozen, batchSetAddressFrozen
  • contracts/CMTATConfidentialBase.sol — workaround applied (spender changed to from)
  • Nethermind AuditAgent Finding 3 — doc/audit/nethermind-audit-agent/nethermind-audit-agent-report-feedback.md

Metadata

Metadata

Assignees

No one assigned

    Labels

    Next releaseThe issue has been merged into dev and will be part of the next release

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions