Live data from Hacker News

Please restore our registers when you’re done with them

randomascii.wordpress.com

61–70 of 142 posts

Re: Please restore our registers when you’re done with them

#61

Earlier quoted context omitted.

The webrtc fix was thematically similar in that the programmer declared what registers were trashed and then the compiler knows which registers need to be saved. I'm not sure why the compiler doesn't notice when registers are used without being declared as being trashed - I'm really not an expert at _writing_ assembly language.

It's literally undecidable in principle whether some assembler correctly restores some register R. That's a non-trivial semantic property, Rice's theorem applies. So the compiler's only practical option if it worked this way would be a conservative option - any time it's unclear whether register R is clobbered, treat it as clobbered. As a trivial example of why a register might not be clobbered even though my code to…

> It's literally undecidable in principle whether some assembler correctly restores some register R.

No, it's literally undecidable in principle whether every bit of assembler correctly restores some register R. For any given bit of inline assembler, it's quite likely to be trivial.

In any case, we can have a useful safety feature without requiring the compiler to decide. The compiler can easily work out all the registers which get written to (right?), just not which get restored. So in addition to the clobber list, we could have a list of registers which the programmer asserts that the code restores. A register which is written to has to be on either the clobber list or the restore list (or be an output). This certainly isn't foolproof, but it would catch accidental clobbers.

Re: Please restore our registers when you’re done with them

#62
post #43
post #42

Naïve question about ABIs: shouldn’t the caller be responsible for that? If I want a function to restore certain registers, wouldn’t it be simpler if I was the one that save them on my memory, call the function, and then override the registers with whatever values the function set? Otherwise it seems we’re just… asking for trouble, so to speak.

It would just be slow for the caller to have to push and pop (say 30) registers in general that the specific callee (and transitive callees) may not even use. Most ABI specify some registers caller-saved, some registers callee-saved (retained unchanged from the perspective of the caller) and some registers scratch (not-saved). In the end it depends on the architecture and on typical workload which are the fastest--an…

You usually wouldn't need to push&pop all registers, just ones that you want to preserve across the call. Regardless, yeah, non-volatile aka callee-saved registers are extremely important for good performance of code that calls functions (esp. loops - without callee-saved registers, you'd have to store the loop counter & length on the stack!)

Re: Please restore our registers when you’re done with them

#63
post #3

It’s interesting that there is a zero stored in a register and used for hours - is that significantly faster than just using some actual zero each time? Perhaps CPUs need a “always zero” register or some similar menomic to help harden.

Where it's not optimized away, getting "an actual zero" requires a memory operation of some kind. Register ops are faster in that they are right there, no fetch needed.

Re: Please restore our registers when you’re done with them

#64
post #61

Earlier quoted context omitted.

It's literally undecidable in principle whether some assembler correctly restores some register R. That's a non-trivial semantic property, Rice's theorem applies. So the compiler's only practical option if it worked this way would be a conservative option - any time it's unclear whether register R is clobbered, treat it as clobbered. As a trivial example of why a register might not be clobbered even though my code to…

> It's literally undecidable in principle whether some assembler correctly restores some register R. No, it's literally undecidable in principle whether every bit of assembler correctly restores some register R. For any given bit of inline assembler, it's quite likely to be trivial. In any case, we can have a useful safety feature without requiring the compiler to decide. The compiler can easily work out all the regi…

> No, it's literally undecidable in principle whether every bit of assembler correctly restores some register R. For any given bit of inline assembler, it's quite likely to be trivial.

Exactly. And if it's non-trivial to decide something that basic, you're doing it wrong. Saving and restoring all the registers is always an option. Only saving and restoring some of them is an optimization which must be shown to be sound.

Re: Please restore our registers when you’re done with them

#65
post #4
post #3

It’s interesting that there is a zero stored in a register and used for hours - is that significantly faster than just using some actual zero each time? Perhaps CPUs need a “always zero” register or some similar menomic to help harden.

Many architectures do have a 0 register because the value is useful. Others have a zero instruction (or both). What would an "actual zero" be -- a literal?

I don't know anything about x86, but on Arm you can use an immediate 0 that is stored in the instruction data.

Re: Please restore our registers when you’re done with them

#66
post #32

Why does a „3rd party encryption software” appear in the call stack of a user mode process in the first place? Is this another case where a “security” software injects broken DLL files into all processes in your system?

The irony of security and anti-cheat software acting like a invasive malware

Re: Please restore our registers when you’re done with them

#67
post #62
post #43

Earlier quoted context omitted.

It would just be slow for the caller to have to push and pop (say 30) registers in general that the specific callee (and transitive callees) may not even use. Most ABI specify some registers caller-saved, some registers callee-saved (retained unchanged from the perspective of the caller) and some registers scratch (not-saved). In the end it depends on the architecture and on typical workload which are the fastest--an…

You usually wouldn't need to push&pop all registers, just ones that you want to preserve across the call. Regardless, yeah, non-volatile aka callee-saved registers are extremely important for good performance of code that calls functions (esp. loops - without callee-saved registers, you'd have to store the loop counter & length on the stack!)

...which is exactly what the callee within the loop would have to do if it used those registers. But I guess your point is that in the callee-save case it only happens when it needs to happen, while in the caller-save case it happens every time whether it needs to or not.

Re: Please restore our registers when you’re done with them

#68
If one could reliably detect this DLL injection in the process address space, then the correct "fix" is to crash immediately. Authors of such tools should seek another way of accomplishing their goal, preferably one that does not export their own bugs to innocent bystanders.

Re: Please restore our registers when you’re done with them

#69
post #62

Earlier quoted context omitted.

You usually wouldn't need to push&pop all registers, just ones that you want to preserve across the call. Regardless, yeah, non-volatile aka callee-saved registers are extremely important for good performance of code that calls functions (esp. loops - without callee-saved registers, you'd have to store the loop counter & length on the stack!)

...which is exactly what the callee within the loop would have to do if it used those registers. But I guess your point is that in the callee-save case it only happens when it needs to happen, while in the caller-save case it happens every time whether it needs to or not.

yep, hence why calling conventions usually have both callee-saved and caller-saved registers, so that you only have "unnecessary" stack usage when you need to use more than roughly half of the registers.

Re: Please restore our registers when you’re done with them

#70
In this particular example, why not just

  PXOR XMM7 XMM7
before using XMM7 to zero anything? That way the compiler doesn't have to assume that XMM7 is zero, it can know.

Yes it's an extra instruction but XORing a register with itself is such a common metaphor for zeroing that register that CPU designers try to make it fast.

Edit: Just noticed that Veliladon essentially made the same comment herein and explained the reason why it's not done this way.

Post reply on HN