I have a list of numbers and want to give my users the option to enter a printf-sty开发者_如何学Pythonle format string to specify how the numbers should be output.
How can I validate the user-supplied format string against my parameter list? Malformed input should not crash the program, and I want to avoid any format string attacks.
I do not care if the validation handles just the format options specified in POSIX or the compiler specific superset. Is there any library call to do this, or will I have to write it myself?
Clarification: What I need is something like this:
float var1, var2, var3, var4;
// var1 .. var2 are given by the program
const char * userSupplied = getFormatStringFromUser();
if( isValidFormat( userSupplied, "float", "float", "float", "float" ) )
printf( userSupplied, var1, var2, var3, var4 );
else
printf( "The format you gave was invalid!\n" );
In this example, I know that I have four floats. I want to allow any format that only references zero to four floats.
So the following format strings should be allowed by isValidFormat():
- "%f %g %e %.1f"
- "Foo is %g, Bar is %g"
- "Nothing"
While the following should be rejected:
- "%s"
- "Foo is %d"
Do not pass user-inputted strings into printf
. printf
's format string interface is designed for code, not for human input. You will find that if you just make your own format string spec, you will have the freedom to design it towards the users.
Is there a reason you want to impose the completely obfuscated printf
format string spec on your users?
Essentially you are asking help writing something to convert your format string spec to the printf
format string spec. I would instead suggest you write code to construct the printf
format string from the one the user types in. This is safer, and gives you more flexibility. Even if it's more code, it's less hacky.
Writing the code for you is too much work, but I'll give you a good approach. Design regular expressions for the valid format specifiers for each type you need to support, then use them to construct a larger regular expression for the entire format string and see if it matches. For example, a floating point (double
) argument's regex would look something like:
%[+- 0#]*[0-9]*([.][0-9]+)?[aefgAEFG]
And a regex for literal text that could appear anywhere between format specifiers would look something like:
([^%]|%%)*
Make sure when matching the format string you insist that the whole string match the regex (use ^
and $
anchors at the ends) and not just a substring.
There is no standard (POSIX or C) way and I know of no library providing this. So you will have to write your own or do a better search than me. Note that you have to check only those which are useful for your.
If you have pre-defined input parameters, use switch case.
switch ( <variable> ) {
case accetable-value_1:
Code to execute if <variable> == accetable-value_1
break;
case accetable-value_2:
Code to execute if <variable> == accetable-value_2
break;
...
default:
error: This is not a valid value, please enter a valid value
break;
}
In RRDtool I use code like this to check various format patterns.
#include <glib.h>
static int bad_format_check(const char *pattern, char *fmt) {
GError *gerr = NULL;
GRegex *re = g_regex_new(pattern, G_REGEX_EXTENDED, 0, &gerr);
GMatchInfo *mi;
if (gerr != NULL) {
// rrd_set_error("cannot compile regular expression: %s (%s)", gerr->message,pattern);
return 1;
}
int m = g_regex_match(re, fmt, 0, &mi);
g_match_info_free (mi);
g_regex_unref(re);
if (!m) {
// rrd_set_error("invalid format string '%s' (should match '%s')",fmt,pattern);
return 1;
}
return 0;
}
#define SAFE_STRING "(?:[^%]+|%%)*"
int bad_format_imginfo(char *fmt){
return bad_format_check("^" SAFE_STRING "%s" SAFE_STRING "%lu" SAFE_STRING "%lu" SAFE_STRING "$",fmt);
}
#define FLOAT_STRING "%[-+ 0#]?[0-9]*(?:[.][0-9]+)?l[eEfF]"
int bad_format_axis(char *fmt){
return bad_format_check("^" SAFE_STRING FLOAT_STRING SAFE_STRING "$",fmt);
}
int bad_format_print(char *fmt){
return bad_format_check("^" SAFE_STRING FLOAT_STRING SAFE_STRING "%s" SAFE_STRING "$",fmt);
}
The easiest would be to use sprintf (instead of printf) to compute the result in a string and the test the error code returned by sprintf.
精彩评论