This is the mail archive of the 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]

array bounds, sanitizer, safe programming, and cilk array notation

Hi all,

I am writing numerical code, so I am trying to make the use 
of arrays in C (with gcc) suck a bit less. In general, the long term
goal would be to have either a compile-time warning or the possibility
to get a run-time error if one writes beyond the end of an array as 
specified by its type.

So one example (see below) I looked at is where I pass an array of
too small size to a function, to see how see can be diagnosed. In some
cases, we can get a runtime error with the address sanitizer, but this
is fairly limited (e.g. it does not work when the array is embedded
in a struct) and I also got mixed results when the function
is inlined.

For pointers to arrays with static size one can get an "incompatible
pointer" warning - which is nice. With clang, I also get warning for 
pointers which are declared as array parameters and use the 'static' 
keyword to specify a minimum size. This a diagnostic we are currently

The next step would be to have diagnostics also for the VLA
case if the size is known at compilation time (as in the
example below) and a run-time error when it is not (maybe 
with the undefined behaviour sanitizer?).

If we have the later, I think this might also help with safer 
programming in C, because one would get either a compile time or 
runtime error when I passing a buffer which is too small to
a function. For example, snprintf could have a prototype like

int snprintf(size_t size; char str[static size], size_t size, 
  const char *format, ...);

That VLAs essentially provide the bounded pointer type C is
missing has been pointed out before, e.g. there was a proposal
by John Nagle, although he proposed rather intrusive language
changes (e.g. adding references to C) which are not necessary
in my opinion:

Finally, what is missing is a way to diagnose problems inside
the called functions. -Warray-bounds=2 (with my recently
accepted patch) helps with this, but - so far - only for static 

void foo(int (*x)[4])
	(*x)[4] = 5;	// warning

It would be nice to also have these warning and runtime errors
(with the undefined behaviour sanitizer) for VLAs. 

Finally, I think we should have corresponding warning also
for pointers which are declared as array parameters:

void foo2(int x[4])
	x[4] = 5;

The later does not currently produce a warning, because x
is converted to a pointer and the length is ignored. 

If it is not possible to have warning here for compatibility
reasons, one possibility is to have an extension similar to 
'static' which makes 'x' a proper array in the callee, e.g. 
something like:

void foo2(int x[array 4])
	// x is now of type int[4] and not int*

	x[4] = 5; // error

The semantics would be that the array is still passed as
a pointer but the type of x would be int[4]. Because it
immediately decays into a pointer when used, no code generation
changes would be required (except maybe when looking at the
type with sizeof and _Generic).

Another reason I like this is because Cilk array notation
currently requires the length to be specified for 'x' because
it is a pointer and not an array. If x would be an array,
something like this would work:

void foo2(int x[array 4])
	x[:] = 1;

In fact, the documentation for Cilk as such examples
(without the array keyword), and I guess this works on the
intel compiler but not on gcc.

I am willing to spend some (limited) time on all of this, but
I thought I ask for comments first. I appreciate any feedback,
suggestions, and help!


// file 1
extern void bar(int x[static 5])
	for (int i = 0; i < 5; i++)
		x[i] = 1;

extern void bar2(int (*x)[5])
	for (int i = 0; i < 5; i++)
		(*x)[i] = 1;

// file 2

#include <stdio.h>

extern void bar(int x[static 5]);
extern void bar2(int (*x)[5]);

int main()
	int x[4] = { 0 };
	bar(x);		// warning only with clang (found by asan)
	bar2(&x);	// warning (found by asan)
	int c = 4;
	int y[c];
	for (int i = 0; i < c; i++)
		y[i] = 0;

	bar(y);		// not diagnosed (found by asan)
	bar2(&y);	// not diagnosed (found by asan)

	struct foo {
		int z[4];
		int bar;
	} zz = { { 0 }, 0 };

	bar(zz.z);	// warning only with clang
	bar2(&zz.z);	// warning

	printf("%d %d %d %d\n", x[0], x[1], x[2], x[3]);
	printf("%d %d %d %d\n", y[0], y[1], y[2], y[3]);
	printf("%d %d %d %d\n", zz.z[0], zz.z[1], zz.z[2], zz.z[3]);

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