Variable argument list walked with the <stdarg.h> macros
ID |
objectivec.maintainability.avoid_variadic_functions |
Severity |
info |
Remediation Complexity |
hard |
Remediation Risk |
medium |
Remediation Effort |
medium |
Resource |
Api Design |
Language |
Objective-C |
Description
Reports a use of the <stdarg.h> facility - va_start, va_arg or va_end - which is how a method or function that takes a variable argument list reads what the caller passed.
Rationale
Nothing checks a variadic call. The compiler cannot see how many arguments a caller supplied or what their types were, so va_arg reading one type where the caller pushed another is undefined behaviour discovered at run time rather than a build error, and walking past the last argument reads whatever happens to be on the stack. Objective-C makes the second failure routine, because the usual convention is a nil-terminated list and a caller who forgets the terminator walks off the end. Neither ARC nor the static analyser can reason about the types crossing that boundary either.
The following code illustrates the pattern detected by this rule:
@implementation Logger
- (void)logFormat:(NSString *)format, ... {
va_list args;
// FLAGGED: Variable argument list walked with the <stdarg.h> macros
va_start(args, format);
// FLAGGED: Variable argument list walked with the <stdarg.h> macros
NSString *line = [[NSString alloc] initWithFormat:format arguments:va_arg(args, id)];
// FLAGGED: Variable argument list walked with the <stdarg.h> macros
va_end(args);
NSLog(@"%@", line);
}
@end
Remediation
Take an NSArray or NSDictionary parameter, or a small options object, instead of a variable argument list - the collection literal syntax makes the call site no longer than the variadic one and the contents stay type checked. Where a variadic entry point has to be kept for source compatibility, make it a thin wrapper that packs the arguments into a collection and forwards to the checked method that does the work.