3 ms·
Even without array bounds checking, a bit of discipline and smart conventions will go a long way of reducing errors: 1. Define a macro function for retrieving
by augustk 3y ago
Even without array bounds checking, a bit of discipline and smart conventions will go a long way of reducing errors:
1. Define a macro function for retrieving the length of an array:
#define LEN(arr) (sizeof (arr) / sizeof (arr)[0])
2. Don't introduce macro constants for array lengths; hard code the length in the declaration and use LEN to retrieve it. Example:
int a[100];
...
for (i = 0; i < LEN(a); i++) {
...
}
3. Define a macro function for dynamic array allocation:
#define NEW_ARRAY(ptr, n) \
(ptr) = malloc((n) * sizeof (ptr)[0]); \
if ((ptr) == NULL) { \
fprintf(stderr, "Memory allocation failed: %s\n", strerror(errno)); \
exit(EXIT_FAILURE); \
}
4. When you create a function with an array argument, also add an argument for the array length.
5. Use a convention for naming the length of array pointer targets, for instance by adding the suffix `Len'. Example:
int *b, bLen = 100;
...
NEW_ARRAY(b, bLen); /* nice to know that b and bLen belong together */
...
SomeFunction(b, bLen, ...);
...
for (i = 0; i < bLen; i++) {
...
}
6. Define your own safe wrappers around unsafe standard library functions or use someone else's code that does that.
- ChrisRR 3y agoThe issue with 1 is that it only works until you pass an array into a function by pointer, then the macro no longer works. In my experience it's most likely that a function will write past the bounds of a buffer that's been passed as an argument. In that case, make sure the size of array is always included as an argument as you said in 4.
- ollien 3y ago> The issue with 1 is that it only works until you pass an array into a function by pointer, then the macro no longer works. GCC even has a warning for this. Even worse, even if you specify the argument to be "of the type" array, it will actually still decay to a pointer. Basically, this macro will only work if you use it in the same function the array is defined. https://godbolt.org/z/vr4za73qq https://godbolt.org/z/vr4za73qq
- bheadmaster 3y agoOne exception is if you explicitly define argument as array of fixed length. Downside being, obviously, that it will only work with arrays of that particular length.
- uecker 3y agoYou need to pass a pointer to an array: https://godbolt.org/z/jYzY79ac4 https://godbolt.org/z/jYzY79ac4 When passing an array it decays into a pointer and the size is lost. We can also change sizeof to recover it, but there was a proposal for a _Lengthof operator which could work here.
- mzs 3y agosee item 4
- f33d5173 3y agoYou could extend point 1. by making a convention of always declaring pointers to arrays like so: int (*data)[datalen]; This requires you to dereference it once to get an array, then dereference it a second time to get a value. The advantage is that the array value can be used the same as an normal array on the stack, including passing it to the array length macro you describe.
- teo_zero 3y agoIsn't this exactly what the fine article does?
- mtlmtlmtlmtl 3y agoYour allocation macro can lead to heap underflows if the multiplication wraps around. Which can definitely be exploitable. You should either add overflow checking to the macro or even better just use the damn libc api and call calloc. Or if you really insist on avoiding zeroing overhead, there's reallocarray(NULL, ...) if you use a reasonably modern libc.
- mzs 3y agoNice! I don't like how C has null terminated char arrays plays with this. Ideally this would somehow enforce a guard null byte at the end of each array not included in the size.
- nerpderp82 3y ago> bit of discipline