Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[v0.5] New design with two wrapper types: VolatilePtr and VolatileRef #29

Merged
merged 71 commits into from
Jun 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
a7ae579
New design based on `UnsafeCell`
phil-opp May 18, 2021
946b37b
Use `*mut T` instead of `&UnsafeCell<T>` since the latter is derefere…
phil-opp May 21, 2021
682dd70
Add access types for allowing only unsafe reads and/or writes
phil-opp May 21, 2021
0699e4e
Remove stabilized const_generics feature
phil-opp Jun 13, 2021
2ece021
Redesign access types and adjust (doc)tests
phil-opp Jun 13, 2021
534ec6e
Use `NonNull` instead of `*mut T`
phil-opp Jun 13, 2021
c3d6b9a
Add lifetime parameter and rename to `VolatilePtr`
phil-opp Jun 14, 2021
9f7b015
Fix: do bounds checking in `index` and `index_mut`
phil-opp Jun 14, 2021
b525f38
Add `const` index functions under a new `very_unstable` feature
phil-opp Jun 14, 2021
4accd51
add `VolatilePtr::as_slice_mut`
Freax13 Jun 4, 2022
a9e8509
add `VolatilePtr::iter` & `VolatilePtr::iter_mut`
Freax13 Jun 4, 2022
167adec
add `VolatilePtr::is_empty`
Freax13 Jun 4, 2022
aa93fb9
add missing access where bounds
Freax13 Jun 4, 2022
554c806
reject mapping to unaligned fields
Freax13 Jun 4, 2022
ff0ee4f
WIP: owned with borrow
Freax13 Jun 15, 2022
cd57be9
fix UAF in doctest
Freax13 Jul 3, 2022
73cd996
Re-add the wrapper type used in `v0.3` as `VolatileCell`
phil-opp Jan 17, 2023
6ab1af4
Move pointer-based implementation to submodule
phil-opp Jan 17, 2023
d69da5c
Merge branch 'unsafe-cell-but-owned' into next
phil-opp Jan 17, 2023
111db0a
Adjust stable functions of `VolatilePtr` to simplified access types
phil-opp Jan 17, 2023
8385818
Add methods for borrowing a `VolatileCell` as `VolatilePtr`
phil-opp Jan 17, 2023
2fbc956
Use new pointer borrow methods for implementing read/write functions …
phil-opp Jan 17, 2023
cf4c57c
Wrap `VolatileCell::value` in `UnsafeCell` to prevent aliasing issues
phil-opp Jan 17, 2023
1d2d3bf
Reexport `VolatileCell` and `VolatilePtr`
phil-opp Jan 17, 2023
c323667
Improve generated docs
phil-opp Jan 17, 2023
5da07e7
Fix: Using `borrow` on a `WriteOnly` pointer should result in no access
phil-opp Jan 17, 2023
06404ff
Move trait bounds to methods (instead of impl blocks)
phil-opp Jan 17, 2023
60b2db2
Remove unneeded unsafe
phil-opp Jan 17, 2023
9b03f61
Remove unneeded extern crate statements in doc tests
phil-opp Jan 17, 2023
f5602e7
Move `VolatilePtr` trait bounds to methods (instead of impl blocks)
phil-opp Jan 17, 2023
2be8bbd
Reorder impl blocks (unstable features last)
phil-opp Jan 17, 2023
70fa6ca
Use 2021 edition
phil-opp Jan 17, 2023
f3795cc
Remove unused import
phil-opp Jan 17, 2023
f21ef3f
Fix doctests
phil-opp Jan 17, 2023
cb0fc46
Add a `new_read_only` convenience function
phil-opp Jan 17, 2023
4a8c18b
Create an internal `new_generic` constructor
phil-opp Jan 17, 2023
d0a237a
Update unstable methods and their doc tests
phil-opp Jan 17, 2023
bbb23f4
Remove stabilized features
phil-opp Jan 17, 2023
a143e0b
Update `very_unstable` to work with new access types and latest nightly
phil-opp Jan 17, 2023
b7cb879
Add an alternative `VolatilePtrCopy` variant that implement Copy
phil-opp Jan 17, 2023
99e3329
Implement Send/Sync for `VolatilePtr`
phil-opp Jan 17, 2023
927b061
Merge pull request #28 from Freax13/unsafe-cell-but-owned
phil-opp Jan 17, 2023
24326c3
Merge branch 'unsafe-cell' into next
phil-opp Jan 17, 2023
c948890
Refactor macros and unstable functions into submodules
phil-opp Jan 17, 2023
01cbbdc
Adjust and enable `VolatilePtr` tests
phil-opp Jan 17, 2023
e1578af
Implement test suite for `VolatilePtrCopy` too
phil-opp Jan 17, 2023
8201fa5
Rename `VolatilePtr` to `VolatilePtrSend`
phil-opp Jan 17, 2023
8e71845
Remove the `VolatileCell` type for now
phil-opp Jan 17, 2023
dd5cfaf
Provide a default `Access` implementation for `Readable`
phil-opp Jan 20, 2023
42fe49d
Add conversion methods from `VolatilePtrSend` to `VolatilePtrCopy`
phil-opp Jan 20, 2023
6fd4ea1
Rename non-copy pointer type to `VolatileRef`
phil-opp Apr 28, 2023
401d903
Rename copyable pointer type to `VolatilePtr`
phil-opp Apr 28, 2023
1e265b1
Remove most methods from `VolatileRef` to avoid duplication
phil-opp Apr 28, 2023
34ecfdc
Various improvements to `VolatilePtr` type
phil-opp Apr 28, 2023
ad02642
Make `VolatileRef` copyable if it's read-only
phil-opp Apr 28, 2023
519895e
Improve docs
phil-opp Apr 28, 2023
739e5b2
Fix unstable module
phil-opp Apr 28, 2023
e539980
Explain why there is no `VolatileCell` type
phil-opp Jun 24, 2023
bacaa77
Update crate description in README
phil-opp Jun 24, 2023
50cbec6
Update crate metadata
phil-opp Jun 24, 2023
759e312
Delete old `ref.rs` file
phil-opp Jun 24, 2023
24db296
Remove `*_mut` variants of pointer methods
phil-opp Jun 24, 2023
16dcea3
Print pointer instead of values in `Debug` impl
phil-opp Jun 24, 2023
f220854
Document the constructor functions
phil-opp Jun 24, 2023
00fc090
Document the conversion functions and add a `into_ptr` method
phil-opp Jun 24, 2023
c82b35d
Remove uneeded feature gates
phil-opp Jun 24, 2023
2108aad
Document remaining methods
phil-opp Jun 24, 2023
6410c4e
Silence two clippy warnings
phil-opp Jun 24, 2023
020ffbe
Update cargo-release configuration to latest version
phil-opp Jun 24, 2023
0d51b27
Write changelog entry for 0.5 release
phil-opp Jun 24, 2023
225ae4c
Also test `very_unstable` feature on CI
phil-opp Jun 24, 2023
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
20 changes: 20 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,26 @@ jobs:
command: test
args: --features unstable

