[C-safe-secure-studygroup] Rule 11.1 - function pointer conversions

Martin Sebor msebor at gmail.com
Wed Nov 29 19:25:05 GMT 2017


Here's some more detail about my concerns with Rule 11.1 that
we talked about today:  Conversions shall not be performed
between a pointer to a function and any other type.

First, by prohibiting conversions from void* the rule outlaws
the use of the POSIX dlsym() API which returns a void* pointing
to the named symbol in a shared library.  The symbol may be
either a function or an object.

http://pubs.opengroup.org/onlinepubs/9699919799/functions/dlsym.html

A subset of safety critical software that runs on POSIX systems
makes use of dynamically loaded libraries (e.g., software that
supports the concept of plugins must).

Second, another (similar) use case for converting between
functions of different types is to support dynamic dispatch:
declare and store a pointer to a function of one of a finite
set of incompatible types to eventually call it through
an expression whose type matches the function.  In POSIX
environments this could be done by storing a void* but, for
better type safety, some software chooses to use a "generic
function pointer," such as a void(*)(void), or void(*)()
(pointer to a function with no prototype), or
void(*)(struct IncompleteType) where struct IncompleteType
is an incomplete type.  This is done so that the pointer
cannot accidentally be used to call a function (it has to
be converted to some other type).

These uses are both not uncommon (relatively speaking) and
a rule that didn't accommodate them could be excessively
restrictive to the point of not being viable.

I think a rule requiring a diagnostic for conversions between
function pointers and object pointers would be appropriate and
useful.

Similarly, a rule that requied a diagnostic for conversions
between function pointers that are ABI-incompatible would
also be helpful.

However, requiring diagnostics for other conversions should be
considered carefully.  For example, it's not clear to me what
would be gained by disallowing a cast from, say, a function
taking a const char*:

   void f (const char*);

to a pointer to a function taking an unqualified char*:

   void (*)(char*);

since calling f() through such a pointer (although undefined
by the language) is valid under all ABIs and, unlike a conversion
in the opposite direction, doesn't make it possible to modify
the argument.  Such casts may be necessary when dealing with
legacy code that's not completely const-correct (declares
functions that do not modify the objects to take non-const
pointers to the objects' types).

Similarly, in software targeting just a single data model (say
just ILP32), converting a function that returns a long:

   long f (void);

to a pointer to a function returning an int:

   int (*) (void);

although undefined by the language, is safe according to the ABI
and there may be valid reasons for doing so (especially in large
code bases of system software).

Martin



More information about the C-safe-secure-studygroup mailing list