cybersecurity · intermediate · ~20 min
Implement a tolerant scanner that recognises real printf specifiers and ignores `%%`.
Implement int has_format_specifier(const char *s) returning 1 if s contains a character sequence that looks like a printf format specifier, else 0.
For simplicity treat any % that is followed (after optional flags -+ 0#, optional width digits, optional .precision, optional length modifier l/h/ll/z) by one of d, i, u, x, X, o, c, s, p, n, f, e, g, a, A as a specifier.
A literal %% (escaped percent) does NOT count.
has_format_specifier("hello %s") -> 1
has_format_specifier("100%%") -> 0 // escaped
has_format_specifier("rate=99%") -> 0 // % at end, no letter
has_format_specifier("count: %5d") -> 1
has_format_specifier("plain text") -> 0
has_format_specifier("%n is bad") -> 1 // %n writes to memory — particularly dangerous
printf(user_input) lets an attacker read stack memory with %x %x %x, dereference pointers with %s, and even write to memory with %n. The fix is to always call printf("%s", user_input), never printf(user_input). This validator is a defensive last line of defence when you're unsure whether an upstream caller obeyed the rule.
printf(user_string) is one of the oldest and most-exploited bugs in C. The fix is to refuse user-supplied format strings before they reach printf. This exercise builds a lightweight pre-check that flags any % that isn't escaped or in a known-safe position.
Null-terminated string.
0 or 1.
O(strlen).
int has_format_specifier(const char *s) { /* TODO */ return 0; }
Returning 1 for any % (false positives on 99%). Forgetting %%. Ignoring width/precision/length modifiers.
%% (escape); % at end; %5d; %.2f; %n (especially dangerous).
O(strlen).
Solve this exercise in the browser editor — compile and run against the test harness, no setup required.