Skip to content

Commit a9ff63b

Browse files
authored
Alignment fixes (soundness) (#80)
* Alignment fixes (soundness) * cargo fmt * Add codegen-check, verify * Disable coverage for codegen-check * Missing cfg(feature(coverage_attribute))
1 parent 72aac9d commit a9ff63b

20 files changed

Lines changed: 183 additions & 537 deletions

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,24 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## Unreleased
9+
10+
### Fixes
11+
12+
- Fixed a soundness hole in `Vector4` when using smaller scalar types.
13+
14+
### Breaking changes
15+
16+
- Vector types and references can no longer be converted to and from `glam`
17+
types by reference. This fixes a soundness hole, because it relaxes the
18+
alignment requirement of `Vector4`, which would otherwise result in a size
19+
mismatch between `Vector4` and `glam` vectors for smaller scalar types. I.e.
20+
`glam::U8Vec4` has alignment 1. In general, it is brittle to rely on the
21+
specific alignment of `glam` vector types, because they are highly
22+
architecture dependent, and it is unlikely that it gains anything, because
23+
unaligned vector register loads are no longer slow (with AVX, the `vaddps`
24+
instruction supports unaligned loads natively).
25+
826
## [0.17.0] - 2025-05-03
927

1028
### Added

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[workspace]
2-
members = ["tests/wasmtime-guest"]
2+
members = [ "codegen-check","tests/wasmtime-guest"]
33

44
[package]
55
name = "glamour"
@@ -28,7 +28,7 @@ glam = { version = "0.30.0", default-features = false, features = [
2828
"bytemuck",
2929
"approx",
3030
] }
31-
bytemuck = { version = "^1.8", default-features = false }
31+
bytemuck = { version = "^1.23", default-features = false, features = ["must_cast"] }
3232
num-traits = { version = "^0.2.19", default-features = false }
3333
approx = "^0.5"
3434
facet = { version = "0.18.4", optional = true, default-features = false }

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ let size: Size2<MyUnit> = Size2 { width: 100.0, height: 200.0 };
4949
let vector_untyped: &Vector4<f32> = vector.as_untyped();
5050

5151
// Use glam when needed:
52-
let vector_raw: &glam::Vec4 = glamour::Transparent::peel_ref(&vector);
52+
let vector_raw: glam::Vec4 = vector.to_raw();
5353
```
5454

5555
[See the documentation module for more examples.](crate::docs::examples)

codegen-check/Cargo.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[package]
2+
name = "codegen-check"
3+
version = "0.1.0"
4+
edition = "2024"
5+
publish = false
6+
7+
[lib]
8+
path = "lib.rs"
9+
10+
[dependencies]
11+
glamour.path = ".."
12+
13+
[lints.rust]
14+
unexpected_cfgs = { level = "warn", check-cfg = [
15+
'cfg(coverage,coverage_nightly)',
16+
] }

codegen-check/lib.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//! Codegen checks for the `glamour` crate.
2+
//!
3+
//! This is intended to be run manually when checking that glamour types
4+
//! generate specific assembly output.
5+
//!
6+
//! 1. Install `cargo-show-asm` (`cargo install cargo-show-asm`).
7+
//! 2. Run `cargo show-asm --release --target-cpu=native --target codegen-check
8+
//! <symbol>`, where `<symbol>` is one of the functions below.
9+
//!
10+
//! Note that the `#[inline(never)]` is required to force the compiler to
11+
//! generate the symbol.
12+
13+
#![cfg_attr(coverage, feature(coverage_attribute))]
14+
#![cfg_attr(coverage, coverage(off))]
15+
16+
use glamour::Vector4;
17+
18+
#[inline(never)]
19+
pub fn sum_f32x4(v: &[Vector4<f32>]) -> Vector4<f32> {
20+
// This should generate a tight SIMD loop using unaligned loads or `vaddps`
21+
// when the CPU supports it.
22+
v.iter().copied().sum()
23+
}
24+
25+
#[inline(never)]
26+
pub fn sum_u8x4(v: &[Vector4<u8>]) -> Vector4<u8> {
27+
v.iter().copied().sum()
28+
}

src/angle.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ pub struct Angle<U: Scalar = f32> {
3131
}
3232
unsafe impl<T: Scalar> Zeroable for Angle<T> {}
3333
unsafe impl<T: Scalar> Pod for Angle<T> {}
34-
unsafe impl<T: Scalar> Transparent for Angle<T> {
34+
impl<T: Scalar> Transparent for Angle<T> {
3535
type Wrapped = T;
3636
}
3737

@@ -432,8 +432,6 @@ mod tests {
432432
assert_ulps_eq, assert_ulps_ne,
433433
};
434434

435-
use crate::{peel_mut, peel_ref};
436-
437435
use super::*;
438436

439437
type Angle = super::Angle<f32>;
@@ -517,9 +515,7 @@ mod tests {
517515

518516
#[test]
519517
fn angle_cast() {
520-
let mut a = Angle::CIRCLE;
521-
let _: &f32 = peel_ref(&a);
522-
let _: &mut f32 = peel_mut(&mut a);
518+
let a = Angle::CIRCLE;
523519
let _: f32 = peel(a);
524520
let _: f32 = a.to_radians();
525521
let _: Angle = 1.0.into();

src/bindings/vec.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ pub trait Vector:
2626
+ DivAssign<Self>
2727
+ Rem<Self, Output = Self>
2828
+ RemAssign<Self>
29+
+ core::iter::Sum
30+
+ core::iter::Product
2931
+ for<'a> core::iter::Sum<&'a Self>
3032
+ for<'a> core::iter::Product<&'a Self>
3133
{

src/forward.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ macro_rules! forward_fn_self_ref {
179179
{
180180
crate::wrap_ret_val!(
181181
$($($ret)* => )*
182-
crate::peel_ref(self).$fn_name(
182+
crate::peel_copy(self).$fn_name(
183183
$(crate::forward_arg!($arg_name: $arg_ty)),*
184184
)
185185
)
@@ -436,7 +436,7 @@ macro_rules! forward_arg {
436436
crate::peel($arg)
437437
};
438438
($arg:ident: ref_self) => {
439-
crate::peel_ref($arg)
439+
&crate::peel_copy($arg)
440440
};
441441
($arg:ident: ref_scalar_array_4) => {
442442
$arg

src/impl_ops.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -252,12 +252,16 @@ macro_rules! impl_assign_ops {
252252
$(
253253
impl<T: crate::Unit> core::ops::$op_trait<$rhs_ty<T>> for $lhs_ty<T> {
254254
fn $op_name(&mut self, rhs: $rhs_ty<T>) {
255-
core::ops::$op_trait::$op_name(crate::peel_mut(self), crate::peel(rhs))
255+
let mut value = crate::peel_copy(self);
256+
core::ops::$op_trait::$op_name(&mut value, crate::peel(rhs));
257+
*self = crate::wrap(value);
256258
}
257259
}
258260
impl<T: crate::Unit> core::ops::$op_trait<&$rhs_ty<T>> for $lhs_ty<T> {
259261
fn $op_name(&mut self, rhs: &$rhs_ty<T>) {
260-
core::ops::$op_trait::$op_name(crate::peel_mut(self), crate::peel(*rhs))
262+
let mut value = crate::peel_copy(self);
263+
core::ops::$op_trait::$op_name(&mut value, crate::peel(*rhs));
264+
*self = crate::wrap(value);
261265
}
262266
}
263267
)*
@@ -275,12 +279,16 @@ macro_rules! impl_scalar_assign_ops {
275279
$(
276280
impl<T: crate::Unit<Scalar = $rhs_ty>> core::ops::$op_trait<$rhs_ty> for $lhs_ty<T> {
277281
fn $op_name(&mut self, rhs: $rhs_ty) {
278-
core::ops::$op_trait::$op_name(crate::peel_mut(self), rhs);
282+
let mut value = crate::peel_copy(self);
283+
core::ops::$op_trait::$op_name(&mut value, rhs);
284+
*self = crate::wrap(value);
279285
}
280286
}
281287
impl<T: crate::Unit<Scalar = $rhs_ty>> core::ops::$op_trait<&$rhs_ty> for $lhs_ty<T> {
282288
fn $op_name(&mut self, rhs: &$rhs_ty) {
283-
core::ops::$op_trait::$op_name(crate::peel_mut(self), *rhs);
289+
let mut value = crate::peel_copy(self);
290+
core::ops::$op_trait::$op_name(&mut value, *rhs);
291+
*self = crate::wrap(value);
284292
}
285293
}
286294
)*

src/impl_traits.rs

Lines changed: 8 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ macro_rules! impl_basic_traits {
1313

1414
impl<T: Unit> PartialEq for $base_type_name<T> {
1515
fn eq(&self, other: &Self) -> bool {
16-
*crate::peel_ref(self) == *crate::peel_ref(other)
16+
crate::peel_copy(self) == crate::peel_copy(other)
1717
}
1818
}
1919

@@ -76,7 +76,7 @@ macro_rules! impl_basic_traits_vectorlike {
7676
where
7777
H: core::hash::Hasher,
7878
{
79-
core::hash::Hash::hash(crate::peel_ref(self), state)
79+
core::hash::Hash::hash(&crate::peel_copy(self), state)
8080
}
8181
}
8282

@@ -132,12 +132,12 @@ macro_rules! impl_basic_traits_vectorlike {
132132

133133
#[must_use]
134134
fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
135-
crate::peel_ref(self).abs_diff_eq(crate::peel_ref(other), epsilon)
135+
crate::peel_copy(self).abs_diff_eq(&crate::peel_copy(other), epsilon)
136136
}
137137

138138
#[must_use]
139139
fn abs_diff_ne(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
140-
crate::peel_ref(self).abs_diff_ne(crate::peel_ref(other), epsilon)
140+
crate::peel_copy(self).abs_diff_ne(&crate::peel_copy(other), epsilon)
141141
}
142142
}
143143

@@ -154,7 +154,7 @@ macro_rules! impl_basic_traits_vectorlike {
154154
epsilon: Self::Epsilon,
155155
max_relative: Self::Epsilon,
156156
) -> bool {
157-
crate::peel_ref(self).relative_eq(crate::peel_ref(other), epsilon, max_relative)
157+
crate::peel_copy(self).relative_eq(&crate::peel_copy(other), epsilon, max_relative)
158158
}
159159

160160
#[must_use]
@@ -164,7 +164,7 @@ macro_rules! impl_basic_traits_vectorlike {
164164
epsilon: Self::Epsilon,
165165
max_relative: Self::Epsilon,
166166
) -> bool {
167-
crate::peel_ref(self).relative_ne(crate::peel_ref(other), epsilon, max_relative)
167+
crate::peel_copy(self).relative_ne(&crate::peel_copy(other), epsilon, max_relative)
168168
}
169169
}
170170

@@ -176,12 +176,12 @@ macro_rules! impl_basic_traits_vectorlike {
176176

177177
#[must_use]
178178
fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
179-
crate::peel_ref(self).ulps_eq(crate::peel_ref(other), epsilon, max_ulps)
179+
crate::peel_copy(self).ulps_eq(&crate::peel_copy(other), epsilon, max_ulps)
180180
}
181181

182182
#[must_use]
183183
fn ulps_ne(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
184-
crate::peel_ref(self).ulps_ne(crate::peel_ref(other), epsilon, max_ulps)
184+
crate::peel_copy(self).ulps_ne(&crate::peel_copy(other), epsilon, max_ulps)
185185
}
186186
}
187187

@@ -283,29 +283,6 @@ macro_rules! impl_basic_traits_vectorlike {
283283
crate::peel(value)
284284
}
285285
}
286-
impl<T: Unit<Scalar = $scalar>> core::borrow::Borrow<$glam_ty> for $base_type_name<T>
287-
{
288-
fn borrow(&self) -> &$glam_ty {
289-
crate::peel_ref(self)
290-
}
291-
}
292-
impl<T: Unit<Scalar = $scalar>> core::borrow::BorrowMut<$glam_ty>
293-
for $base_type_name<T>
294-
{
295-
fn borrow_mut(&mut self) -> &mut $glam_ty {
296-
crate::peel_mut(self)
297-
}
298-
}
299-
impl<T: Unit<Scalar = $scalar>> AsRef<$glam_ty> for $base_type_name<T> {
300-
fn as_ref(&self) -> &$glam_ty {
301-
crate::peel_ref(self)
302-
}
303-
}
304-
impl<T: Unit<Scalar = $scalar>> AsMut<$glam_ty> for $base_type_name<T> {
305-
fn as_mut(&mut self) -> &mut $glam_ty {
306-
crate::peel_mut(self)
307-
}
308-
}
309286
)*
310287
};
311288
}

0 commit comments

Comments
 (0)