The constructor function is actually not just a regular function, and actually is called exactly once and cannot be called again.
Not only does the EVM not know what a constructor is, it also doesn't know what a function is.
The way functions are implemented in Ethereum is that the compiler includes a check at the beginning of the code which compares the first four bytes of the transaction data with the function signatures, computed as bytes4(sha3("myFunction(uint256, bytes, address, ...)"))
.
The constructor is not included in the list of function signitures that it checks, and the constructor code is not even included in the contract.
Contracts are created by sending a transaction with no to
field. The data in the transaction is interpreted as EVM code. This code (which includes the constructor function) is run, and its return value (which does not include the constructor) is used as the new code in the contract.
Thus there is no security hole here.