public inbox for libstdc++@gcc.gnu.org
 help / color / mirror / Atom feed
From: Jonathan Wakely <jwakely.gcc@gmail.com>
To: tika <tikabass@yahoo.fr>
Cc: "libstdc++" <libstdc++@gcc.gnu.org>
Subject: Re: Questions about std::string::operator=(char)
Date: Sat, 27 Mar 2021 10:35:10 +0000	[thread overview]
Message-ID: <CAH6eHdSgs3ioWmeUj6Wk_+-NzTL-r+kKGmQhX4cFaFWpm8n=Lw@mail.gmail.com> (raw)
In-Reply-To: <7eeb24bf-58cc-5316-807e-64434ecf4b83@yahoo.fr>

On Sat, 27 Mar 2021 at 02:26, tika via Libstdc++ <libstdc++@gcc.gnu.org> wrote:
>
>
> Synopsys:?? assigning a char to a std::string makes a string 1 char long,
> but what happens if that char is zero??? And showing how easily can this
> UB pass undetected through unit testing...

This is not UB.

>
> The story:?? I created a stupid bug, that my unit testing did not
> catch....?? And ended up spending more more time debugging the test than
> fixing my bug.
>
> The bug itself is kind of boring...?? Using the return value of int
> snd_get_name(int, char*) C function from the alsa library, and putting
> an int into a string. That's what I think could be a rather common mistake.
>
> Here's my piece of code, the various tests that could be applied (and
> most fail to detect the bug in my code).?? It turns out that assigning a
> null character to a std::string breaks a lot iof invariants we usually
> depend on.?? All of gcc 10.2.1 (with libstdc++-10.2.1-9), clang 11.0.1
> and icc 2021.1.2 fail to warn of the mistake with -Wall, for gcc at
> least, that is both both on my system (fedora) and on godbolt.?? MSVC
> does a lot better, with both warnings and different outcome, with str =
> 0 (correctly ?) resulting in an empty string of length zero.

Are you sure you tested the same thing?

> I know it is UB,

It's not.

The std::string::operator=(char) overload gets used to assign a single
char to the string, just as though you'd one str.assign("\0", 1).

but I'm pretty sure the fact that this can go
> undetected so easily by the most common unit testing methods makes it an
> interseting one.?? Is it a feature? Is it something that is part of the
> standard's private UB collection? What does the standard say about
> expected invariants such as s == s.c_str() ? And about the contract of
> std::basic_string::operator(Char) ?

There is no contract. You are allowed to put char(0) into a std::string.

This is not undefined, and is working as required by the standard.

> So, here's some code that show the issue and how stcky it is... You can
> also find it here: https://godbolt.org/z/cTWGWohv3
> <https://godbolt.org/z/cTWGWohv3>
>
> #include<string>
> #include<cstring>
> #include<iostream>
>
> // -------------------------------------------------------------------------
> // simulating what is the C driver FYI, it's the alsa sound driver,
> // but that's not much relevant
>
> char*some_text =nullptr;
>
> voidinit_text()
> {
>  ?????? some_text =strdup("some text");
> }
>
> intget_card_name(int,char** x)
> {
> *x =some_text;
> return0;
> }
>
> // end 'driver' code
> // -------------------------------------------------------------------------
>
> intmain()
> {
>  ?????? init_text();
>
>  ?????? inti =0;
> char*name;
>  ?????? std::string s;
>
> // my original bug... &"??! happens.
>  ?????? s =get_card_name(i,&name);
>
> // Should have read:
> // if (!get_card_name(i, &name))
> // s = name;
>
> // simulating EXPECT_FALSE(s.empty());
>  ?????? std::cout <<"s.empty() : "<<s.empty()<<'\n';
>
> // simulating EXPECT_NE(s.length(), 0);
>  ?????? std::cout <<"s.length() : "<<s.length()<<'\n';
>
> // simulating EXPECT_NE(s, "");
>  ?????? std::cout <<"(s == \"\") : "<<(s =="")<<'\n';
>
> // simulating EXPECT_EQ(s, s.c_str());
>  ?????? std::cout <<"(s == s.c_str()): "<<(s ==s.c_str())<<" <- bug
> detected!\n";

This is not a bug. The std::string "\0" is not equal to the const
char* "\0" because the std::string has length 1, but the length of the
const char* is 0.

> // checking for transmisssion by copy
>  ?????? std::string t =s;
>  ?????? std::cout <<"t == s : "<<(t ==s)<<" <- transmission of odd
> behaviour by copy\n";
>
> // a trace to check contents:
>  ?????? std::cout <<"s : \""<<s <<"\"\n";
>
>
> if(!s.empty())
>  ?????? std::cout <<"int(s.front()) : "<<(int)s.front()<<'\n';
>
>  ?????? free(some_text);
> returns.length();
> }
>
>

  reply	other threads:[~2021-03-27 10:35 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <7eeb24bf-58cc-5316-807e-64434ecf4b83.ref@yahoo.fr>
2021-03-27  2:25 ` tika
2021-03-27 10:35   ` Jonathan Wakely [this message]
2021-03-27 10:37     ` Jonathan Wakely
2021-03-27 14:58     ` tika
2021-03-27 15:32       ` Jonathan Wakely

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CAH6eHdSgs3ioWmeUj6Wk_+-NzTL-r+kKGmQhX4cFaFWpm8n=Lw@mail.gmail.com' \
    --to=jwakely.gcc@gmail.com \
    --cc=libstdc++@gcc.gnu.org \
    --cc=tikabass@yahoo.fr \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).