%n used in a non-immutable format string

Background:

I am investigating an issue with the logging in my app. The app receives some string from the server, which we log with function similar to NSLog. The format we call that function is:

Code Block C
MyLog(@"%@", message);

In one case, message contains text "Save 38% now". At this point the app crashes.

The issue:

While looking into it I was able to pinpoint the issue. The full code is below.

Code Block C
#define FOO(FORMAT, ...) (\
{\
char *str;\
str = [[NSString stringWithFormat:FORMAT, ##VA_ARGS] UTF8String];\
str;\
}\
)\
#import "MyClass.h"
@implementation MyClass
- (instancetype)init
{
    self = [super init];
    if (self) {
        char *foo = FOO(@"%@", @"Save 38% now");
        printf(foo);
    }
    return self;
}
@end

Instantiate class anywhere,

Code Block
MyClass *my = [[MyClass alloc] init];

, and the app will crash upon printf(foo); with the message:

Code Block
%n used in a non-immutable format string


Basically, the string "% n", with a space between "%" and "n" is threated as "%n", which is considered a vulnerability since latest OS releases.

Discussion:
I am still trying to proof my thoughts and find a way to solve the issue.

I believe, the code responsible for the crash is this: https://opensource.apple.com/source/Libc/Libc-1244.30.3/stdio/FreeBSD/vfprintf.c

I cannot understand why space between two characters does not make any difference for the code.

So far, it looks like a bug.

Solution:
n/a

Accepted Reply

Regardless of the space issue you highlighted, the code you posted doesn’t make sense. Specifically, this line:

Code Block
printf(foo);


should be this:

Code Block
printf("%s", foo);


Without that, any percents in foo will be interpreted as introducing a conversion specifier and that’s not what you want.

If you want this sort of code to work as written, you’d need code to escape the percents. Or switch to fwrite.



Beyond that, your FOO macro’s use of UTF8String is worrying. UTF8String is declared using NS_RETURNS_INNER_POINTER, which maps to objc_returns_inner_pointer. Technically this is supposed to work because the Clang docs (1) say “the last use of the returned pointer … in the calling function” — note that it’s “calling function”, not “enclosing scope” — but I’d be inclined to be a bit conservative here.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"

(1) releases.llvm.org/9.0.0/tools/clang/docs/AutomaticReferenceCounting.html#interior-pointers

Replies

Regardless of the space issue you highlighted, the code you posted doesn’t make sense. Specifically, this line:

Code Block
printf(foo);


should be this:

Code Block
printf("%s", foo);


Without that, any percents in foo will be interpreted as introducing a conversion specifier and that’s not what you want.

If you want this sort of code to work as written, you’d need code to escape the percents. Or switch to fwrite.



Beyond that, your FOO macro’s use of UTF8String is worrying. UTF8String is declared using NS_RETURNS_INNER_POINTER, which maps to objc_returns_inner_pointer. Technically this is supposed to work because the Clang docs (1) say “the last use of the returned pointer … in the calling function” — note that it’s “calling function”, not “enclosing scope” — but I’d be inclined to be a bit conservative here.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"

(1) releases.llvm.org/9.0.0/tools/clang/docs/AutomaticReferenceCounting.html#interior-pointers