Commit 37f1372
authored
Prevent view creation with underflowed size (#2927)
# Checklist
- [x] The title and commit message(s) are descriptive.
- [x] Small commits made to fix your PR have been squashed to avoid
history pollution.
- [x] Tests have been added for new features or bug fixes.
- [ ] API of new functions and classes are documented.
# Description
When a slice with conditions such as `start > stop && step > 0` (or vice
versa) is used to create a view (sliced or strided), the resulting array
view can have an enormous size. Accessing its elements then leads to
undefined behavior.
Here is a minimal example:
```cpp
#include <iostream>
#include <xtensor/containers/xarray.hpp>
#include <xtensor/views/xview.hpp>
int main()
{
xt::xarray<int>::shape_type shape = {5};
xt::xarray<int> a(shape);
// v := a[3:0:1]
auto slice = xt::range(3, 0, 1);
auto v = xt::view(a, slice);
// Print the shape of v
for (auto e : v.shape())
{
std::cout << e << " ";
}
std::cout << std::endl;
}
```
The expected output is `0`, but the actual output is
`18446744073709551613`.
The cause of this issue appears to be the size calculation in the
`xstepped_range` constructor in
`xtensor/include/xtensor/views/xslice.hpp`, shown below:
```cpp
template <class T>
inline xstepped_range<T>::xstepped_range(size_type start_val, size_type stop_val, size_type step) noexcept
: m_start(start_val)
, m_size(size_type(0))
, m_step(step)
{
size_type n = stop_val - start_val;
m_size = n / step + (((n < 0) ^ (step > 0)) && (n % step));
}
```
When `start_val` is greater than `stop_val` (or more precisely, when
`stop_val - start_val` has the opposite sign from `step`), `m_size` can
become negative. This subsequently causes the resulting size to
underflow.
One simple way to fix this issue is to clamp `m_size` to a minimum value
of `0`:
```cpp
template <class T>
inline xstepped_range<T>::xstepped_range(size_type start_val, size_type stop_val, size_type step) noexcept
: m_start(start_val)
, m_size(size_type(0))
, m_step(step)
{
size_type n = stop_val - start_val;
m_size = std::max(n / step + (((n < 0) ^ (step > 0)) && (n % step)), size_type(0));
}
```
Note on terminology: > I used the term "underflow" to describe "integer
underflow", the behavior where subtracting from a small unsigned integer
results in a huge value.1 parent 38cd7ec commit 37f1372
2 files changed
Lines changed: 41 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1058 | 1058 | | |
1059 | 1059 | | |
1060 | 1060 | | |
| 1061 | + | |
| 1062 | + | |
| 1063 | + | |
| 1064 | + | |
| 1065 | + | |
1061 | 1066 | | |
1062 | | - | |
| 1067 | + | |
1063 | 1068 | | |
1064 | 1069 | | |
1065 | 1070 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
226 | 226 | | |
227 | 227 | | |
228 | 228 | | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
229 | 264 | | |
230 | 265 | | |
231 | 266 | | |
| |||
0 commit comments