3

Is it safe to call std::memset(pointer, ch, count) with invalid pointer (e.g., nullptr or junk) when count equals 0?

Shawn
  • 47,241
  • 3
  • 26
  • 60
user2052436
  • 4,321
  • 1
  • 25
  • 46
  • It does fall under the as-if rule (https://en.cppreference.com/w/cpp/string/byte/memset). So it seems to be safe to do so. But I can't find explicit confirmation in the standard – Pepijn Kramer Nov 09 '22 at 16:28
  • [C](https://en.cppreference.com/w/c/string/byte/memset) says UB, [C++](https://en.cppreference.com/w/cpp/string/byte/memset) doesn't so I'm not sure – NathanOliver Nov 09 '22 at 16:29
  • I believe the ISO C++ standard refers to the ISO C standard regarding the functions from the C standard library. Therefore, it may be more appropriate to make this a C question than a C++ question, unless a specific C++ rule is relevant here. – Andreas Wenzel Nov 09 '22 at 16:29
  • No, and this has been covered multiple times at Cppcon (don't have the bandwidth right now, but I think https://www.youtube.com/watch?v=yG1OZ69H_-o or https://www.youtube.com/watch?v=g7entxbQOCc will cover it). You're not allowed to pass NULL as either the source or destination, and compilers are free to optimize accordingly. See also: https://stackoverflow.com/questions/5243012/is-it-guaranteed-to-be-safe-to-perform-memcpy0-0-0 – Stephen Newell Nov 09 '22 at 16:56
  • Clang does gives a warning about size 0, MSVC/gcc do not : https://godbolt.org/z/3TzKfGT16. – Pepijn Kramer Nov 09 '22 at 17:07
  • Based on the comments, the TL;DR is: **No**, it is _not_ safe to do this. – Craig Estey Nov 09 '22 at 17:22
  • https://stackoverflow.com/q/8597034/1918193 – Marc Glisse Nov 09 '22 at 18:52

1 Answers1

6

No, that causes undefined behavior. For example:

void* p = get_address(); // may return null
size_t sz = get_size();  // zero if previous returned null

memset(p, 0, sz); // Compiler may assume that p is not null

if (p) { // this null-check can be omitted since we "know" p is not null
  foo(p);
}

And indeed, if you look at the code generated by GCC:

main:
        push    rbx
        call    get_address()
        mov     rbx, rax
        call    get_size()
        mov     rdi, rbx
        xor     esi, esi
        mov     rdx, rax
        call    memset
        mov     rdi, rbx
        call    foo(void*) ; <-- unconditional call
        xor     eax, eax
        pop     rbx
        ret

You can see that the "if" branch is omitted.

Aykhan Hagverdili
  • 28,141
  • 6
  • 41
  • 93