Live data from Hacker News

Clang vs. Clang

blog.cr.yp.to

331–340 of 405 posts

Re: Clang vs. Clang

#331

Earlier quoted context omitted.

And even then there was no notion of constant-time being observable behavior to the compiler. You cannot write reliably constant-time code in C because execution time is not a property the C language includes in its model of computation.

But having a straightforward/predictable mapping to the underlying machine and its semantics is included in the C model of computation. And that is actually not just compatible with the C "model of computation" being otherwise quite incomplete, these two properties are really just two sides of the same coin. The whole idea of an "abstract C machine" that unambiguously and completely specifies behavior is a fiction.

> But having a straightforward/predictable mapping to the underlying machine and its semantics is included in the C model of computation.

While you can often guess what the assembly will be from looking at C code given that you're familiar with the compiler, exactly how C is to be translated into assembly isn't well-specified.

For example, you can't expect that all uses of the multiplication operator "*" results in an actual x86 mul instruction. Many users expect constant propagation, so you can write something like "2 * SOME_CONSTANT" without computing that value at runtime; there is no guarantee of this behavior, though. Also, for unsigned integers, when optimizations are turned on, many expect compilers to emit left shift instructions when multiplying by a constant power of two, but again, there's no guarantee of this. That's not to say this behavior couldn't be part of a specification, but it's just an informal expectation right now.

What I think people might want is some readable, well-defined set of attribute grammars[0] for translation of C into assembly for varying optimization levels - then, you really would be able to know exactly how some piece of C code under some context would be translated into assembly. They've already been used for writing code generator generators in compilers, but what I'm thinking is something more abstract, not as concrete as a code generation tool.

[0]: https://en.wikipedia.org/wiki/Attribute_grammar

Re: Clang vs. Clang

#332

Earlier quoted context omitted.

But C never had been portable assembly. Which I think is somewhat the core of the problem. People treating things in C in ways they just are not. Weather that is C is portable assembly or C the "it's just bit's in memory" view of things (which often is double wrong ignoring stuff like hardware caching). Or stuff like writing const time code based on assuming that the compiler probably, hopefully can't figure out that…

> But C never had been portable assembly. The ANSI C standards committee disagrees with you. "Committee did not want to force programmers into writing portably, to preclude the use of C as a “high-level assembler:” https://www.open-std.org/JTC1/SC22/WG14/www/docs/n897.pdf p 2, line 39. (p10 of the PDF) "C code can be portable. " line 30

These are aspiration statements, not a factual judgment of what that standard or its existing implementations actually are. At least they do not cover all implementations nor define precisely what they cover. Note the immediate next statement: "C code can be non-portable."

In my opinion, C has tried to serve two masters and they made a screw-hammer in the process.

The rest of the field has moved on significantly. We want portable behavior, not implementation-defined vomit that will leave you doubting whether porting introduces new UB paths that you haven't already fully checked against (by, e.g. varying the size of integers in such a way some promotion is changed to something leading to signed overflow; or bounds checking is ineffective).

The paragraph further down about explicitly and swiftly rejecting a validation test suite should also read as a warning. Not only would the proposal of modern software development without a test suite get you swiftly fired today, but they're explicitly acknowledging the insurmountable difficulties in producing any code with consistent cross-implementation behavior. But in the time since then, other languages have demonstrated you can reap many of the advantages of close-to-the-metal without compromising on behavior consistency in cross-target behavior, at least for many relevant real-word cases.

They really knew what they were building, a compromise. But that gets cherry-picked into absurdity such as stating C is portable in present-tense or that any inherent properties make it assembly-like. It's neither.

Re: Clang vs. Clang

#333
post #61

Earlier quoted context omitted.

Couldn't you syscall into the kernel to set the flag, then return back into usermode with it set?

So your compiler is supposed to emit a pair of syscalls each function that does integer math? Never mind that a pair of syscalls that do WRMSR may well take longer than whatever crypto operation is between them. I have absolutely nothing good to say about Intel’s design here.

You could just leave it on.

I agree it's not great.

Re: Clang vs. Clang

#334

Earlier quoted context omitted.

The full quote is: > Although it strove to give programmers the opportunity to write truly portable programs, the C89 Committee did not want to force programmers into writing portably, to preclude the use of C as a “high-level assembler:” the ability to write machine-specific code is one of the strengths of C. It is this principle which largely motivates drawing the distinction between strictly conforming program and…

It specifically says that the use of C as a "portable assembler" is a use that the standards committee does not want to preclude. Not sure how much clearer this can be.

That statement means the comittee does not want to stop it from being developed. The question is, has it? They mean a specific implementation could work as portable assembler, mirroring djb's request for an 'unsurprising' C compiler. Another interpretation would be in the context of CompCert, which has been developed to achieve semantic preservation between assembly and its source. Interestingly this of course hints at verifying an assembled snippet coming from some other source as well. Then that alternate source for the critical functions frees the rest of compiler internals from the problems of preserving constant-timeness and leakfreedom through their passes.

Re: Clang vs. Clang

#335
post #256

Earlier quoted context omitted.

No, I am not saying that every project in the wild has a bad "coding strategy". Some of the most reliable software I use everyday is written in C. Some of this I use for decades without every encountering a crash or similar bug. So the meme that "all C code crashes all the time because of UB" is clearly wrong. It is not intractable, in my experience you just have to document some rules and occasionally make sure they…

> So the meme that "all C code crashes all the time because of UB" is clearly wrong. It's not about crash at all, but “all software has security vulnerabilities because of UB” is unfortunately true. > It is not intractable, in my experience you just have to document some rules and occasionally make sure they are followed. If even DJB couldn't get that part perfectly I'm pretty certain you cannot either.

Not all software is security relevant. I run a lot of large simulations. I do not care at all if that software would crash on specially prepared inputs. I do care that it's as fast as possible.

Re: Clang vs. Clang

#336
post #24
post #3

Earlier quoted context omitted.

Optimizing compilers that don't allow disabling all optimizations makes it impossible to write secure code with them. Must do it with assembly.

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…

Hmm, not sure, I think it would be possible to mark a function with a pragma as "constant time", and the compiler could make sure that it indeed is that. I think it wouldn't be impossible to actually teach it to convert branched code into unbranched code automatically for many cases as well. Essentially, the compiler pass must try to eliminate all branches, and the code generation must make sure to only use data-constant-time ops. It could warn/fail when it cannot guarantee it.

Re: Clang vs. Clang

#337
post #96

Earlier quoted context omitted.

Perhaps the solution is also to reign in the language standard to support stricter use cases. For example, what if there was a constant-time { ... }; block in the same way you have extern "C" { ... }; . Not only would it allow you to have optimizations outside of the block, it would also force the compiler to ensure that a given block of code is always constant-time (as a security check done by the compiler).

> Perhaps the solution is also to reign in the language standard to support stricter use cases. Here's a nine-year-old comment from the author asking for exactly that: https://groups.google.com/g/boring-crypto/c/48qa1kWignU/m/o8...

That thread already spawned a GCC-wiki page https://gcc.gnu.org/wiki/boringcc, with a quote in bold:

>The only thing stopping gcc from becoming the desired boringcc is to find the people willing to do the work.

And frankly, nine years is enough time to build a C compiler from scratch.

Re: Clang vs. Clang

#338

Earlier quoted context omitted.

Even stipulating that part of the argument, the author then goes on a tear about optimizations breaking constant-time evaluation, which doesn’t have anything to do with UB. The real argument seems to be that C compilers had it right when they really did embody C as portable assembly, and everything that’s made that mapping less predictable has been a regression.

But C never had been portable assembly. Which I think is somewhat the core of the problem. People treating things in C in ways they just are not. Weather that is C is portable assembly or C the "it's just bit's in memory" view of things (which often is double wrong ignoring stuff like hardware caching). Or stuff like writing const time code based on assuming that the compiler probably, hopefully can't figure out that…

Nobody says that implementation-defined behavior must be sane or safe. The crux of the issue is that a compiler can assume that UB never happens, while IB is allowed to. Does anyone have an example where the assumption that UB never happens actually makes the program faster and better, compared to UB==IB?

Re: Clang vs. Clang

#339
post #100
post #98

Earlier quoted context omitted.

There's an important point to be made here: those who define the semantics of C and C++ shovel an unreasonable amount of behavior into the bucket of "undefined behavior". Much of this has dubious justifications, while making it more difficult to write correct programs.

To be pedantic, I think you're speaking about unspecified behavior and implementation defined behavior. Undefined behavior specifically refers to things that have no meaningful semantics, so the compiler assumes it never happens. Unspecified behavior is anything outside the scope of observable behavior for which there are two or more ways the implementation can choose. Since the timing of instructions on machines wit…

The point is much of what the C standard currently calls undefined behavior should instead be either unspecified or implementation-defined. This includes the controversial ones like strict aliasing and signed overflow.

Additionally, part of the problem is compiler devs insisting on code transforms that are unsound in the presence of undecidable UB, without giving the programmer sufficiently fine control over such transforms (at best we have a few command line flags for some of them, worst case you'd need to disable all optimizations including the non-problematic ones.)

Re: Clang vs. Clang

#340

Earlier quoted context omitted.

Why not just specify "all branches of this code must execute in the same amount of time", and let the compiler figure it out for the architecture being compiled for?

Instruction duration isn't constant even within the same arch. You cannot have branches in constant-time code. I do wonder though how often cpu instructions have data-dependent execution times....

(Correction: You cannot have data-dependent branches. You can of course have branches to make a fixed-iterations for loop, for example)
Post reply on HN