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

Fix some sources of undefined behavior #1171

Merged
merged 4 commits into from
Jun 11, 2024

Conversation

slipher
Copy link
Member

@slipher slipher commented May 31, 2024

  • Fix unsafely populating vector of unknown size via memset
  • Cut down on memset and memcpy generally. The problem with them is the undefined behavior with null pointers
  • Rewrite Color to avoid UB array indexing style

@@ -1510,7 +1510,7 @@ float VectorDistanceSquared( vec3_t v1, vec3_t v2 )
// *INDENT-OFF*
void MatrixIdentity( matrix_t m )
{
memcpy( m, matrixIdentity, sizeof( matrix_t ) );
MatrixCopy( matrixIdentity, m );
Copy link
Member

@illwieckz illwieckz May 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing I like with memcpy is that compilers usually know the best way to copy and use intrinsics as much as possible, usually even in debug mode, while you need some levels of compiler optimizations to get MatrixCopy vectorized by the compiler.

I wonder if we can have a custom memcpy with an assert it's not null (for non-user provided data), and one with a check otherwise, when std::copy_n is not available.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That UB problem of memcpy is very annoying because memcpy is very efficient and is assumed to do the best for the task…

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, MatrixCopy still does memcpy :) This case is not one of the risky ones as the array is fixed-length. Here I'm really just trying to reduce the number of times memcpy appears in the source.

Also std::copy and friends are expected to shell out to memcpy when applicable (nonzero length, struct is POD or whatever)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, MatrixCopy still does memcpy :)

Ahah, yes, that's true for this one, I'm even the one who did it: d8089d6 😄️

@@ -54,15 +54,15 @@ int trap_CM_MarkFragments( int numPoints, const vec3_t *points, const vec3_t pro

std::vector<std::array<float, 3>> mypoints(numPoints);
std::array<float, 3> myproj;
memcpy((float*)mypoints.data(), points, sizeof(float) * 3 * numPoints);
memcpy(mypoints.data(), points, sizeof(float) * 3 * numPoints);
Copy link
Member

@illwieckz illwieckz May 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one may be a good candidate for a custom memcpy with a check if there is no alternative to memcpy.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. In this case the types are incompatible according to C++ rules, but we know the layout is the same.

@illwieckz
Copy link
Member

Can memcpy_s be of any use for us?

Quoting https://en.cppreference.com/w/c/string/byte/memcpy :

errno_t memcpy_s( void *restrict dest, rsize_t destsz, const void *restrict src, rsize_t count ); (2)

  1. Same as (1), except that the following errors are detected at runtime and cause the entire destination range [dest, dest+destsz) to be zeroed out (if both dest and destsz are valid), as well as call the currently installed constraint handler function:
  • dest or src is a null pointer
  • destsz or count is greater than RSIZE_MAX
  • count is greater than destsz (buffer overflow would occur)
  • the source and the destination objects overlap

The behavior is undefined if the size of the character array pointed to by dest < count <= destsz; in other words, an erroneous value of destsz does not expose the impending buffer overflow.

As with all bounds-checked functions, memcpy_s only guaranteed to be available if __STDC_LIB_EXT1__ is defined by the implementation and if the user defines __STDC_WANT_LIB_EXT1__ to the integer constant 1 before including <string.h>.

@slipher slipher force-pushed the memcpy-friendship-ended branch 2 times, most recently from d97dd30 to 9591fba Compare June 2, 2024 08:51
@slipher
Copy link
Member Author

slipher commented Jun 2, 2024

Can memcpy_s be of any use for us?

I don't think so. The advantage would be that calling it with a null pointer is now a defined behavior: it definitely crashes. But this is outweighed by the negatives. Calculating the destination writable size seems like a pain in the ass and could introduce new crashes because we did it wrong. Also I imagine that compilers would not be able to inline this, so we would lose efficiency.

slipher added 4 commits June 10, 2024 18:36
memcpying into a vector can cause UB if the size is 0. In most cases,
ths more efficient iterator-based constructor can be used.

Fixes Unvanquished/Unvanquished#2682 (the
underlying cause).
Motivated by DaemonEngine#839 (comment)

At least 2 places looked definitely buggy (i.e. can pass null to
memcpy):
- StripColors (one of the StripColors variants was deleted due to the
  bug but there is another!)
- Util::Reader::ReadData (when reading an empty vector)
Doing (&red)[2] via ToArray() is UB
@slipher slipher force-pushed the memcpy-friendship-ended branch from 9591fba to 73e08a2 Compare June 10, 2024 23:39
@slipher slipher merged commit 6959367 into DaemonEngine:master Jun 11, 2024
9 checks passed
@slipher slipher deleted the memcpy-friendship-ended branch June 11, 2024 01:58
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.

3 participants