Skip to content

Commit

Permalink
[fix] do not call memcpy() if the length is null
Browse files Browse the repository at this point in the history
  • Loading branch information
biojppm committed Oct 1, 2022
1 parent dc7b1ef commit 1973d49
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 11 deletions.
2 changes: 2 additions & 0 deletions changelog/current.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

### Fixes

- `substr`, `to_chars()`, charconv: ensure `memcpy` is not called when the length is zero. Doing this is UB and enabled the optimizer to wreak havoc in the branches of calling code. See comments at [rapidyaml#264](https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637) for an example and fix. See [Raymond Chen's blog](https://devblogs.microsoft.com/oldnewthing/20140627-00/?p=633) for an explanation.
- `atof()` and `atod()` ([PR#88](https://github.com/biojppm/c4core/pull/88)):
- Always use the fastest implementation available: `std::from_chars()` if available (C++17 or higher standard, with later compilers), `fast_float::from_chars()` otherwise. On Visual Studio, `fast_float::from_chars()` is preferred over `std::from_chars()`.
- If `std::from_chars()` is not available and `C4CORE_NO_FAST_FLOAT` is defined, then the fallback is based on `sscanf()`.
Expand All @@ -50,3 +51,4 @@
- @mlondono74
- @musicinmybrain
- @pkubaj
- @Gei0r
44 changes: 38 additions & 6 deletions src/c4/charconv.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2290,7 +2290,15 @@ inline size_t to_chars(substr buf, csubstr v) noexcept
{
C4_ASSERT(!buf.overlaps(v));
size_t len = buf.len < v.len ? buf.len : v.len;
memcpy(buf.str, v.str, len);
// calling memcpy with null strings is undefined behavior
// and will wreak havoc in calling code's branches.
// see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
if(len)
{
C4_ASSERT(buf.str != nullptr);
C4_ASSERT(v.str != nullptr);
memcpy(buf.str, v.str, len);
}
return v.len;
}

Expand All @@ -2317,20 +2325,36 @@ inline size_t to_chars(substr buf, substr v) noexcept
{
C4_ASSERT(!buf.overlaps(v));
size_t len = buf.len < v.len ? buf.len : v.len;
memcpy(buf.str, v.str, len);
// calling memcpy with null strings is undefined behavior
// and will wreak havoc in calling code's branches.
// see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
if(len)
{
C4_ASSERT(buf.str != nullptr);
C4_ASSERT(v.str != nullptr);
memcpy(buf.str, v.str, len);
}
return v.len;
}

inline bool from_chars(csubstr buf, substr * C4_RESTRICT v) noexcept
{
C4_ASSERT(!buf.overlaps(*v));
if(buf.len <= v->len)
// is the destination buffer wide enough?
if(v->len >= buf.len)
{
memcpy(v->str, buf.str, buf.len);
// calling memcpy with null strings is undefined behavior
// and will wreak havoc in calling code's branches.
// see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
if(buf.len)
{
C4_ASSERT(buf.str != nullptr);
C4_ASSERT(v->str != nullptr);
memcpy(v->str, buf.str, buf.len);
}
v->len = buf.len;
return true;
}
memcpy(v->str, buf.str, v->len);
return false;
}

Expand All @@ -2341,7 +2365,15 @@ inline size_t from_chars_first(csubstr buf, substr * C4_RESTRICT v) noexcept
if(C4_UNLIKELY(trimmed.len == 0))
return csubstr::npos;
size_t len = trimmed.len > v->len ? v->len : trimmed.len;
memcpy(v->str, trimmed.str, len);
// calling memcpy with null strings is undefined behavior
// and will wreak havoc in calling code's branches.
// see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
if(len)
{
C4_ASSERT(buf.str != nullptr);
C4_ASSERT(v->str != nullptr);
memcpy(v->str, trimmed.str, len);
}
if(C4_UNLIKELY(trimmed.len > v->len))
return csubstr::npos;
return static_cast<size_t>(trimmed.end() - buf.begin());
Expand Down
16 changes: 14 additions & 2 deletions src/c4/std/vector.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,13 @@ inline size_t to_chars(c4::substr buf, std::vector<char, Alloc> const& s)
{
C4_ASSERT(!buf.overlaps(to_csubstr(s)));
size_t len = buf.len < s.size() ? buf.len : s.size();
memcpy(buf.str, s.data(), len);
// calling memcpy with null strings is undefined behavior
// and will wreak havoc in calling code's branches.
// see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
if(len > 0)
{
memcpy(buf.str, s.data(), len);
}
return s.size(); // return the number of needed chars
}

Expand All @@ -67,7 +73,13 @@ inline bool from_chars(c4::csubstr buf, std::vector<char, Alloc> * s)
{
s->resize(buf.len);
C4_ASSERT(!buf.overlaps(to_csubstr(*s)));
memcpy(&(*s)[0], buf.str, buf.len);
// calling memcpy with null strings is undefined behavior
// and will wreak havoc in calling code's branches.
// see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
if(buf.len > 0)
{
memcpy(&(*s)[0], buf.str, buf.len);
}
return true;
}

Expand Down
10 changes: 7 additions & 3 deletions src/c4/substr.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ struct C4CORE_EXPORT basic_substring
if(C4_LIKELY(str && that))
{
{
size_t min = len < sz ? len : sz;
const size_t min = len < sz ? len : sz;
for(size_t i = 0; i < min; ++i)
if(str[i] != that[i])
return str[i] < that[i] ? -1 : 1;
Expand Down Expand Up @@ -1951,7 +1951,11 @@ struct C4CORE_EXPORT basic_substring
num = num != npos ? num : len - ifirst;
num = num < that.len ? num : that.len;
C4_ASSERT(ifirst + num >= 0 && ifirst + num <= len);
memcpy(str + sizeof(C) * ifirst, that.str, sizeof(C) * num);
// calling memcpy with null strings is undefined behavior
// and will wreak havoc in calling code's branches.
// see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
if(num)
memcpy(str + sizeof(C) * ifirst, that.str, sizeof(C) * num);
}

public:
Expand Down Expand Up @@ -2069,7 +2073,7 @@ struct C4CORE_EXPORT basic_substring
{ \
C4_ASSERT((last) >= (first)); \
size_t num = static_cast<size_t>((last) - (first)); \
if(sz + num <= dst.len) \
if(num > 0 && sz + num <= dst.len) \
{ \
memcpy(dst.str + sz, first, num * sizeof(C)); \
} \
Expand Down

0 comments on commit 1973d49

Please sign in to comment.