Live data from Hacker News

Clang vs. Clang

blog.cr.yp.to

311–320 of 405 posts

Re: Clang vs. Clang

#311

Earlier quoted context omitted.

I don't know what you're ranting on about. Functions have parameters. In the case of the previous function, it is not defined if its parameter is INT_MAX, but is defined for all other values of int. Having functions that are only valid on a subset of the domain defined by the types of their parameters is a commonplace thing, even outside of C. Yes, a compiler can deduce that a particular code path can be completely e…

The point is that a compiler can notice that one branch of your code leads to UB and elide the whole branch, even eliding code before the UB appears. The way this cascades is very hard to track and understand - in this case, the fact that stupid() is UB when called with INT_MAX makes foo() be UB when called with 0, which can cascade even more. And no, this doesn't happen in any other commonly-used language. No other…

It is perfectly possible to write a function in pure Java that would never terminate when called with parameter values outside of the domain for which it is defined. It is also possible for it to yield an incorrect value.

Your statement that such a function would throw an exception is false.

Ensuring a function is only called for the domain it is defined on is entirely at the programmer's discretion regardless of language. Some choose to ensure all functions are defined for all possible values, but that's obviously impractical due to combinatorial explosions. Types that encapsulate invariants are typically seen as the solution for this.

Re: Clang vs. Clang

#312
post #42
post #24

Earlier quoted context omitted.