very_unstable:
name: Test Suite (very_unstable)
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2

- name: Install nightly toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
override: true

- name: Run cargo test --features very_unstable
uses: actions-rs/cargo@v1
with:
command: test
args: --features very_unstable


lints:
name: Lints
Expand Down
11 changes: 8 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,25 @@ version = "0.4.6"
authors = ["Philipp Oppermann <[email protected]>"]
license = "MIT OR Apache-2.0"
keywords = ["volatile"]
description = "A simple volatile wrapper type"
description = "Volatile wrapper types for raw pointers"
documentation = "https://docs.rs/volatile"
repository = "https://github.com/rust-osdev/volatile"
edition = "2021"

[dependencies]

[features]
# Enable unstable features; requires Rust nightly; might break on compiler updates
unstable = []
# Enable unstable and experimental features; requires Rust nightly; might break on compiler updates
very_unstable = ["unstable"]

[dev-dependencies]
rand = "0.8.3"

[package.metadata.release]
dev-version = false
pre-release-replacements = [
{ file="Changelog.md", search="# Unreleased", replace="# Unreleased\n\n# {{version}} – {{date}}", exactly=1 },
{ file = "Changelog.md", search = "# Unreleased", replace = "# Unreleased\n\n# {{version}} – {{date}}", exactly = 1 },
]
pre-release-commit-message = "Release version {{version}}"

Expand Down
7 changes: 7 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Unreleased

