Consequences of passing too few register parameters to a C function

· coding · Source ↗

TLDR

  • 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.

Original | Discuss on HN