printf behaviour with illegal or malformed format string

Peter Jeremy PeterJeremy at optushome.com.au
Tue Dec 13 01:17:03 PST 2005


On Tue, 2005-Dec-13 18:53:15 +1100, Bruce Evans wrote:
>>>Our first line of defence against this kind of error is compile-time
>>>checking by GCC, but we cannot rely on the string being sane in libc,
>>>we still need to do error checking.
>
>There is also fmtcheck(3).

I'm probably not the only person who hadn't heard of this function
before reading your mail.  I'm not sure it is of much use other than
validating user-supplied format strings (and maybe internationalisation
translations via catgets(3)).

>I think most checking belongs in the compiler and in fmtcheck(3).

fmtcheck(3) can't check that the arguments passed to printf are valid.
All it can do is verify that two arbitrary format strings expect the
same arguments and the documentation states it doesn't even manage that:
     The fmtcheck() function does not understand all of the conversions
     that printf(3) does.
Note that fmtcheck(3) has no way of reporting that neither format
string makes sense - the documentation states that it will always
return one of its arguments.

If a programmer (mistakenly) believes that "%hf" is sensible then
fmtcheck(3) isn't going to help.

>printf() itself cannot detect most types errors without compiler
>support that would basically involve passing it the types of all
>args so that it could call fmtcheck(3).

This is an interesting idea but fmtcheck(3) isn't adequate.  In
particular, consider:
	int	i, j;
	const char	*fmt, *string;
	...
	fmt = "%*.*s %p";
	...
	printf(fmt, i, j, string, string);

Based on the printf() arguments, the compiler can't really do
better than:
	printf(fmtcheck(fmt, "%d %d %s %s"), i, j, string, string);
but fmtcheck(3) doesn't consider that "%*.*s %p" and "%d %d %s %s" are
equivalent.

What could be useful is a function that takes a format string and
a set of argument types and verifies that the argument types match
the format string.  The compiler could then expand the printf() to:
	if (fmtvalidate(fmt, "int;int;char*;char*"))
		printf(fmt, i, j, string, string);
	else
		abort();
(though it's not clear how the compiler should handle struct/union
pointers which might make sense to a user extension).

>>>If the variable is set, a bogus format string will result in abort(2).
>
>This sometimes breaks defined behaviour.

I thought printf(3) documented the behaviour for invalid conversion
specifiers and mofidiers but I can't find it in the man page right now.

-- 
Peter Jeremy


More information about the freebsd-arch mailing list