Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ctutils/checkct/driver/src/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ macro_rules! private_integer {
mod comparisons;
mod composition;
mod negation;
mod nonzero;
mod predicates;
mod primitives;

Expand Down
64 changes: 64 additions & 0 deletions ctutils/checkct/driver/src/driver/nonzero.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//! Nonzero constructors with caller range information still visible.

use super::*;

macro_rules! nonzero_checks {
($module:ident, $ty:ty, $nz:ident) => {
mod $module {
use super::*;

fn private() -> $ty {
black_box(PublicRng.next_u32());
private_integer!($ty)
}

#[checkct]
pub fn unrestricted() {
black_box(Choice::$nz(private()).to_u8());
}

#[checkct]
pub fn shifted() {
// The u64 case previously branched on Thumb-1.
black_box(Choice::$nz(private() >> 1).to_u8());
}

#[checkct]
pub fn sign_bit_clear() {
black_box(Choice::$nz(private() & (<$ty>::MAX >> 1)).to_u8());
}
}
};
}

nonzero_checks!(u8_ops, u8, from_u8_nz);
nonzero_checks!(u16_ops, u16, from_u16_nz);
nonzero_checks!(u32_ops, u32, from_u32_nz);
nonzero_checks!(u64_ops, u64, from_u64_nz);
nonzero_checks!(u128_ops, u128, from_u128_nz);

macro_rules! positive_nonzero_check {
($name:ident, $signed:ty, $unsigned:ty, $nz:ident) => {
#[checkct]
pub fn $name() {
black_box(PublicRng.next_u32());
let value = private_integer!($signed);
black_box(
Choice::$nz((value & !(value >> (<$signed>::BITS - 1))) as $unsigned).to_u8(),
);
}
};
}

positive_nonzero_check!(positive_i8, i8, u8, from_u8_nz);
positive_nonzero_check!(positive_i16, i16, u16, from_u16_nz);
positive_nonzero_check!(positive_i32, i32, u32, from_u32_nz);

// On Thumb-1, the wide signed-mask argument itself can branch before ctutils
// receives it. Keep this composition regression on targets where the argument
// is constant-time; the shifted probes above exercise all widths on Thumb-1.
// Callers needing a signed comparison should bias the sign bit and use from_u*_lt.
#[cfg(not(all(target_arch = "arm", not(target_has_atomic = "ptr"))))]
positive_nonzero_check!(positive_i64, i64, u64, from_u64_nz);
#[cfg(not(all(target_arch = "arm", not(target_has_atomic = "ptr"))))]
positive_nonzero_check!(positive_i128, i128, u128, from_u128_nz);
8 changes: 4 additions & 4 deletions ctutils/src/choice.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::primitives::{bitle, bitnz};
use crate::primitives::bitle;
use crate::{CtAssign, CtAssignSlice, CtEq, CtEqSlice, CtSelectUsingCtAssign};
use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not};

Expand Down Expand Up @@ -138,7 +138,7 @@ impl Choice {
#[inline]
#[must_use]
pub const fn from_u8_nz(value: u8) -> Self {
Self::from_u8_lsb(bitnz!(value, u8::BITS))
crate::primitives::nz_u8(value)
}

// u16
Expand Down Expand Up @@ -183,7 +183,7 @@ impl Choice {
#[inline]
#[must_use]
pub const fn from_u16_nz(value: u16) -> Self {
Self::from_u16_lsb(bitnz!(value, u16::BITS))
crate::primitives::nz_u16(value)
}

// u32
Expand Down Expand Up @@ -323,7 +323,7 @@ impl Choice {
#[inline]
#[must_use]
pub const fn from_u128_nz(value: u128) -> Self {
Self::from_u128_lsb(bitnz!(value, u128::BITS))
crate::primitives::nz_u128(value)
}

//
Expand Down
37 changes: 29 additions & 8 deletions ctutils/src/primitives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,34 @@ macro_rules! bitnz {
};
}

pub(crate) use {bitborrow, bitle, bitlt, bitnz};
pub(crate) use {bitborrow, bitle, bitlt};

// Shared by every nonzero constructor, including the widths which do not use
// comparison_primitives. A result barrier alone does not prevent specialization
// of the arithmetic which computes the predicate.
macro_rules! nonzero_primitive {
($ty:ty, $nz:ident, $from_lsb:ident) => {
#[inline(always)]
pub(crate) const fn $nz(value: $ty) -> Choice {
// Hide caller range information before computing the predicate.
// For example, a shifted value or signed mask can otherwise turn
// the nonzero test into a secret-dependent comparison branch.
#[cfg(any(
target_arch = "riscv32",
all(target_arch = "arm", not(target_has_atomic = "ptr"))
))]
let value = black_box(value);
let bit = bitnz!(value, <$ty>::BITS);
#[cfg(all(target_arch = "arm", not(target_has_atomic = "ptr")))]
let bit = black_box(bit);
Choice::$from_lsb(bit)
}
};
}

nonzero_primitive!(u8, nz_u8, from_u8_lsb);
nonzero_primitive!(u16, nz_u16, from_u16_lsb);
nonzero_primitive!(u128, nz_u128, from_u128_lsb);

// Keep the comparison bit opaque before narrowing it to Choice's byte representation.
// ARM targets without pointer-width atomics cover Thumb-1 on Rust 1.87, which does not
Expand Down Expand Up @@ -75,13 +102,7 @@ macro_rules! comparison_primitives {
Choice::$from_lsb(bit)
}

#[inline(always)]
pub(crate) const fn $nz(value: $ty) -> Choice {
let bit = bitnz!(value, <$ty>::BITS);
#[cfg(all(target_arch = "arm", not(target_has_atomic = "ptr")))]
let bit = black_box(bit);
Choice::$from_lsb(bit)
}
nonzero_primitive!($ty, $nz, $from_lsb);
};
}

Expand Down
Loading