Disabling all optimizations isn't even enough- fundamentally what you need is a much narrower specification for how the source language maps to its output. Even -O0 doesn't give you that, and in fact will often be counterproductive (e.g. you'll get branches in places that the optimizer would have removed them). The problem with this is that no general purpose compiler wants to tie its own hands behind its back in thi…

> You almost may as well just design a new language, at that point. Forget “almost”. Go compile this C code: void foo(int *ptr) { free(ptr); *ptr = 42; } This is UB. And it has nothing whatsoever to do with optimizations — any sensible translation to machine code is a use-after-free, and an attacker can probably find a way to exploit that machine code to run arbitrary code and format your disk. If you don’t like this…

This is pretty persnickety and I imagine you're aware of this, but free is a weak symbol on Linux, so user code can replace it at whim. Your foo cannot be statically determined to be UB.

Re: Clang vs. Clang

#313
post #23

C and C++ are unsuitable for writing algorithms with constant-time guarantees. The standards have little to no notion of real time, and compilers don't offer additional guarantees as extensions. But blaming the compiler devs for this is just misguided.

That was my thought reading this article. If you want to produce machine code that performs operations in constant time regardless of the branch taken, you need to use a language that supports expressing that, which C does not.

> If you want to produce machine code that performs operations in constant time regardless of the branch taken

Nobody is asking for that. That's the whole point. Crypto code that needs to be constant time in regards to secret data is needs to avoid branching based on secret data, but the optimizer is converting non-branching code into branching code.

Re: Clang vs. Clang

#314

Earlier quoted context omitted.

The point is that a compiler can notice that one branch of your code leads to UB and elide the whole branch, even eliding code before the UB appears. The way this cascades is very hard to track and understand - in this case, the fact that stupid() is UB when called with INT_MAX makes foo() be UB when called with 0, which can cascade even more. And no, this doesn't happen in any other commonly-used language. No other…

It is perfectly possible to write a function in pure Java that would never terminate when called with parameter values outside of the domain for which it is defined. It is also possible for it to yield an incorrect value. Your statement that such a function would throw an exception is false. Ensuring a function is only called for the domain it is defined on is entirely at the programmer's discretion regardless of lan…

I didn't claim that all functions are either correct or throw an exception in Java. I said that UB doesn't exist in Java, in the sense of a Java program that compiles, but for which no semantics are assigned and the programmer is not allowed to write it. All situations that are UB in C or C++ are either well-defined in Java (signed integer overflow, non-terminating loops that don't do IO/touch volatile variables), many others throw exceptions (out of bounds access, divide by 0), and a few are simply not possible (use after free). Another few are what the C++ standard would call "unspecified behavior", such as unsynchronized concurrent access.

And yes, it's the programmer's job to make sure functions are called in their domain of apllication. But it doesn't help at all when the compiler prunes your code-as-written to remove flows that would have reached an error situation, making debugging much harder when you accidentally do call them with illegal values.

Re: Clang vs. Clang

#315
post #260

Earlier quoted context omitted.

I recommend doing some experiments before considering no register allocation unbearingly slow. I once tried running Gentoo with everything compiled -O0 and the user experience with most software wasn't significantly different. The amount of performance critical C code on a modern PC is surprisingly low. Stuff like media decoding is usually done in assembly.

> I recommend doing some experiments before considering no register allocation unbearingly slow. I once tried running Gentoo with everything compiled -O0 AFAIK, register allocation is one of the few optimization passes which are always enabled on all compilers, even with -O0, so your experiment proves nothing.

It's decided by function use_register_for_decl in gcc: https://github.com/gcc-mirror/gcc/blob/releases/gcc-12/gcc/f... With -g -O0 register is only used in special cases like using the register keyword.

The memory accesses are also easily visible by disassembling the compiled binary. Performance of resulting binary at -O0 is also rougly similar to performance of binary produced by Tiny C Compiler, which doesn't implement register allocation at all.

Re: Clang vs. Clang

#316

Earlier quoted context omitted.

> he's a perfectionist, and he merely expects perfection from the rest of us as well. Nicely put, but at the end perfectionism is a flaw.

Not when computer security is concerned.

Security is a tradeoff. Perfect security is not using a computer.

Re: Clang vs. Clang

#317
post #45

It’s worth noting that, on Intel CPUs, neither clang nor anything else can possibly generate correct code, because correct code does not exist in user mode. https://www.intel.com/content/www/us/en/developer/articles/t... Look at DOITM in that document — it is simply impossible for a userspace crypto library to set the required bit.

> because correct code does not exist in user mode.

User mode code can run in the correct mode. What it cannot do is toggle the mode on/off. Once toggled on, it works perfectly fine for userspace; this could become e.g. a per-process flag enabled by a prctl syscall, with the MSR adjusted during scheduler task switching.

Re: Clang vs. Clang

#318

> It would be interesting to study what percentage of security failures can be partly or entirely attributed to compiler "optimizations". I bet it's roughly none.

The article links to an attack that extracts a 512-bit secret key in 5-10 minutes:

https://pqshield.com/pqshield-plugs-timing-leaks-in-kyber-ml...

https://github.com/antoonpurnal/clangover

Re: Clang vs. Clang

#319
post #304

Earlier quoted context omitted.

That is not UB. That is simply mutable data. The solution here is static analysis (Rust) or immutable persistent collections.

In the context of C++ and STL it is UB. They are in the process of rewording such cases as erroneous instead of UB, but it will take time.

…what’s the difference?

Re: Clang vs. Clang

#320
post #284

Earlier quoted context omitted.

All of it. But especially anything added after C89 that was not already there implicitly. Edit: okay, not all of it. I was hyperbolic. Race conditions and data races should be UB. But anything that can be implementation-defined should be.

So your issue is not at all any specific thing or action anyone took, but just in general having UB in places not strictly necessary. And "Especially anything [different from The Golden Days]", besides being extremely cliche, is a completely arbitrary cutoff point. A given compiler is free to define specific behavior for UB (and indeed you can add compiler flags to do that for many things); the standard explicitly ac…

Sigh...yes, I don't want any UB where it's not necessary.

But if you must have a concrete example, how about realloc?

In C89 [1] (page 155), realloc with a 0 size and a non-NULL pointer was defined as free:

> If size is zero and ptr is not a null pointer, the object it points to is freed.

In C99 [2] (page 314), that sentence was removed, making it undefined behavior when it wasn't before. This is a pure example of behavior becoming undefined when it was not before.

In C11 [3] (page 349), that sentence remains gone.

In C17 [4] (page 254), we get an interesting addition:

> If size is zero and memory for the new object is not allocated, it is implementation-defined whether the old object is deallocated. If the old object is not deallocated, its value shall be unchanged.

So the behavior switches from undefined to implementation-defined.

In C23 [5] (page 357), the wording completely changes to:

> ...or if the size is zero, the behavior is undefined.

So WG14 made it UB again after making implementation-defined.

SQLite targets C89, but people compile it with modern compilers all the time, and those modern compilers generally default to at least C99, where the behavior is UB. I don't know if SQLite uses realloc that way, but if it does, are you going to call it buggy just because the authors stick to C89 and their users use later standards?

[1]: https://web.archive.org/web/20200909074736if_/https://www.pd...

[2]: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf

[3]: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf

[4]: https://web.archive.org/web/20181230041359if_/http://www.ope...

[5]: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3047.pdf

Post reply on HN