Skip to content

Conversation

@onitake
Copy link

@onitake onitake commented Dec 25, 2025

Fixes: #20473

This PR replaces the static upcasting in the helper macros _READ and _WRITE with a memcpy call.

Some of the functions in librt_internal.c use these macros to write 16 or 32 bit integer values, while the data->ptr is byte-aligned and may point to a memory address that is not divisible by 2 or 4.
This leads to a crash (with SIGBUS) on CPU architectures that have strict memory alignment, or possibly a performance penalty on others.

By replacing the direct assignment with memcpy, the compiler will figure out the most optimal method to access the unaligned memory.
I analyzed the resulting machine code generated by gcc 15 on amd64 and sparc64: The memcpy call was in fact optimized away, with instructions that avoided the crash on sparc64. On amd64, it made no difference performance-wise (compared to the original code).

#define _READ(result, data, type) \
do { \
*(result) = *(type *)(((ReadBufferObject *)data)->ptr); \
memcpy((void *) result, ((ReadBufferObject *)data)->ptr, sizeof(type)); \

Choose a reason for hiding this comment

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

The spacing between the cast and variable is inconsistent:

(void *) result vs (ReadBufferObject *)data. Since the previous code uses no spacing here, I suggest changing it to (void *)result.

do { \
*(type *)(((WriteBufferObject *)data)->ptr) = v; \
type temp = v; \
memcpy(((WriteBufferObject *)data)->ptr, (const void *) &temp, sizeof(type)); \

Choose a reason for hiding this comment

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

Same spacing inconsistency issue here.

#define _WRITE(data, type, v) \
do { \
*(type *)(((WriteBufferObject *)data)->ptr) = v; \
type temp = v; \

Choose a reason for hiding this comment

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

Looking at the usage of _WRITE(), I'm not 100% sure whether the temporary variable is needed, but it's probably safer when dealing with variables passed as a function parameter.

@glaubitz
Copy link

Besides two minor nitpicks with the formatting (spacing), the changes look good to me and I think it's the way to go to avoid unaligned access which can cause performance issues on other architectures besides the Bus error exception on SPARC.

Thanks for fixing this!

@ilevkivskyi
Copy link
Member

As I mentioned in original version of this PR, I think this is a too big change, there are only couple problematic uses of _READ/_WRITE, it is probably better to add a separate pair, say _READ_STRICT/_WRITE_STRICT, for those couple places. Anyway, I will probably leave final decision to @JukkaL

@glaubitz
Copy link

As I mentioned in original version of this PR, I think this is a too big change, there are only couple problematic uses of _READ/_WRITE, it is probably better to add a separate pair, say _READ_STRICT/_WRITE_STRICT, for those couple places. Anyway, I will probably leave final decision to @JukkaL

I don't think it's actually a big change. It just replaces a problematic assignment of type-cast memory with the safer memcpy() which is guaranteed to work properly on all platforms and will just be optimized out by the compiler on platforms where it's safely possible.

@onitake
Copy link
Author

onitake commented Dec 27, 2025

As I mentioned in original version of this PR, I think this is a too big change, there are only couple problematic uses of _READ/_WRITE, it is probably better to add a separate pair, say _READ_STRICT/_WRITE_STRICT, for those couple places. Anyway, I will probably leave final decision to @JukkaL

I don't think this makes a lot of sense, because the two helper functions are internal and not used anywhere else besides this source file, and because the same alignment issues would also appear if they were used anywhere else. It's better to just fix all potential uses of these macros. As mentioned, there is no expected impact on platforms where unaligned memory access doesn't matter, but the memcpy will ensure that the correct instructions are generated where it does.

@thesamesam
Copy link

Also, doing it selectively implies there aren't any aliasing issues here, and it looks like there might be (the PR as-is would fix that).

@onitake
Copy link
Author

onitake commented Dec 27, 2025

@glaubitz Regarding your suggestions: I tried without the temporary variable first, but got an error, because some of the usages simply pass a constant value that can't be used as a reference.
As for whitespace - I'd prefer to wait for feedback from the project owners first, but will gladly adapt if needed.

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.

Unaligned memory access in _write_short_int

4 participants