On Fri, Nov 01, 2024 at 02:07:26PM +0100, Jan Hubicka wrote:
> > On Fri, Nov 01, 2024 at 01:34:45PM +0100, Jan Hubicka wrote:
> > > > 
> > > > malloc(SIZE_MAX / 2 + 1) does not succeed.
> > > 
> > > Is malloc required to return NULL for block of half of address size or
> > > it is glibc behavior? Some time ago we was discussing possibility to
> > 
> > I don't see C23 saying that explicitly (though it e.g. says that
> > calloc is guaranteed to return NULL if nmemb * size computation wraps 
> > around),
> > but I think it is derived from the fact that on objects larger than half
> > of the address space one can't (at least on arches where
> > ptrdiff_t/size_t/void * have the same precision) perform correct pointer
> > arithmetics.
> > 
> > So, I think for calloc-like functions we need to be careful.
> > 
> > Or we could just use the ranger and do this only if the size is known
> > from value ranges not to be larger than half of the address space and
> > for calloc-like that there is no overflow?
> 
> I think it can be implemented with some care.  When the propagation hits
> 
> if (malloc_retval != NULL) 
> 
> I can look up the corresponding size parameter of malloc, check with
> ranger if it is possibly too large, mark it live and produce the

Note, ranger has two ways of using it, one is cheap of just checking
global ranges of a SSA_NAME etc., the more expensive is enabling the
ranger and doing the same query with a particular stmt on which to query.

I think e.g. for warnings we use TYPE_MAX_VALUE (ptrdiff_type_node)
as the maximum object size, though I really don't know if we can actually
rely on malloc returning NULL above it or if it is only possible that it
will return NULL in that case regardless of free memory.  In the latter
case, perhaps we'd need to optimize the
  ptr = malloc (sz);
  if (!ptr)
    whatever;
  else
    free (ptr);
into
  if (sz > something)
    {
      ptr = malloc (sz);
      if (!ptr)
        whatever;
      else
        free (ptr);
    }
?
Another question is if people could just use a lame way to ask if malloc
can allocate specific amount of memory by doing allocation and immediately
freeing and just deciding something based on whether it returned NULL or
non-NULL.
Guess even in single-threaded programs there is no guarantee that
  suceeded = 0;
  ptr = malloc (sz);
  if (ptr)
    succeeded = 1;
  free (ptr);
succeeded implies that malloc (sz) (or smaller size than that)
will return non-NULL, but it is very likely.

So, the question is what we actually should optimize by default and what we
shouldn't.
Guess
  ptr = malloc (sz);
  if (ptr)
    free (ptr);
or
  ptr = malloc (sz);
  if (!ptr)
    __builtin_unreachable ();
  free (ptr);
is fine no matter what, but already doing something further like abort
or having other side-effects only done in one case and not the other
might not be (or perhaps could be dependent on some non-default compiler
option to say it is ok).  Stores to ptr pointed object done conditionally
on ptr being non-NULL is fine (given that it will be thrown away and nothing
ought to inspect that).

We already do optimize
  ptr = new char[sz];
  delete[] ptr;
and that might throw for sz >= PTRDIFF_MAX at least on glibc, but I think
for calls from new/delete the standard says it is ok to optimize those away.

Whether it is ok to optimize
  ptr = new (std::nothrow) char[sz];
  if (!ptr)
    failed = 1;
  delete[] ptr;
is another question.

        Jakub

Reply via email to