Re: std::cout breaks after nullptr

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Since there is a suggestion for libstdc++ iostream library about adding
__attribute__((nonnull)), cc libstdc++ list.

On 2019-03-24 19:40 +0000, Jonny Grant wrote:
> On 24/03/2019 13:53, Xi Ruoyao wrote:
> > On 2019-03-24 12:45 +0000, Jonny Grant wrote:
> > > Hello!
> > > 
> > > Is this an issue? G++ doesn't identify problem in this test case doesn't
> > > when compiling or running.
> > > 
> > > "hello" is not displayed, std::cout never works again after nullptr.
> > > 
> > > 
> > > // g++-8 -Wall -Wextra -Wconversion-null -Wno-nonnull-compare
> > > -Wzero-as-null-pointer-constant
> > > -fsanitize=null,returns-nonnull-attribute,signed-integer-
> > > overflow,leak,undefined,address
> > > -o cout cout.cpp
> > > #include <iostream>
> > > int main()
> > > {
> > >       const char * p = nullptr;
> > >       std::cout << p;
> > >       std::cout << "hello";
> > > }
> > 
> > The standard *Requires* p is not a null pointer here (N4618 27.7.3.2.4
> > p3).  So
> > if p is a null pointer, the implementation (GCC and libstdc++) can do
> > anything.
> > 
> > Libstdc++ is friendly.  It does not abort, raise exception, or run
> > /usr/game/starwar, it just sets ios::badbit to cout (ostream:559) so
> > actually
> > you can detect this issue by checking the state of cout.  But do NOT rely on
> > this behavior.  This is really undefined.
> > 
> > Because libstdc++ is so friendly, the sanitizer don't even know this is an
> > undefined behavior.  And, it can not sanitize undefined behaviors in
> > libstdc++.so because it was built w/o -fsanitize= option.
> > 
> > Well, if you want this to be detected at compile time, maybe you can
> > convince
> > Jonathan to add an __attribute__((nonnull(2))) to operator<<(basic_ostream&,
> > const char *).  Then this case can be detected with "-O -Wnonnull".
> > 
> 
> Hello Xi
> 
> Sounds reasonable to add the nonnull attribute. But I'm not an expert, 
> so let's wait for Jonathan's reply!
> 
> Can I ask, for this nonnull attribute, would it also detect for 
> std::cin, std::err, std::clog as well as std::cout ? Worth adding for 
> std::wcin, std::wcout, std::cerr, std::wclog too.

> Are there other places where nullptr would not be valid? There are lots 
> of classes, eg <streambuf>

Yes.  Just searching the Standard with "shall not be null" or something, we can
see several examples.

> BTW, this other test case below using std::cin does cause SEGV. What is 
> better, SEGV or ignore the nullptr and set ios::badbit too?

For istream the Standard doesn't explicitly assert "shall not be null".  But
after all "storing into null" is undefined behavior so the compiler or runtime
library can do anything :).

> Santizer does seem to find this one in libstdc++
> I didn't even need to add -g for syms!
> 
> 
> // g++-8 -O -Wnonnull -Wall -Wextra -Wconversion-null 
> -Wno-nonnull-compare -Wzero-as-null-pointer-constant 
> -fsanitize=null,returns-nonnull-attribute,signed-integer-
> overflow,leak,undefined,address 
> -o cout cout.cpp
> #include <iostream>
> int main()
> {
>      char * p = nullptr;
>      std::cout << "press a key, and then press return\n";
>      std::cin >> p;
> }
> 
> 
> 
> AddressSanitizer:DEADLYSIGNAL
> =================================================================
> ==2534==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 
> (pc 0x7fa4504d40a0 bp 0x000000000000 sp 0x7ffe0c21fd30 T0)
> ==2534==The signal is caused by a WRITE memory access.
> ==2534==Hint: address points to the zero page.
>      #0 0x7fa4504d409f in std::basic_istream<char, 
> std::char_traits<char> >& std::operator>><char, std::char_traits<char> 
>  >(std::basic_istream<char, std::char_traits<char> >&, char*) 
> (/usr/lib/x86_64-linux-gnu/libstdc++.so.6+0xab09f)
>      #1 0x55a36acccba9 in main (/home/jonny/code/cout+0xba9)
>      #2 0x7fa44f339b96 in __libc_start_main 
> (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
>      #3 0x55a36acccad9 in _start (/home/jonny/code/cout+0xad9)
> 
> AddressSanitizer can not provide additional info.
> SUMMARY: AddressSanitizer: SEGV 
> (/usr/lib/x86_64-linux-gnu/libstdc++.so.6+0xab09f) in 
> std::basic_istream<char, std::char_traits<char> >& std::operator>><char, 
> std::char_traits<char> >(std::basic_istream<char, std::char_traits<char> 
>  >&, char*)
> ==2534==ABORTING

In this case there is a SIGSEGV so it can be catched.  But if there is an
undefined behavior in libstdc++ without raising a signal, the sanitizer won't
even know that.

In this case the sanitizer just works as a signal handler.  Even if there is no
-fsanitizer enabled, Glibc catchsegv utility can provide the same info.  I don't
think address sanitizer is only a signal handler.

To make the sanitizer works perfectly it should be enabled at compile time.  For
example, back in GCC 4.x PR 67214 could be detected by the sanitizer.  But in
later GCC versions the code was moved from the headers into libstdc++.so so it
seemed vanished.  But actually the undefind behavior was still there.
-- 
Xi Ruoyao <xry111@xxxxxxxxxxxxxxxx>
School of Aerospace Science and Technology, Xidian University




[Index of Archives]     [Linux C Programming]     [Linux Kernel]     [eCos]     [Fedora Development]     [Fedora Announce]     [Autoconf]     [The DWARVES Debugging Tools]     [Yosemite Campsites]     [Yosemite News]     [Linux GCC]

  Powered by Linux