135 lines
4.1 KiB
Text
135 lines
4.1 KiB
Text
|
|
:experimental:
|
|
|
|
[[sect-Defensive_Coding-C-Other]]
|
|
== Other C-related Topics
|
|
|
|
[[sect-Defensive_Coding-C-Wrapper-Functions]]
|
|
=== Wrapper Functions
|
|
|
|
Some libraries provide wrappers for standard library functions.
|
|
Common cases include allocation functions such as
|
|
`xmalloc` which abort the process on
|
|
allocation failure (instead of returning a
|
|
`NULL` pointer), or alternatives to relatively
|
|
recent library additions such as `snprintf`
|
|
(along with implementations for systems which lack them).
|
|
|
|
In general, such wrappers are a bad idea, particularly if they
|
|
are not implemented as inline functions or preprocessor macros.
|
|
The compiler lacks knowledge of such wrappers outside the
|
|
translation unit which defines them, which means that some
|
|
optimizations and security checks are not performed. Adding
|
|
`__attribute__` annotations to function
|
|
declarations can remedy this to some extent, but these
|
|
annotations have to be maintained carefully for feature parity
|
|
with the standard implementation.
|
|
|
|
At the minimum, you should apply these attributes:
|
|
|
|
* If you wrap function which accepts are GCC-recognized format
|
|
string (for example, a `printf`-style
|
|
function used for logging), you should add a suitable
|
|
`format` attribute, as in <<ex-Defensive_Coding-C-String-Functions-format-Attribute>>.
|
|
|
|
* If you wrap a function which carries a
|
|
`warn_unused_result` attribute and you
|
|
propagate its return value, your wrapper should be declared
|
|
with `warn_unused_result` as well.
|
|
|
|
* Duplicating the buffer length checks based on the
|
|
`__builtin_object_size` GCC builtin is
|
|
desirable if the wrapper processes arrays. (This
|
|
functionality is used by the
|
|
`-D_FORTIFY_SOURCE=2` checks to guard
|
|
against static buffer overflows.) However, designing
|
|
appropriate interfaces and implementing the checks may not
|
|
be entirely straightforward.
|
|
|
|
For other attributes (such as `malloc`),
|
|
careful analysis and comparison with the compiler documentation
|
|
is required to check if propagating the attribute is
|
|
appropriate. Incorrectly applied attributes can result in
|
|
undesired behavioral changes in the compiled code.
|
|
|
|
[[sect-Defensive_Coding-C-Common-Mistakes]]
|
|
=== Common mistakes
|
|
|
|
==== Mistakes in macros
|
|
A macro is a name given to a block of C statements as a pre-processor
|
|
directive. Being a pre-processor the block of code is transformed by
|
|
the compiler before being compiled.
|
|
|
|
A macro starts with the preprocessor directive, #define. It can
|
|
define a single value or any 'substitution', syntactically valid or
|
|
not.
|
|
|
|
A common mistake when working with macros is that programmers treat
|
|
arguments to macros like they would functions. This becomes an issue
|
|
when the argument may be expanded multiple times in a macro.
|
|
|
|
For example:
|
|
|
|
macro-misuse.c
|
|
[source,C]
|
|
----
|
|
#define simple(thing) do { \
|
|
if (thing < 1) { \
|
|
y = thing; \
|
|
} \
|
|
else if (thing > 100) { \
|
|
y = thing * 2 + thing; \
|
|
} \
|
|
else { \
|
|
y = 200; \
|
|
} \
|
|
} while (0)
|
|
|
|
int main(void) {
|
|
int x = 200;
|
|
int y = 0;
|
|
simple(x++);
|
|
|
|
return 0;
|
|
}
|
|
----
|
|
|
|
Each pass through the simple() macro would mean that x could be
|
|
expanded in-place each time 'thing' was mentioned.
|
|
|
|
The 'main' function would be processed and expanded as follows:
|
|
|
|
macro-misuse-post-processing.c
|
|
[source,C]
|
|
----
|
|
|
|
int main(void) {
|
|
int x = 200;
|
|
int y = 0;
|
|
do {
|
|
if ( x++ < 1) {
|
|
y = x++;
|
|
}
|
|
else if (thing > 100) {
|
|
y = x++ * 2 + x++;
|
|
}
|
|
else {
|
|
x = 200;
|
|
}
|
|
} while (0)
|
|
|
|
return 0;
|
|
}
|
|
----
|
|
|
|
Each evaluation of the argument to 'simple' (x++) would be executed
|
|
each time it was referenced.
|
|
|
|
While this may be 'expected' behaviour by the original creator, large
|
|
projects may have programmers who were unaware of how the macro may
|
|
expand and this may introduce unexpected behaviour, especially if the
|
|
value is later used as indexing into an array or able to be
|
|
overflowed.
|
|
|
|
|
|
|