Skip to content

Make sure memcpy/memmove/memset with size 0 behave correctly #516

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

Open
RalfJung opened this issue May 10, 2024 · 6 comments
Open

Make sure memcpy/memmove/memset with size 0 behave correctly #516

RalfJung opened this issue May 10, 2024 · 6 comments
Labels
good first issue Good for newcomers

Comments

@RalfJung
Copy link
Member

Zero-sized memory accesses are now always permitted, even if the pointer is NULL or dangling (but it must be aligned still). For codegen this means in particular that memcpy/memmove/memset must be lowered to operations that are never UB when the size is 0 (and the pointer is sufficiently aligned). In LLVM that's easy as LLVM's corresponding intrinsics explicitly allow size 0. However, in C, memcpy/memmove/memset with size 0 is UB on NULL (and dangling pointers are impossible to even mention in C), so GCC may use a different semantics for its builtins. For Rust's GCC backend, it's crucial that we use GCC builtins that allow size 0 with any pointer.

@hhamud
Copy link

hhamud commented May 24, 2024

@antoyo Can I try this one?

Also any pointers on how to solve this issue will be helpful

@antoyo
Copy link
Contributor

antoyo commented May 25, 2024

@hhamud: I assigned the issue to you.

You would need to check if the GCC builtins follow the right semantics and if not, adjust the code here so that we follow the right semantics.

@GrigorenkoPV
Copy link

However, in C, memcpy/memmove/memset with size 0 is UB on NULL

There are proposals in progress to allow that: https://www.open-std.org/JTC1/SC22/WG14/www/docs/n3261.pdf

Fingers crossed it gets accepted

@FractalFir
Copy link
Contributor

After a bit of research, I have found an example where GCC optimizes things based on the assumption that the arguments to memcpy are not null.

Here is a sample I found on the internet:

https://godbolt.org/z/aPcr1bfPe

#include <string.h>

int do_thing1();
int do_thing2();

void test(char *dest, const char *src, size_t len) {
    memcpy(dest, src, len);
    if (dest == NULL) {
        // This branch will be removed by GCC due to undefined behavior.
        do_thing1();
    } else {
        do_thing2();
    }
}

Here, the memory copy of dest allows GCC to assume that the pointer is not null, optimizing the branch check away. This is enabled with -O2(more specifically, with -fdelete-null-pointer-checks).

This does not seem to break Rust code today, at least on x86_64(is cg_gcc disabling -fdelete-null-pointer-checks), but there is a slight chance that the ARM bootstrap crashes are caused by zero-sized copies(the debug info is a bit muddy, and it is hard to tell what is going on, exactly).

@antoyo
Copy link
Contributor

antoyo commented Jun 14, 2025

is cg_gcc disabling -fdelete-null-pointer-checks

I don't think we disable that.
Btw, I remember having to fix some issue in the past where GCC would remove null pointer checks.

@RalfJung
Copy link
Member Author

RalfJung commented Jun 14, 2025

From what I heard, C26 will also permit memcpy on NULL (when the size is 0), so it might be best for GCC to just entirely stop doing this optimization.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
good first issue Good for newcomers
Projects
None yet
Development

No branches or pull requests

5 participants