Skip to content

Commit

Permalink
Mark push_next() as unsafe and add push_one() alternative
Browse files Browse the repository at this point in the history
`push_next()` is totally `unsafe` because of dereferencing a chain of
`p_next` pointers to find the end of the chain to insert, which was
obfuscated by a large `unsafe` block for the `BaseOutStructure` pointer
cast in commit c8c8f69 ("`next` can contain a pointer chain and we need
to correct insert it.").

While this function should definitely be marked unsafe, wrapping
builders in `unsafe {}` en masse in user code isn't all too desirable,
especially when this soundness issue only exists to optionally walk
a `p_next` chain while most users are likely inserting bare structs
without pointer chains most of the time.  `push_one()` is introduced for
this reason, remaining safe to call without any unintended raw pointer
dereferences.
  • Loading branch information
MarijnS95 committed Dec 4, 2024
1 parent 7dcf57b commit e567485
Show file tree
Hide file tree
Showing 5 changed files with 3,704 additions and 924 deletions.
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `get_pipeline_executable_properties()`;
- `get_pipeline_executable_statistics()`.
The expected length of this array can be queried with the respective `*_len()` variant of these functions.
- `push_next()` has been marked as `unsafe` and users are encouraged to call `push_one()` instead. (#909)

## [0.38.0] - 2024-04-01

Expand Down
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,19 +91,20 @@ let device: Device = instance

### Pointer chains

Use `base.push_next(ext)` to insert `ext` at the front of the pointer chain attached to `base`.
Use `base.push_one(ext)` to insert `ext` at the front of the pointer chain attached to `base`. If `ext` already contains a pointer chain of its own,
call `unsafe` `push_next()` instead.

```rust
let mut variable_pointers = vk::PhysicalDeviceVariablePointerFeatures::default();
let mut corner = vk::PhysicalDeviceCornerSampledImageFeaturesNV::default();

let mut device_create_info = vk::DeviceCreateInfo::default()
.push_next(&mut corner)
.push_next(&mut variable_pointers);
.push_one(&mut corner)
.push_one(&mut variable_pointers);
```

The generic argument of `.push_next()` only allows valid structs to extend a given struct (known as [`structextends` in the Vulkan registry](https://registry.khronos.org/vulkan/specs/1.3/styleguide.html#extensions-interactions), mapped to `Extends*` traits).
Only structs that are listed one or more times in any `structextends` will implement a `.push_next()`.
The generic argument of `.push_one()` only allows valid structs to extend a given struct (known as [`structextends` in the Vulkan registry](https://registry.khronos.org/vulkan/specs/1.3/styleguide.html#extensions-interactions), mapped to `Extends*` traits).
Only structs that are listed one or more times in any `structextends` will implement a `.push_one()`.

### Flags and constants as associated constants

Expand Down
4 changes: 2 additions & 2 deletions ash/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,8 @@ mod tests {
<*mut _>::cast(&mut corner),
];
let mut device_create_info = vk::DeviceCreateInfo::default()
.push_next(&mut corner)
.push_next(&mut variable_pointers);
.push_one(&mut corner)
.push_one(&mut variable_pointers);
let chain2: Vec<*mut vk::BaseOutStructure<'_>> = unsafe {
vk::ptr_chain_iter(&mut device_create_info)
.skip(1)
Expand Down
Loading

0 comments on commit e567485

Please sign in to comment.