Skip to content

Conversation

@mtreinish
Copy link

When iterating over a view of a row major matrix and finishing a column during iteration it is possible for the pointer to be incremented outside the allocation when determining the current column has been finished. This is fine because the pointer never is actually reading from outside the allocation, the inner_end value used to track we've finished the column will match on the next call to next() and never actually attempt to read from outside the allocation for the matrix. However, this increment step is done using add() instead of wrapping_add() and per the wrapping_add() documentation [1] wrapping_add() should be used in situations where the pointer can be incremented outside the allocation. While using add() in these situations is immediately undefined behavior. This will also trigger miri failures when testing code that is building a MatrixView to a row major array and then iterating over it. This commit changes the use of pointer add() to wrapping_add() in the Iterator::next() method to fix this issue.

[1] https://doc.rust-lang.org/std/primitive.pointer.html#method.wrapping_add

When iterating over a view of a row major matrix and finishing a column
during iteration it is possible for the pointer to be incremented
outside the allocation when determining the current column has been
finished. This is fine because the pointer never is actually reading from
outside the allocation, the `inner_end` value used to track we've
finished the column will match on the next call to `next()` and never
actually attempt to read from outside the allocation for the matrix.
However, this increment step is done using `add()` instead of
`wrapping_add()` and per the `wrapping_add()` documentation [1]
`wrapping_add()` should be used in situations where the pointer can be
incremented outside the allocation. While using `add()` in these
situations is immediately undefined behavior. This will also trigger miri
failures when testing code that is building a `MatrixView` to a row
major array and then iterating over it. This commit changes the use of
pointer `add()` to `wrapping_add()` in the `Iterator::next()` method to
fix this issue.

[1] https://doc.rust-lang.org/std/primitive.pointer.html#method.wrapping_add
@Ralith
Copy link
Collaborator

Ralith commented Oct 25, 2025

It's legal and routine for add to result in a pointer that is the first address past the end of the allocation. Is that not what's happening here?

@mtreinish
Copy link
Author

That is what's happening here, but the documentation on pointers that I linked is pretty clear that using add() to advance the pointer outside the allocation is undefined behavior:

Compared to add, this method basically delays the requirement of staying within the same allocation: add is immediate Undefined Behavior when crossing object boundaries; wrapping_add produces a pointer but still leads to Undefined Behavior if a pointer is dereferenced when it is out-of-bounds of the object it is attached to

Additionally in the safety section in the add method's documentation says:

If the computed offset is non-zero, then self must be derived from a pointer to some allocation, and the entire memory range between self and the result must be in bounds of that allocation. In particular, this range must not “wrap around” the edge of the address space.

If you run iter() under miri it will error on the add call here and flag this as undefined behavior. wrapping_add() is the correct call to make if we need to increment the pointer outside the allocation like this.

@Ralith
Copy link
Collaborator

Ralith commented Oct 26, 2025

Read the wording you quoted more carefully:

the entire memory range between self and the result must be in bounds of that allocation

This is true when the result is the first address past the end of the allocation.

mtreinish added a commit to mtreinish/qiskit-core that referenced this pull request Oct 27, 2025
This commit adds a skip on the
test_transpose_faer_to_nalgebra_conversion test when running under miri.
Miri is flagging undefined behavior in the nalgebra code which is
triggered by advancing the iterator's pointer outside the allocation.
This is because the nalgebra code is using the wrong pointer api for doing
the pointer addition. A PR has been proposed in dimforge/nalgebra#1562 to
fix this, but until that is merged and in a release Qiskit is using we
will need to skip miri on this test.
@mtreinish
Copy link
Author

There are two reasons that you can know this is needed in this case. The first is miri is flagging it as undefined behavior when you run iteration on a row major array under miri. It is explicitly flagging that the pointer addition as going out of bounds and it being undefined behavior. The second is you can see how the pointer is being used in the current iteration code. We check inner_end and ptr in the code like:

if self.ptr == self.inner_end

and self.inner_end is set with:

// This might go past the end of the allocation,
// depending on the value of 'size'. We use
// `wrapping_offset` to avoid UB
self.inner_end = self.ptr.wrapping_offset(stride);

