Proposal: Security Working Group

This is a great idea. I would love to join this WG :star_struck:

2 Likes

Yes, please no Discord.

I can contribute a few hours a week to the WG. I’d prefer an async communication platform as I’m in the UTC+1 timezone but otherwise I don’t have a strong opinion.

1 Like

Is this opaque to the optimizer?

No, it is just an operation, like casting an i32 to an i64.

Whether such operations are opaque to the optimizer is kind of orthogonal to the operation itself. You can make any code un-optimizable by using, depending on what "level" of disabled optimizations you want: #[cold], #[inline(never)], the LLVM optnone attribute, inline asm!, etc.

Folks here may be interested in this proposal I just made for a process around the Unsafe Code Guidelines.

2 Likes

I've been doing security-related work on Rust lately on my own and I do not intend to stop, so I'd volunteer for the WG.

Oh my god, YES.

Performance seems to be a common reason, and a guide on getting safe code go fast (and diagnosing why it doesn't) is badly needed. I've already argued this in more detail in my "Auditing popular crates" post.

Based on my experience with auditing Rust code so far, I can suggest the following actionable work items:

Plus a bunch of other, less critical things that I don't want to list right now.

But the guide to replacing unsafe code with safe equivalents would be my #1 priority in this endeavor. Just to get the ball rolling and have something actionable, let's think how the use of unsafe in Claxon could have been avoided so that this bug would have been prevented.

6 Likes

I would love to volunteer

1 Like

Yeah, I'm a big fan of this approach. I've been working on something that I hope to eventually publish to the community once we get enough experience with it on Fuchsia that is in this vein. It's worked really well for us, and I expect that the community will be able to get a lot of mileage out of that general approach.

2 Likes

The problem isn’t with converting i1 to iN (or u1 to uN), it is with realizing the Boolean result of a hardware comparison operation as an i1 or u1 in constant time. It is this process that usually causes LLVM codegen to output conditional branch instructions which have timing-observable side effects.

Many instruction-set architectures (e.g., RISC-V, ARM Cortex M, ARMv8 ) provide a way to materialize a Boolean result of a comparison directly in a register, enabling crypto algorithms to generate a bit mask of 0 or -1 and thus operate in constant time. (This approach is too inefficient for general use.) We need to have a way to annotate crypto-sensitive comparisons for MIR so that it has LLVM and other backends generate constant-time compare-to-Boolean code sequences via architecture-specific intrinsics, rather than generating conditional-branch instructions around loads of an immediate 0 or -1. That is the point I failed to make clear in my earlier post.

This is actually how most FFI crates work in practice today. (And I consider it good practice.)

Good to know. This was my first stab at FFI, so I was pretty unaware of the prior art, so I didn't realize it wasn't new lol. Do your research, kids.

1 Like

To more forward with the WG I suggest starting a discussion on a structured debate platform such as Kialo regarding the scope. This thread is already getting unwieldy, and we’ve only just started.

Some like that would also be helpful to decide on a real-time communications platform to use, if any.

Ah! The only semi-reliable way that I know of to control whether conditional branch instructions are used in LLVM is to use the likely/unlikely intrinsics (that's basically all they do).

We need to have a way to annotate crypto-sensitive comparisons for MIR so that it has LLVM and other backends

Does LLVM support this? I can't find any attributes for this in the lang ref.

LLVM supports conditional compare of a pair of integer or float scalars or vectors, generating an i1 or N x i1 result

- <result> = icmp <cond> <ty> <op1>, <op2>
  yields i1 or <N x i1>:result 
  where cond = {eq, ne, ugt, uge, ult, ule, sgt, sge, slt, sle}
  and the prefix `u` means "unsigned" while the prefix `s` means "signed".

- <result> = fcmp [fast-math flags]* <cond> <ty> <op1>, <op2>
  yields i1 or <N x i1>:result
  where cond = {false, oeq, ogt, oge, olt, ole, one, ord, ueq, ugt, uge, ult, ule, une, uno, true}  
  and the prefix `o` means "ordered and …" while the prefix 'u' means "unordered or …".

Many Rust Tier 1 ISAs support the scalar integer compares and some variety of the float compares in single instructions. Some earlier ARM ISAs (e.g., Cortex M) require a cumbersome multi-instruction sequence. Other ISAs used in IoT devices might not have any support at all.

I meant whether LLVM supported an annotation to force the generation of constant-time code.

Without it, I don’t think we can feasibly emulate the lack of such an LLVM feature in rustc (nothing is impossible, but this would be… challenging).

My reply was meant to show that LLVM already contains the required backend support. A grep of the LLVM 6.0.1 source provides over 40k hits (in over 5k files total) on the string \W[fi]cmp\W. That also served to locate the interesting file include\llvm\Analysis\CmpInstAnalysis.h whose header reads

// This file holds routines to help analyse compare instructions
// and fold them into constants or other compare instructions

The above statistics – over 40k hits in over 5k files total – imply to me that the LLVM team has provided the needed infrastructure and we just haven’t found the means of invoking it for constant-time compares.

So you are saying that icmp and fcmp are guaranteed to be lowered to constant-time machine code and that you would like Rust C to emit those instead of some other IR for comparisons? If so, what is the IR that we are currently emitting?

EDIT: AFAICT the LLVM LangRef does not provide any constant-time guarantees for icmp/fcmp :confused:

I like your contributions. If you want your words to remain visible to the Rust community for future reference, you’d need to stick the LLVM discussion in its own thread. Otherwise, this LLVM discussion will just be repeated another time with no real visibility on what was said here because it’s hidden in another thread.

1 Like

There is an rfc issue open: https://github.com/rust-lang/rfcs/issues/847 So maybe we can move the discussion there?