NSString initWithFormat crash on ios18

var format = "%7B%22sign%22%3Anull%2C%22company%22%3A%22%E5%85%84%E5%BC%9F%E6%B5%B7%E6%B4%8B%E7%A7%91%E6%8A%80%E6%9C%89%E9%99%90%E5%85%AC%E5%8F%B8%22%2C%22businessNo%22%3Anull%2C%22scene%22%3Anull%2C%22interviewCode%22%3A%22767676%22%7D"
let message = withVaList([]) { args in
let msg = NSString(format: format, arguments: args)
print(msg)
}

Answered by DTS Engineer in 830134022

On all versions of iOS 18?

If so, I don’t think there’s much point filing a bug about this. What you’re doing is very worrying from a binary compatibility perspective. If you’d noticed this during the iOS 18.0 beta cycle then it would be reasonable to file a bug about this that’s focused on this binary compatibility issue. However, the iOS 18 ship sailed many months ago, so the time for that has past. It’s now time to fix your code.

You didn’t give us a lot of details about your code but the code you did post is very concerning. Using varargs from Swift is challenging at the best of times — it’s hard using varags correctly from C, and Swift doesn’t make it any easier! — and the snippet you posted is clearly incorrect.

Consider this tiny test project:

import Foundation
func main() {
var format = "%7B%22s\n"
withVaList([]) { args in
let msg = NSString(format: format, arguments: args)
print(msg)
}
}
main()

On my Mac it prints:

7B(null)

That’s because %22s is treated as a conversion specifier, namely for a 22 character wide C string. You’re not passing it a C string, and thus it gets an undefined value. It just so happens that this value is NULL, and hence the (null) is the output. However, it could just as easily be some other random value that causes a memory access exception.

If you post more info about your high-level goal here, I should be able to offer more insight into how to proceed.

Share and Enjoy

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

You should probably raise this as a bug in the usual way. It won't really get progressed if it's only posted in these Developer Forums.

You need to raise each issue you find separately at https://feedbackassistant.apple.com/ You can post the FB numbers here if you want, so that others can link to them.

On all versions of iOS 18?

If so, I don’t think there’s much point filing a bug about this. What you’re doing is very worrying from a binary compatibility perspective. If you’d noticed this during the iOS 18.0 beta cycle then it would be reasonable to file a bug about this that’s focused on this binary compatibility issue. However, the iOS 18 ship sailed many months ago, so the time for that has past. It’s now time to fix your code.

You didn’t give us a lot of details about your code but the code you did post is very concerning. Using varargs from Swift is challenging at the best of times — it’s hard using varags correctly from C, and Swift doesn’t make it any easier! — and the snippet you posted is clearly incorrect.

Consider this tiny test project:

import Foundation
func main() {
var format = "%7B%22s\n"
withVaList([]) { args in
let msg = NSString(format: format, arguments: args)
print(msg)
}
}
main()

On my Mac it prints:

7B(null)

That’s because %22s is treated as a conversion specifier, namely for a 22 character wide C string. You’re not passing it a C string, and thus it gets an undefined value. It just so happens that this value is NULL, and hence the (null) is the output. However, it could just as easily be some other random value that causes a memory access exception.

If you post more info about your high-level goal here, I should be able to offer more insight into how to proceed.

Share and Enjoy

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

If you post more info about your high-level goal here, I should be able to offer more insight into how to proceed.

That format string is actually URL-encoded JSON:

{"sign":null,"company":"兄弟海洋科技有限公司","businessNo":null,"scene":null,"interviewCode":"767676"}

Even without the URL encoding, there are no %@ placeholders that the string initializer expects.

So if the high-level goal is to generate this JSON with certain parameters inserted at run time, and then URL-encode it, there are far better techniques for doing so.

So most of those % should have been %%, right?

Well, I suppose that could work but it doesn’t exactly improve readability. 😉

If this landed in the inbox of peer code reviews at my day job here, I’d ask them to redo it using a struct implementing Encodable, a JSONEncoder, and either string modifier addingPercentEncoding(withAllowedCharacters:) or one of the URL APIs that does percent encoding automatically.

My advice would be to never use a variable as a format. It’s a common cause of crashes and security vulnerabilities. See Uncontrolled format string.

Share and Enjoy

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

NSString initWithFormat crash on ios18
 
 
Q