if we need to use wrapping_offset() to avoid UB on one side, we also need to use wrapping_add() if the values can be equal.

But also I think what you're potentially missing here is when I said it's row major not column major. So one address at this point in the code is the row stride size - 1 not just size of T. Depending on the shape of the array and the underlying allocation size you can see how it will go out of bounds.

github-merge-queue bot pushed a commit to Qiskit/qiskit that referenced this pull request Oct 29, 2025
* Upgrade faer to the latest version

This commit updates faer to the latest version 0.23.1. This also
requires an update to faer-ext as the two crates are tightly coupled.
The faer-ext version bump does depend on the latest nalgebra release
when the nalgebra conversion feature is enabled. However, the latest
nalgebra release requires a newer rust version than our MSRV and we
can't upgrade to it. To workaround this issue, this commit adds
conversion functions to convert between the types by reference with no
copies. For the faer->nalgebra this is basically just an inlining of
what faer-ext does, but for nalgebra->faer it goes through ndarray
(since we already have that).

* Bump to newer bugfix

* Bump private-gemm-x86

* Bump private-gemm-x86 again

* Update crates/synthesis/src/linalg/mod.rs

* Add explicit lifetime parameters to function signature

* Update safety comment for nalgebra_to_faer

* Update stride type conversion panic message

* Run cargo fmt

* Fix clippy failures too

* Add tests and docs for faer conversion function

There are some caveats around using the faer conversion function around
how nalgebra expects the memory layout to be setup. This documents them
to make it clear there is some care needed with the function.
Additionally, this commit adds some test coverage to the function so that
we are directly covering it and potentially running it under miri (I'm
not actually sure if this will work under miri, so we'll find out in CI
and go from there).

* Test something besides an identity matrix

* Skip miri on test_transpose_faer_to_nalgebra_conversion

This commit adds a skip on the
test_transpose_faer_to_nalgebra_conversion test when running under miri.
Miri is flagging undefined behavior in the nalgebra code which is
triggered by advancing the iterator's pointer outside the allocation.
This is because the nalgebra code is using the wrong pointer api for doing
the pointer addition. A PR has been proposed in dimforge/nalgebra#1562 to
fix this, but until that is merged and in a release Qiskit is using we
will need to skip miri on this test.
raynelfss pushed a commit to raynelfss/qiskit that referenced this pull request Oct 31, 2025
* Upgrade faer to the latest version

This commit updates faer to the latest version 0.23.1. This also
requires an update to faer-ext as the two crates are tightly coupled.
The faer-ext version bump does depend on the latest nalgebra release
when the nalgebra conversion feature is enabled. However, the latest
nalgebra release requires a newer rust version than our MSRV and we
can't upgrade to it. To workaround this issue, this commit adds
conversion functions to convert between the types by reference with no
copies. For the faer->nalgebra this is basically just an inlining of
what faer-ext does, but for nalgebra->faer it goes through ndarray
(since we already have that).

* Bump to newer bugfix

* Bump private-gemm-x86

* Bump private-gemm-x86 again

* Update crates/synthesis/src/linalg/mod.rs

* Add explicit lifetime parameters to function signature

* Update safety comment for nalgebra_to_faer

* Update stride type conversion panic message

* Run cargo fmt

* Fix clippy failures too

* Add tests and docs for faer conversion function

There are some caveats around using the faer conversion function around
how nalgebra expects the memory layout to be setup. This documents them
to make it clear there is some care needed with the function.
Additionally, this commit adds some test coverage to the function so that
we are directly covering it and potentially running it under miri (I'm
not actually sure if this will work under miri, so we'll find out in CI
and go from there).

* Test something besides an identity matrix

* Skip miri on test_transpose_faer_to_nalgebra_conversion

This commit adds a skip on the
test_transpose_faer_to_nalgebra_conversion test when running under miri.
Miri is flagging undefined behavior in the nalgebra code which is
triggered by advancing the iterator's pointer outside the allocation.
This is because the nalgebra code is using the wrong pointer api for doing
the pointer addition. A PR has been proposed in dimforge/nalgebra#1562 to
fix this, but until that is merged and in a release Qiskit is using we
will need to skip miri on this test.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants