Live data from Hacker News

Please restore our registers when you’re done with them

randomascii.wordpress.com

51–60 of 142 posts

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

#51
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?

Yes. That's pretty common, unfortunately.

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

#52

I'm sure some C++ lawyer can correct me, but isn't branching off of `m_ptr` (e.g. `CHECK()`ing the value) after `std::move(m_ptr)` technically Unspecified Behavior since `std::move(m_ptr)` leaves `m_ptr` as an Unspecified Value? It would be up to the compiler to define the behavior if they so pleased, but the C++ spec would not require such behavior to be defined at all.

There is a big difference between "undefined" and "unspecified" behavior. In this case, the behavior of `unique_ptr(unique_ptr&&)` is in fact specified. [0]

However, the bigger issue with that code is that it can easily stop working with a simple refactor. Consider:

    void foo(std::unique_ptr ptr) {}
    void bar(std::unique_ptr&& ptr) {}

    int main()
    {
        std::unique_ptr p1{new int{1}};
        std::unique_ptr p2{new int{2}};

        foo(std::move(p1));
        assert(p1 == nullptr);

        bar(std::move(p2));
        assert(p2 != nullptr);
     }
Neither of the above asserts will fire, but from the calling site, they look exactly the same. In my opinion, the more explicit option would be to do something like `bar(std::exchange(p2, nullptr))`

[0]: overload (5) https://en.cppreference.com/w/cpp/memory/unique_ptr/unique_p...

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

#53
post #15

Sounds like there should be an option when you write assembly code to tell the compiler "please save/restore any register that I'm modifying in this asm code according to the target you are compiling for"

Maybe we need some kind of higher level language that gets boiled down into assembly.

And, a compiler that knows custom SIMD optimizations for every algorithm anyone might ever need, and can recognize when you have coded one of them so it can substitute its SIMD version.

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

#54
post #22

To what degree is this possible to check statically? It feels like at least simple breaks of the ABI rules like this can be detected somewhat statically. The author already started with a very simple and incomplete version. In general, I wonder, are there any (many?) static analyzers for assembled binaries.

Compilers do inter-procedural register allocation and use custom calling conventions for local calls (where “local” can be quite large with LTO), while preserving ABI externally. This means that clobbering a callee-saved register without saving/restoring it in the same function is not necessarily a bug.

Curiously, I found a register clobber bug in the NaCl cryptography library today. Apparently, they used a custom assembler-preprocessor (qhasm) that avoids certain classes of bugs and aids with porting, but while the tool seems to actually model the register in some way, it does not treat it as callee-saved.

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

#56
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.

> 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? That's a waste if the callee doesn't use them.

Indeed. But isn’t it also a waste when someone doesn’t follow through and then we have situations like the one described on the article?

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

#57

I'm sure some C++ lawyer can correct me, but isn't branching off of `m_ptr` (e.g. `CHECK()`ing the value) after `std::move(m_ptr)` technically Unspecified Behavior since `std::move(m_ptr)` leaves `m_ptr` as an Unspecified Value? It would be up to the compiler to define the behavior if they so pleased, but the C++ spec would not require such behavior to be defined at all.

It’s an unspecified but required to be valid value for the moved type. The author mentions it’s a smart pointer type, which could easily be defined to act like this.

It appears that you need to be really smart in order to not blow your own foot off with this "smart pointer." I'll stick to the regular dumb kind, thanks.

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

#58

Earlier quoted context omitted.

Yes, but it's not a property of the assembly (or assembler), it's a necessity for the compiler to correctly codegen around the inline assembly. Historically, assemblers have been really dumb, so ABI is not a thing they'd track, especially as... I don't think they know what functions are? So while they can notice call/ret, they have no knowledge of a label being a jump or call target per-se, do they? So you'd need an…

> I don't think they know what functions are? It's a long time since I tangled with x86 assembler; but as I recall, ENTER and LEAVE were specifically for functions, and I'm not aware of any other use for them.

ENTER and LEAVE were specific x86 instructions though, not some kind of assembler special sauce. The assembler just translated your ENTER instruction into the corresponding machine opcode; no ABI knowledge required whatsoever.

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

#59
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.

RISC-V has such a register. It returns zero when read, and ignored writes.

[deleted]

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

#60
post #56

Earlier quoted context omitted.

> 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? That's a waste if the callee doesn't use them.

Indeed. But isn’t it also a waste when someone doesn’t follow through and then we have situations like the one described on the article?

Such cases are rare bugs in a small amount of code — mostly just compilers and hand-written assembly.

Preemptively saving and restoring all registers in all callers would appreciably slow down every single function call on every device in the world using that ABI.

The cumulative cost would be astronomical.

Post reply on HN