This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH] avoid calling memset et al. with excessively large sizes (PR 79095)


On Tue, Jan 17, 2017 at 10:59:43PM -0700, Jeff Law wrote:
> > I agree that breaking those applications would be bad.  It could
> > be dealt with by adding an option to let them disable the insertion
> > of the trap.  With the warning, programmers would get a heads up
> > that their (already dubious) code won't work otherwise.  I don't
> > think it's a necessary or wise to have the default mode be the most
> > permissive (and most dangerous) and expect users to tweak options
> > to make it safe.  Rather, I would argue that it should be the other
> > way around.  Make the default safe and strict and let the advanced
> > users who know how deal with the risks tweak those options.
> I still come back to the assertion that changing this loop to a mem* is
> fundamentally the wrong thing to do as it changes something that has well
> defined semantics to something that is invalid.
> 
> Thus the transformation into a mem* call is invalid.

The mem* call is as valid as the loop, it will work exactly the same.
If you have say on 32-bit target
#include <stdlib.h>

int
main ()
{
  char *p = malloc (3U * 1024 * 1024 * 1024);
  if (p == NULL)
    return 0;
  size_t i;
  for (i = 0; i < 3U * 1024 * 1024 * 1024; i++)
    p[i] = 6;
  use (p);
  return 0;
}

then the loop does the same thing as will memset (p, 6, 3U * 1024 * 1024 * 1024);
do.  On such large objects some operations may not work properly, e.g.
&p[i] - &p[0] might be negative etc., but that is not something the above
loop does or memset will do internally.  If the loop doesn't use just 3/4 of
the address space, but much more, e.g. more than whole address space minus
one page, which is what happens in the testcase, it is indeed quite sure it
will crash if invoked, but the problem with the warning is the same with
many other late warnings or warnings excessively using VRP etc.
If we are given
void
foo (char *p, size_t len)
{
  memset (p, '\0', len);
}
then it also can invoke UB if somebody calls it with a huge len
(~(size_t)0), but we rightly don't want to warn because we can't prove it
will be called with that.  If some unrelated or related test somewhere else
makes the value range smaller on some path or turns it constant on some
path, the warning does warn, even when there is a sometimes low, sometimes
very high possibility the warning is on dead code.
Which is why IMHO it is a very bad idea to enable this warning by default
even without any -W* option, it just has too many false positives.
Turning extra large memcpy (as memcpy can't have overlap in between the
objects, already starting from half of address space is fine; for memset
I'd think only for something like -4096 or something similar that is just
unlikely in hosted environment to be a single object) into a trap or
unreachable might be a nice optimization and allow optimizing away code that
would follow the spot that would always crash.  But with warning above it
the problem is that it is hard to figure out if it is reachable or not.

	Jakub


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]