- **Breaking:** [New design based on raw pointers](https://github.com/rust-osdev/volatile/pull/29)
- The previous reference-based design was [unsound](https://github.com/rust-osdev/volatile/pull/13#issuecomment-842455552) because it allowed the compiler to insert spurious reads.
- The new design features two wrapper types for raw pointers: `VolatilePtr` and `VolatileRef`
- `VolatilePtr` provides safe read and write access to volatile values. Like raw pointers, it implements `Copy` and is `!Sync`.
- `VolatileRef` is a pointer type that respects Rust's aliasing rules. It doesn't implement `Copy`, requires a `&mut` reference for modification, and implements `Sync`. It can converted to temporary `VolatilePtr` instances through the `as_ptr`/`as_mut_ptr` methods.
- We now provide methods for volatile slice operations and a `map!` macro for struct field projection. These advanced features are gated behind a cargo feature named _"unstable"_.

# 0.4.6 – 2023-01-17

- Fix UB in slice methods when Deref returns different references ([#27](https://github.com/rust-osdev/volatile/pull/27))
Expand Down
22 changes: 20 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,27 @@

[![Build Status](https://github.com/rust-osdev/volatile/workflows/Build/badge.svg)](https://github.com/rust-osdev/volatile/actions?query=workflow%3ABuild) [![Docs.rs Badge](https://docs.rs/volatile/badge.svg)](https://docs.rs/volatile/)

Provides the wrapper type `Volatile`, which wraps a reference to any copy-able type and allows for volatile memory access to wrapped value. Volatile memory accesses are never optimized away by the compiler, and are useful in many low-level systems programming and concurrent contexts.
Provides volatile wrapper types for raw pointers.

The wrapper types *do not* enforce any atomicity guarantees; to also get atomicity, consider looking at the `Atomic` wrapper types found in `libcore` or `libstd`.
The volatile wrapper types in this crate wrap a pointer to any [`Copy`]-able type and provide volatile memory access to wrapped value.
Volatile memory accesses are never optimized away by the compiler, and are useful in many low-level systems programming and concurrent contexts.

This crate provides two different wrapper types: [`VolatilePtr`] and [`VolatileRef`].
The difference between the two types is that the former behaves like a raw pointer, while the latter behaves like a Rust reference type.
For example, `VolatilePtr` can be freely copied, but not sent across threads because this could introduce mutable aliasing.
The `VolatileRef` type, on the other hand, requires exclusive access for mutation, so that sharing it across thread boundaries is safe.

Both wrapper types *do not* enforce any atomicity guarantees; to also get atomicity, consider looking at the `Atomic` wrapper types found in `libcore` or `libstd`.

## Why is there no `VolatileCell`?

Many people expressed interest in a `VolatileCell` type, i.e. a transparent wrapper type that owns the wrapped value.
Such a type would be similar to [`core::cell::Cell`], with the difference that all methods are volatile.
Unfortunately, it is not sound to implement such a `VolatileCell` type in Rust.
The reason is that Rust and LLVM consider `&` and `&mut` references as _dereferencable_.
This means that the compiler is allowed to freely access the referenced value without any restrictions.
So no matter how a `VolatileCell` type is implemented, the compiler is allowed to perform non-volatile read operations of the contained value, which can lead to unexpected (or even undefined?) behavior.
For more details, see the discussion [in our repository](https://github.com/rust-osdev/volatile/issues/31) and [in the `unsafe-code-guidelines` repository](https://github.com/rust-lang/unsafe-code-guidelines/issues/411).

## License

Expand Down
77 changes: 69 additions & 8 deletions src/access.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,83 @@
//! Marker types for limiting access.

/// Private trait that is implemented for the types in this module.
pub trait Access: Copy + Default {
/// Ensures that this trait cannot be implemented outside of this crate.
#[doc(hidden)]
fn _private() -> _Private {
_Private
}

/// Reduced access level to safely share the corresponding value.
type RestrictShared: Access;
}

/// Helper trait that is implemented by [`ReadWrite`] and [`ReadOnly`].
pub trait Readable {}
pub trait Readable: Copy + Default {
/// Reduced access level to safely share the corresponding value.
type RestrictShared: Readable + Access;

/// Ensures that this trait cannot be implemented outside of this crate.
fn _private() -> _Private {
_Private
}
}

/// Helper trait that is implemented by [`ReadWrite`] and [`WriteOnly`].
pub trait Writable {}
pub trait Writable: Access {
/// Ensures that this trait cannot be implemented outside of this crate.
fn _private() -> _Private {
_Private
}
}

/// Implemented for access types that permit copying of `VolatileRef`.
pub trait Copyable {
/// Ensures that this trait cannot be implemented outside of this crate.
fn _private() -> _Private {
_Private
}
}

impl<T> Access for T
where
T: Readable + Default + Copy,
{
type RestrictShared = <T as Readable>::RestrictShared;
}

/// Zero-sized marker type for allowing both read and write access.
#[derive(Debug, Copy, Clone)]
#[derive(Debug, Default, Copy, Clone)]
pub struct ReadWrite;
impl Readable for ReadWrite {}
impl Readable for ReadWrite {
type RestrictShared = ReadOnly;
}
impl Writable for ReadWrite {}

/// Zero-sized marker type for allowing only read access.
#[derive(Debug, Copy, Clone)]
#[derive(Debug, Default, Copy, Clone)]
pub struct ReadOnly;

impl Readable for ReadOnly {}
impl Readable for ReadOnly {
type RestrictShared = ReadOnly;
}
impl Copyable for ReadOnly {}

/// Zero-sized marker type for allowing only write access.
#[derive(Debug, Copy, Clone)]
#[derive(Debug, Default, Copy, Clone)]
pub struct WriteOnly;
impl Access for WriteOnly {
type RestrictShared = NoAccess;
}
impl Writable for WriteOnly {}

/// Zero-sized marker type that grants no access.
#[derive(Debug, Default, Copy, Clone)]
pub struct NoAccess;
impl Access for NoAccess {
type RestrictShared = NoAccess;
}
impl Copyable for NoAccess {}

#[non_exhaustive]
#[doc(hidden)]
pub struct _Private;
Loading