Skip to main content
Sigvex

Incorrect Constructor

Detects initialization functions that were intended as constructors but became callable public functions due to naming errors in Solidity 0.4.x contracts.

Incorrect Constructor

Overview

Remediation Guide: How to Fix Incorrect Constructor

The incorrect constructor detector identifies public functions that exhibit constructor-like behavior — writing to multiple storage slots, storing msg.sender as an owner, and lacking re-initialization guards — but are callable by anyone after deployment. In Solidity versions before 0.4.22, constructors were functions matching the contract name. A typo in the function name, or renaming the contract without updating the constructor, turned the constructor into an ordinary public function that anyone could call to re-initialize the contract and claim ownership.

Sigvex detects this pattern at the bytecode level by identifying functions with high storage write density, CALLER opcode usage (for msg.sender), and no initialization guard (no SLOAD-then-compare check at the function entry).

Why This Is an Issue

When a constructor becomes a public function, any user can call it after deployment to overwrite critical state variables. The Rubixi contract (2016) had this exact vulnerability: the developer renamed the contract from DynamicPyramid to Rubixi but forgot to rename the constructor, leaving DynamicPyramid() as a public function that set the creator/owner.

While modern Solidity (0.4.22+) uses the constructor keyword, this vulnerability still appears in:

  • Contracts compiled with older Solidity versions still deployed on mainnet
  • Initialization functions in proxy patterns that lack proper guards
  • Contracts imported from legacy codebases

How to Resolve

// Before: Vulnerable — Solidity 0.4.x constructor typo
contract MyContract {
    address public owner;

    // BUG: This was supposed to be "MyContract" — now anyone can call it
    function myContract() public {
        owner = msg.sender;
    }
}

// After: Fixed — use constructor keyword (Solidity >= 0.4.22)
contract MyContract {
    address public owner;

    constructor() {
        owner = msg.sender;
    }
}

Examples

Vulnerable Code

// solidity 0.4.21
contract TokenSale {
    address public owner;
    uint256 public price;

    // VULNERABLE: Not a constructor — contract was renamed from "CrowdSale"
    function CrowdSale() public {
        owner = msg.sender;
        price = 1 ether;
    }

    function withdraw() public {
        require(msg.sender == owner);
        msg.sender.transfer(address(this).balance);
    }
}

Fixed Code

// solidity ^0.8.0
contract TokenSale {
    address public owner;
    uint256 public price;

    constructor() {
        owner = msg.sender;
        price = 1 ether;
    }

    function withdraw() external {
        require(msg.sender == owner, "Not owner");
        payable(owner).transfer(address(this).balance);
    }
}

Sample Sigvex Output

{
  "detector_id": "incorrect-constructor",
  "severity": "critical",
  "confidence": 0.78,
  "description": "Public function CrowdSale() writes to 2 storage slots including msg.sender storage without an initialization guard. This appears to be a misnamed constructor.",
  "location": { "function": "CrowdSale()", "offset": 0 }
}

Detection Methodology

  1. Initialization pattern matching: Identifies functions with multiple SSTORE instructions, particularly those that store CALLER (msg.sender) — a hallmark of constructor logic.
  2. Guard check detection: Looks for initialization guards (loading a flag from storage and comparing it) at the function entry. Functions with such guards are less likely to be vulnerable.
  3. Known mutating function exclusion: Skips well-known standard library functions like renounceOwnership and transferOwnership that legitimately write to storage and reference the caller.
  4. Name heuristic: Functions with names that are capitalized or match common constructor naming patterns receive higher confidence.

Limitations

False positives:

  • Legitimate initialization functions in upgradeable contracts (e.g., initialize()) that intentionally perform constructor-like operations may be flagged if they lack visible guards.
  • Admin functions that set multiple state variables may match the initialization pattern.

False negatives:

  • Constructors with minimal storage writes (only one slot) may not match the pattern threshold.
  • Contracts where the constructor was correctly named but performs no CALLER storage may be missed.

References