bug-ncurses
[Top][All Lists]
Advanced

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

Re: -fsanitize=undefined detects undefined behaviour in signed shift ove


From: Thomas Dickey
Subject: Re: -fsanitize=undefined detects undefined behaviour in signed shift overflow
Date: Sun, 9 May 2021 19:19:34 -0400
User-agent: Mutt/1.10.1 (2018-07-13)

On Sat, May 08, 2021 at 10:35:54PM +0100, Sergei Trofimovich wrote:
> On Sat, May 08, 2021 at 04:31:46PM -0400, Thomas Dickey wrote:
> > On Sat, May 08, 2021 at 08:55:48PM +0100, Sergei Trofimovich wrote:
> > > Hello ncurses maintainers!
> > > 
> > > In search for an unrelated bug I built a few local tools with
> > > `-fsanitize=undefined` gcc option to catch an suspected undefined
> > > behaviour.
> > > 
> > > Among other things ncurses was flagged in ncurses-based applications as:
> > >   ncurses/tinfo/read_entry.c:111:19:
> > >     runtime error: left shift of 255 by 24 places cannot be represented 
> > > in type 'int'
> > > 
> > > which looks like a real (perhaps minor) problem:
> > 
> > I'm aware of the warning, but don't see a way to detect the overflow
> > and not elicit a warning about undefined behavior.
> > 
> > A patch would help discussion.
> > 
> > (earlier this week I noticed a change by someone to eliminate undefined
> > behavior which doesn't produce a compiler warning with/without the change,
> > _and_ the change causes incorrect behavior -- something to keep in mind)
> 
> Aha, I was not able to lookup past discussions. Does ncurses have an
> automatic regression testing suite I could throw my patches at?

no - I pick up warnings by test-builds. Depending on the type of change, I
do test-builds on one or more of my machines, and compare the logs.
Test-builds exercise 5-10 configurations, etc.  I've a few dozen machines,
and limited time...

        https://invisible-island.net/personal/lint-tools.html#proc_builds
 
> I see two possible fixes:
> 
> 1. Add CFLAGS=-fwrapv as part of ncurses' ./configure. I'm not very well
>    versed in autoconf and it's forks to try it out easily.
> 
>    The following does not produce warning and should preserve reasonable
>    overflow semantics:
>      $ ./configure --enable-termcap CFLAGS='-O2 -g -fsanitize=undefined 
> -fwrapv' LDFLAGS='-O2 -g -fsanitize=undefined -fwrapv'

well that tells it to either ignore (or disable runtime checks) for a lot
of things:

https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html

The checks are useful for development, less useful in release code.

> 2. Use unsigned arithmetics (also attached as a separate file).

that hard-codes an assumption that it's using two's complement :-)
 
> --- a/ncurses/tinfo/read_entry.c
> +++ b/ncurses/tinfo/read_entry.c
> @@ -98,28 +98,28 @@ convert_32bits(char *buf, NCURSES_INT2 *Numbers, int 
> count)
>      }
>      return size;
>  }
>  #else
>  static size_t
>  convert_32bits(char *buf, NCURSES_INT2 *Numbers, int count)
>  {
>      int i, j;
>      unsigned char ch;
>      for (i = 0; i < count; i++) {
> -     int value = 0;
> +     unsigned value = 0;
>       for (j = 0; j < SIZEOF_32BITS; ++j) {
>           ch = UChar(*buf++);
> -         value |= (ch << (8 * j));
> +         value |= ((unsigned)ch << (8 * j));
>       }
> -     if (value == -1)
> +     if (value == ~0u)
>           Numbers[i] = ABSENT_NUMERIC;
> -     else if (value == -2)
> +     else if (value == ~0u - 1)
>           Numbers[i] = CANCELLED_NUMERIC;
>       else if (value > MAX_OF_TYPE(NCURSES_INT2))
>           Numbers[i] = MAX_OF_TYPE(NCURSES_INT2);
>       else
>           Numbers[i] = (short) value;
>       TR(TRACE_DATABASE, ("get Numbers[%d]=%d", i, Numbers[i]));
>      }
>      return SIZEOF_SHORT;
>  }
> 
> -- 
> 
>   Sergei

> --- a/ncurses/tinfo/read_entry.c
> +++ b/ncurses/tinfo/read_entry.c
> @@ -98,28 +98,28 @@ convert_32bits(char *buf, NCURSES_INT2 *Numbers, int 
> count)
>      }
>      return size;
>  }
>  #else
>  static size_t
>  convert_32bits(char *buf, NCURSES_INT2 *Numbers, int count)
>  {
>      int i, j;
>      unsigned char ch;
>      for (i = 0; i < count; i++) {
> -     int value = 0;
> +     unsigned value = 0;
>       for (j = 0; j < SIZEOF_32BITS; ++j) {
>           ch = UChar(*buf++);
> -         value |= (ch << (8 * j));
> +         value |= ((unsigned)ch << (8 * j));
>       }
> -     if (value == -1)
> +     if (value == ~0u)
>           Numbers[i] = ABSENT_NUMERIC;
> -     else if (value == -2)
> +     else if (value == ~0u - 1)
>           Numbers[i] = CANCELLED_NUMERIC;
>       else if (value > MAX_OF_TYPE(NCURSES_INT2))
>           Numbers[i] = MAX_OF_TYPE(NCURSES_INT2);
>       else
>           Numbers[i] = (short) value;
>       TR(TRACE_DATABASE, ("get Numbers[%d]=%d", i, Numbers[i]));
>      }
>      return SIZEOF_SHORT;
>  }
>  


-- 
Thomas E. Dickey <dickey@invisible-island.net>
https://invisible-island.net
ftp://ftp.invisible-island.net

Attachment: signature.asc
Description: PGP signature


reply via email to

[Prev in Thread] Current Thread [Next in Thread]