Passing too few parameters to a C function is undefined behavior with concrete hardware consequences, worst on Itanium due to its NaT bit and architectural register frames.
Key Takeaways
On stack-based calling conventions, under-passing parameters causes stack imbalance or corruption, especially with callee-clean conventions.
On register-based ABIs, the called function reads whatever uninitialized value sits in the unused register, a silent data hazard.
Itanium’s “Not a Thing” (NaT) bit marks registers holding invalid speculative values; spilling a NaT register to memory raises a NaT consumption exception.
Itanium’s architectural call mechanism requires the caller to declare output register count; reading a stacked register outside that frame is undefined and can fault.
Writing a stacked register outside the declared frame on Itanium is required to raise an Illegal Operation fault, even from a register-to-register move.
Hacker News Comment Review
Commenters noted that in C (pre-C23), unprototyped function pointers allowed mismatched argument counts silently; C23 closes this by making empty parameter lists equivalent to (void).
Discussion touched on practical ABI introspection: one commenter exploited under-passing to detect calling convention variants at runtime by checking whether the first argument read as NULL.
General consensus is that Itanium’s strict enforcement, while technically sound, is another example of its unusual architecture imposing costs invisible on x86 or ARM.
Notable Comments
@CodeArtisan: Pre-C23 int (*f)() allowed calling with any argument count; -std=c23 makes gcc and clang error on this, with a Godbolt demo linked.
@charleslmunger: Used deliberate under-passing on a fixed ABI to detect at runtime whether a JNI env pointer was the first parameter, based on NULL vs non-NULL.