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
span<T> constructors allow unsafe conversions #313
Comments
Just wanted to say that I also consider both points very serious bugs that need to be addressed as soon as possible (in particular before someone starts relying on that part of the gsl in production code). |
@MikeGitb regarding the |
@neilmacintosh: I believe so and that is exactly what I've done in the past. I was just in the process of migrating a smaller project from my own ArrayView / ByteType to the If you think that this is the right way to go I can offer a PR. The frustrating thing is that the enum version is no doubt the far superior one and even with gcc and clang it probably works 99.9% of the time (e.g. I didn't find a realistic example involving Another option would probably be to pass the no-strict-aliasing flag that should prevent such "optimizations". However, I can't gauge if that is a) always sufficient and b) acceptable for other projects that want to use the gsl. |
@MikeGitb I think the right fix here is probably to use a |
@neilmacintosh Any thoughts about the constructor-related issues? |
@bogiord Yep, I started taking a look on Friday. I'll update the issue when I next get a chance. |
@bogiord thanks for identifying the issues with conversions here. I have to say this thread has made me laugh a little, though...there's been talk of explosions! I usually think of my job in software as rather boring. I had no idea things could get so dramatic with some overly permissive SFINAE! I've corrected the logic inside I've just committed some changes that mean this should now be fixed. |
Oh, no! You... laughed? I was hoping you'd be terrified by the prospect of undefined behaviour making your computer explode. Damn! I guess I should have written something else than "explosive cake". :-D The patch looks good, but it doesn't address the first point - construction from And looking at the code a second time I've just realized that the constructors from On the other hand, the constructor from built-in array doesn't allow qualification conversions between element types (there's a related active Standard issue - CWG2018). I think the restrictions should be uniform across all these constructors. Some more nitpicking:
|
@bogiord ;-D Yes, I consciously separated out the fix for the first point and left it for the next round of change, because I want to change some of the other aspects of construction from Container first. As Vicente has pointed out, there are better ways available (at least on some platforms) to identify contiguous sequence containers. After that, I will revise the Container, array and pointer-based conversion ctors to all add consistent conversion restrictions in one pass. Good catch of the redundant |
@neilmacintosh: Sorry for not coming back to this earlier. I was traveling and didn't have access to a proper development environment. Anyway, I created a PR that implements both solutions and maybe someone with more experience can have a closer look at this issue. |
* Add test to demonstrate byte aliasing problem on g++ and clang++ * Add note about no-strict-aliasing flag in README * Activate aliasing unit test and use -fno-strict-aliasing flag
std::is_convertible<typename Container::pointer, pointer>
is the wrong test to constrain the constructor from a container. Example:The same problem is present if we construct the
span
fromstd::array<D, 3>
, because the constructor fromstd::array<ArrayElementType, N>&
is not constrained and thenstorage_type
usespointer
to store the pointer to the first element, allowing derived-to-base pointer conversions. (By the way, the default template argument forArrayElementType
is pointless, it will always be deduced.)Now, conversions between
span
s:is_allowed_element_type_conversion
is way too permissive.is_allowed_integral_conversion
is the wrong test in this context. You can't look at an array ofint
s through aspan<long>
, even ifint
andlong
have the same internal representation; this is breaking aliasing rules and is undefined behaviour according to [basic.lval]/8 in the Standard (N4606, the current draft).is_allowed_pointer_conversion
is also wrong. Just because a pointer type is convertible to another it doesn't mean that an array of the former is convertible to an array of the latter.And on top of all this, the
reinterpret_cast<pointer>(other.data())
is the icing on the explosive cake. One example involving pointers:On the other hand,
std::is_same<From, std::remove_cv_t<To>>
disallows conversions likevolatile T
toconst volatile T
, which are safe (it also disallows identical cv-qualified types, likeconst T
toconst T
, but those cases are handled by the copy and move constructors, so don't use this test).Really, the only conversions that should be allowed on
span
elements are qualification conversions as defined in [conv.qual] in the Standard. The previous version of the convertibility tests, which used pointers to arrays of unknown bound of the element types, were perfect for this purpose.Also, I'm not sure why
is_allowed_element_type_conversion
allows conversions tobyte
.span
's constructors don't seem to be able to handle such conversions properly (adjusting the extent); as far as I can tell, they're supposed to be done throughas_bytes
oras_writeable_bytes
, which don't use conversions betweenspan
s internally.Note that the current definition of
gsl::byte
doesn't allow viewing an array ofT
through aspan<byte>
according to the Standard. This would be allowed by the aliasing rules ifbyte
were atypedef
forchar
orunsigned char
. Hopefully the proposal forstd::byte
will be accepted, but until thenenum class byte
is just asking for trouble in a library that aims to work safely across compilers. Example:In MSVC it always prints
14
. In Clang and GCC it prints7
with optimizations enabled and14
without optimizations - classic undefined behaviour; sample here.The same code but with
using byte = unsigned char;
always prints14
in all compilers; sample here.The text was updated successfully, but these errors were encountered: