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