The consensus protocol limits on blocks are:
1 million or fewer bytes canonically-serialized size. 20,000 or fewer "sigops"
Unfortunately, Satoshi implemented those as quick fixes with zero code review and little testing (Bitcoin was not a Big Deal back then, it was the right decision at the time), and the way sigop counting is done is... well, just wrong.
Here's how Satoshi did it, as pseudo-code (see GetLegacySigOpCount in main.cpp for actual code):
For all the transactions in a block: