3 ms·
Too bad that Intel chips more or less reserve the right to take LLVM’s nice output and make it non-constant-time anyway. See: https://www.intel.com/content/ww
by amluto 11mo ago
Too bad that Intel chips more or less reserve the right to take LLVM’s nice output and make it non-constant-time anyway. See:
https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/best-practices/data-operand-independent-timing-isa-guidance.html https://www.intel.com/content/www/us/en/developer/articles/t...
Sure, you could run on some hypothetical OS that supports DOITM and insert syscalls around every manipulation of secret data. Yeah, right.
- JoshTriplett 11mo agoLast I saw, it seemed like the plan was to unconditionally enable it, and on the off chance there's ever a piece of hardware where it's a substantial performance win, offer a way to opt out of it.
- amluto 11mo agoI advocated for that, and then I completely lost track of the status. The whole design is ridiculous.
- matu3ba 11mo agoWhat would be more sane alternatives, when it becomes obvious that any side-effect of timing is a potential attack vector? See https://www.hertzbleed.com/ https://www.hertzbleed.com/ for frequency side channels. I do only see dedicated security cores as options with fast data lanes to the CPU similar to what Apple is doing with Secure Enclave or do you have better suggestions that still allow performance and power savings?
- amluto 11mo agoA design such that it would actually make sense for a compiler to mark code that should permit data-dependent CPU optimizations differently from code that should not. This could be done using an opcode prefix, which would bloat code but would work perfectly. Or it could use an RFLAGS bit or a bit in MXCSR or a new register, etc. Almost anything would be better than an MSR that is only accessible to privileged code.
- cesarb 11mo ago> Or it could use an RFLAGS bit or a bit in MXCSR or a new register, etc. > Almost anything would be better than an MSR that is only accessible to privileged code. ARM does that: their flag (DIT) is accessible by non-privileged code. If you know the architecture has that flag, either because your -march= is recent enough or because the operating system told you so through the hwcaps or the emulated id registers, you can use it freely without needing to switch to privileged mode through a syscall.
- JoshTriplett 11mo agoThe one reason I can imagine to make it privileged-only is that it could be high-overhead to switch: if a CPU conditioned various kinds of predictors on it, it might have to flush those predictors when toggling it.
- amluto 11mo agoIMO the best design would be to keep the flag with the data. Give each register an extra bit indicating whether it’s sensitive. Any data-dependent-timing operation can’t possibly leak the data until the data is available to it, and that’s exactly when the ALU would find out that the data is sensitive anyway. No pipeline stalls.
- JoshTriplett 11mo agoMakes sense, but "give each register an extra bit" seems like something that would very easily get lost with a spill or any number of other ways.
- matu3ba 11mo agoSorry for necro-bumping, but there is a paper doing exactly that besides various other things to eliminate timing channels claiming also to prevent attacks based on speculative execution etc: "BLACKOUT : Data-Oblivious Computation with Blinded Capabilities" https://arxiv.org/abs/2504.14654 https://arxiv.org/abs/2504.14654. They basically utilize another bit of CHERI for "blinded capability" and methods to mitigate potential problems you identified.
- stingraycharles 11mo agoSorry, I may be missing the point here, but reading that page doesn’t immediately make it obvious to me what that feature is. Is it some constant time execution mechanism that you can enable / disable on a per-thread basis to do… what exactly?
- wat10000 11mo agoIt turns off CPU features that could cause execution time to vary in a way that depends on the data being operated on.
- stingraycharles 11mo agoIsn’t that exactly what you want for constant time algorithms? So why does the grandparent cite this article as Intel preventing LLVM’s techniques from working?
- wat10000 11mo agoI think the complaint is that this feature isn’t very practical to use (sounds like it needs OS support and a syscall to turn it on and off), and the fact that it exists implies that code that looks like it would be constant time may not be if this feature isn’t enabled.
- seanhunter 11mo agoAs a concrete example, say I have a (very naive and bad) password-checker that works like this pseudocode: > for i = 1 to len(real_password) { > if entered_password[i] != real_password[i] { > return FAILURE > } > } > > return SUCCESS OK now an alert attacker with the ability to very accurately record the time it takes to check the password can determine the length at least of the real password, because the time complexity of this check is O(length of the real password), and they could also gradually determine the password itself because the check would take longer as the attacker got each successive character correct. Taking this general idea and expanding it, there are lots of places where the timing of branches of code can leak information about some secret, so in cryptographic code in particular, it’s often beneficial to be able to ensure that two branches (the success and failure branches in the above) take exactly the same amount of time so the timing doesn’t leak information. So to fix the above you would probably want to do two things. Firstly set a boolean to failure and still continue the checking to ensure the “return failure quickly” problem doesn’t leak information and also change your password check to check against a fixed-width hash or something so the length of the password itself wasn’t a factor. The problem is lots of performance optimizations (pipelining, branch prediction etc) work specifically against this goal- they aim to take branches quickly in the happy path of the code because normally that’s what you want to ensure optimal performance. So say instead of the above I do > bool status = SUCCESS > for i = 1 to hash_length { > if hash_of_entered_password[i] != hash_of_real_password[i] { > status = FAILURE > } > } > > return status …I don’t want the optimizer to realize that when status becomes FAILURE it can never become SUCCESS again and the loop doesn’t do anything else so just return early. I want it to actually run the pointless comparison of the rest of the hash so the timing is exactly the same each time. But now my check is constant time but I’ve shifted the burden onto the person who writes the hash function. That has to run in constant time or my check will once again leak. So in general people want the ability to tell the compiler that they want a particular piece of code to run in constant time. At the moment, in the general case I think you have to break into inline assembly to achieve this.