Ethereum smart contract functions (i.e. methods) are addressed based on a four-byte hash of the signature, within the context of the smart contract itself (a smart contract is analogous to a Java or Python class instance, having state and logic). So, after compilation, a function will be addressed like <smart contract address>.<method hash>, if you can imagine.

This is distinct from other languages where methods and functions are addressed unambiguously by the full signature as a string (like in Python), or by a specific memory address (like in C).

In most situations this addressing scheme works very well, because in practical terms there will never be a hash collision inside of a smart contract, and if there were such a collision, the compiler would catch it and fail to compile, and the EVM would reject any bytecode containing such a conflict. Regardless, this is not a problem within Ethereum.

The thing is, Poly Network is a separate thing from Ethereum, and although it does integrate with Ethereum, it does so with its own separate software. The way it interfaces with Ethereum is via several Ethereum smart contracts, including one (smart contract #1) that holds the tokens that it manages on the Ethereum blockchain (valued in the hundreds of millions of dollars), and another (smart contract #2) that permits users of the Poly Network software to invoke arbitrary functions on arbitrary smart contracts already deployed on Ethereum. The Poly Network software talks directly to these smart contracts.

Poly controls these smart contract with a permissions system implemented inside the smart contracts themselves, whereby the invocation of any function of the smart contracts is verified against a list of public keys (i.e. account addresses) that are authorized to make any such invocation. Only users that hold the private keys that correspond to these authorized public keys may invoke the smart contracts' functions; in practice, these keys are controlled by the Poly Network software. Each smart contract stores this list within its own data (analogous to private instance data inside a Java object).

The problem is that the Poly Network proprietary software uses the same authorized key pairs to move funds in and out of smart contract #1 and also to invoke smart contract #2. This means that a user of the Poly Network software can use smart contract #2 to invoke smart contract #1, which is very bad.

What happened is that the hacker figured this out, and used smart contract #2 to update the authorized public key list inside contract #1, which was allowed to happen because he routed the message through the Poly Network software, which dutifully signed the message with its own secure private keys. In other words, the Poly network signed a message from the hacker authorizing the Poly smart contracts to replace the authorized public key list with the hacker's public key, giving the hacker control over contract #1.

It wasn't as simple as this, though. Apparently, although the Poly Network allows any function name to be invoked by the end user, that function is required to be invoked with a signature that includes a particular set of arguments, specifically "(bytes,bytes,uint64)". The hacker wouldn't be able to call "putCurEpochConPubKeyBytes" directly from the Poly Network software, because "putCurEpochConPubKeyBytes" accepts only one argument, not the three arguments that the Poly Network software uses to construct the signature for function invocations sent from contract #2.

So, the hacker had to discover another function name that when combined with the three arguments would result in a four-byte hash that corresponded to the four-byte hash of the function he wanted to invoke. By brute force the hacker discovered that "f1121318093(bytes,bytes,uint64)" has the same four-byte hash as "putCurEpochConPubKeyBytes(bytes)". So the hacker used an invocation to "f1121318093(bytes,bytes,uint64)" to invoke "putCurEpochConPubKeyBytes(bytes)" on contract #1, and thereby replace the authorized public keys for that contract with his own. Having obtained control over contract #1, he was able to transfer out all of the tokens it controlled, to the tune of hundreds of millions of dollars.

There are any number of ways that Poly Network could have prevented this from occurring, most obviously by not allowing contract #2 to invoke contract #1. They also could have done a better job of filtering what kinds of messages users can send from the Poly Network software.