New issue
Advanced search Search tips
Note: Color blocks (like or ) mean that a user may not be available. Tooltip shows the reason.

Issue 698073 link

Starred by 1 user

Issue metadata

Status: Fixed
Owner:
Long OOO (go/where-is-mgiuca)
Closed: Mar 2017
Cc:
Components:
EstimatedDays: ----
NextAction: ----
OS: All
Pri: 1
Type: Bug-Regression



Sign in to add a comment

base::JoinString hits undefined behaviour if the first part is a null StringPiece

Project Member Reported by mgiuca@chromium.org, Mar 2 2017

Issue description

Chrome Version: 58
OS: All

The new overload of base::JoinString(vector<StringPiece>) that was added in r452432 calls the std::string constructor with the first element of the vector like this (paraphrasing):

    std::string result(part.data(), part.size());

It turns out according to [1] this is undefined behaviour if part.data() is null, which is the case for an empty StringPiece. The StringPiece as_string() method null-checks this [2].

On Linux Clang at least, this doesn't crash or exhibit any bad effects. However, we should add a test case for it in case it tickles the constructor on some platform.

This has already been accidentally fixed in r454189 which removes the special case for the first piece. As this is close to branch point, should monitor to make sure the fix is in the M58 branch.

This code isn't used much yet... just share_service_impl.cc and autofill_profile_comparator.cc.

[1] http://en.cppreference.com/w/cpp/string/basic_string/basic_string (see (4)).
[2] https://cs.chromium.org/chromium/src/base/strings/string_piece.h?type=cs&q=as_string&l=248
 
Update: r454189 is in M58 branch so no merge is required.
Ugh, it's really hard to tell whether this is actually undefined or not.

- http://en.cppreference.com/w/cpp/string/basic_string/basic_string (4) says "The behavior is undefined if s does not point at an array of at least count elements of CharT, including the case when s is a null pointer." Suggesting that it is undefined.
- http://www.cplusplus.com/reference/string/basic_string/basic_string/ (5) says "Copies the first n characters from the array of characters pointed by s." Doesn't mention null.
- The C++14 spec http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf (§21.4.2.6) says "Requires: s points to an array of at least n elements of charT." Maddeningly, this doesn't mention whether null is allowed or not (and I'm not familiar enough with the spec to know whether there is a default expectation).
- Finally, the GCC implementation of libc++ *explicitly* checks for null but only if the length > 0:

 	// NB: Not required, but considered best practice.
	if (__gnu_cxx::__is_null_pointer(__beg) && __beg != __end)
	  __throw_logic_error(__N("basic_string::_S_construct null not valid"));

(basic_string.tcc, std::_S_construct)

which suggests that calling std::string(nullptr, 0) is valid and constructs the empty string.
Project Member

Comment 3 by bugdroid1@chromium.org, Mar 6 2017

The following revision refers to this bug:
  https://chromium.googlesource.com/chromium/src.git/+/601ab6b021ca49678a407f4feb66a313318c18aa

commit 601ab6b021ca49678a407f4feb66a313318c18aa
Author: mgiuca <mgiuca@chromium.org>
Date: Mon Mar 06 03:05:23 2017

Added regression test for base::JoinString given a null StringPiece.

This tests against what was previously undefined behaviour (passing a
null pointer to the std::string constructor), introduced in r452432 and
inadvertently fixed in r454189. These tests won't necessarily catch
future undefined behaviour (because the standard library doesn't
actually seem to mind being given a null pointer) but we stand a better
chance of finding it if there are tests.

BUG= 698073 

Review-Url: https://codereview.chromium.org/2733473002
Cr-Commit-Position: refs/heads/master@{#454817}

[modify] https://crrev.com/601ab6b021ca49678a407f4feb66a313318c18aa/base/strings/string_util_unittest.cc

In C++11, this is not allowed. The requirements for basic_string(const charT* s, const Allocator& a = Allocator()); are provided in [string.cons]/9:

Requires: s shall not be a null pointer
Aha, I just looked at C++11 vs C++14 specs:

C++11 §21.4.2.7: "Requires: s shall not be a null pointer and n < npos."
C++14 §21.4.2.6: "Requires: s points to an array of at least n elements of charT."

For now (on C++11) it definitely can't be null. I'm very confused though what they are doing in C++14, whether this changes it to be valid or whether it's still undefined when the pointer is null, but the language has simply changed to become more ambiguous. ???
Here is the working group change discussion:
http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2235

Personally, I don't see the justification for this change. It looks like they weren't trying to change the behaviour, just make it clearer, and it had the opposite effect. I suspect "points to an array" implies non-null but I can't find any definition of the term.

I'm going to back away slowly now.
I think in C++11 it doesn't say that the array has to be as long as the number, even tho if it's not bad things happen. C++14 tried to fix that by saying it needs that many things. Notably an array of 0 elements is not the same thing as nullptr. You can malloc a 0-sized thing and you get a pointer that isn't null as a result. So I don't think C++14 is allowing nullptr.
I agree with danakj@: I think it's not permitted in C++14 either.

The requirements for this overload in C++14 are actually in [string.cons]/8 (it's difficult to read this section because table 66 splits the requirements from the overload):
Requires: s points to an array of at least traits::length(s) + 1 elements of charT

In [char.traits.require]/table 62, the requirements for length are:
X::length(p) yields: the smallest i such that X::eq(p[i], charT()) is true

Which I read to imply that p must be dereferencable.
[Note: This discussion is academic at this point since we don't use C++14 yet, and even if we did we'd probably not want to rely on this. Feel free to drop out.]

#7: I agree with the conclusion (it "feels right"), but I wish I could find a dedicated piece of text somewhere in this spec which says it concretely. Like "a pointer to an array must not be nullptr, even if the array length is zero". (I too used "malloc(0) gives a non-null pointer as a result" as an argument, but that is just a hint; it doesn't strongly imply that nullptr can't be considered an empty array.)

#8: You're reading the wrong section. [string.cons]/8 is about std::string(const char* s) which, as you say, can't be null or strlen() won't work. I'm talking about [string.cons]/6--7 which is about std::string(const char* s, size_t n), where in theory it might be permissible for s to be nullptr when n == 0.

Sign in to add a comment