Security

    Format string vulnerabilities in mobile apps

    A 2026 view of a format string vulnerability where user input passed as the format string lets an attacker read memory, contrasted with a literal format string and input as an argument

    A format string vulnerability is one of those bugs that looks completely harmless: you log a value, or build a string, and pass a variable straight into a formatting function. The problem is that formatting functions interpret special specifiers like %x, %@, or %s in their format string, so if that format string is something an attacker controls, they can make the function read memory it should not, leak data, crash the app, or worse. The fix is almost trivial once you see it: never let external input be the format string itself. Here is what format string vulnerabilities are, how they show up across mobile platforms, and how to avoid them.

    Short answer

    A format string vulnerability happens when externally controlled input is passed as the format string argument to a formatting function, such as the printf family in C, NSLog or stringWithFormat: in Objective-C, or String(format:) in Swift. Per CWE-134, an attacker who controls the format string can include format specifiers to read process memory, leak sensitive data, or crash the app, and in C the %n specifier can write memory, potentially leading to code execution. The fix is to never use external input as the format string: always pass a fixed, literal format string and supply the input as an argument, for example printf("%s", input) rather than printf(input). Treat the format string as code, not data.

    What you should know

    • Formatting functions interpret specifiers: %x, %@, %s, %n, and others.
    • The bug is input as the format string: not as an argument.
    • The impact is memory read, leak, or crash: and in C, memory write.
    • It spans platforms: C, Objective-C, and Swift formatting APIs.
    • The fix is a literal format string: pass input as an argument instead.

    What is a format string vulnerability?

    It is passing attacker-controlled data where a function expects a format string. Formatting functions take a format string containing specifiers, like %d or %s, that tell the function how to interpret and substitute the following arguments. The functions trust that format string to describe the arguments, so when the format string itself is something an external party controls, the attacker can put specifiers in it that the function then acts on, reading values off the stack or from memory that were never intended as arguments. So a call that passes a user-supplied string directly as the format, rather than as a value to be formatted, hands the attacker control over how the function reads memory. The danger is the conflation of data and format: the input should be data the function prints, but by being the format string it becomes instructions the function follows. That is why a line as innocuous-looking as logging a variable can be a real vulnerability if the variable is the format argument.

    How does it manifest across platforms?

    The shape is the same, the severity varies by language. The table summarizes.

    PlatformVulnerable patternImpact
    C / NDKprintf(input) and similarMemory read and, via %n, write
    Objective-CNSLog(input), stringWithFormat:inputMemory read, leak, crash
    SwiftString(format: input)Reading unintended memory, crash
    Java/KotlinString.format(input)Exception or unintended output

    In C and native NDK code, format string bugs are the most severe, because the %n specifier can write to memory, which historically has led to crashes and even code execution, on top of the memory-reading that other specifiers allow. In Objective-C, passing input to NSLog or stringWithFormat: as the format lets an attacker read memory, leak data, or crash the app. In Swift, the format-string APIs carry the same risk if fed attacker input, though Swift's normal string interpolation is not a format string and is safe. In Java and Kotlin, String.format with attacker-controlled input is less catastrophic, there is no %n write, but it can throw or produce unintended output. The common thread across all of them is the same mistake, input used as the format string.

    How do you prevent it?

    Always use a constant format string and pass input as an argument. The single rule that eliminates the entire class is to never pass externally controlled data as the format string: write printf("%s", input), NSLog(@"%@", input), or the equivalent, with a literal format string and the input as a value, rather than passing the input itself as the format. This applies everywhere you format or log, including debug logging, which is a common place the mistake hides because it feels low-stakes. In Swift, prefer normal string interpolation, which is not a format string, and only use the format-string APIs with literal formats. Treat any formatting call where the format argument is a variable as suspect, and check that the variable is a trusted constant, not external input. Where you use static analysis or compiler warnings, enable the ones that flag non-literal format strings, since they catch this automatically. The principle is to keep the format string under your control as a literal and let external input only ever be an argument, so it is treated as data to print, never as instructions to follow.

    What to watch out for

    The first trap is logging input directly, like NSLog(userValue) or printf(userValue), which feels harmless but is the vulnerability; use a literal format and pass the value as an argument. The second is assuming it only matters in C, when Objective-C and Swift formatting APIs are affected too. The third is overlooking debug or error logging, where untrusted values often flow into format calls. Format string bugs are code-level, so a pre-submission scan such as PTKD.com (https://ptkd.com), which reads the binary against OWASP MASVS, assesses input handling broadly, while enabling compiler and static-analysis warnings for non-literal format strings is the most direct way to catch this in your own code.

    What to take away

    • A format string vulnerability occurs when externally controlled input is passed as the format string to a formatting function, letting an attacker read memory, leak data, crash the app, or in C write memory.
    • It manifests across C, Objective-C, and Swift formatting APIs, most severely in native C code where %n enables memory writes.
    • Prevent it with one rule: always use a literal format string and pass input as an argument, never as the format itself, including in debug logging.
    • Enable compiler and static-analysis warnings for non-literal format strings, and use a pre-submission scan such as PTKD.com to assess your app's input handling.
    • #format-string
    • #cwe-134
    • #input-validation
    • #ios
    • #android
    • #owasp-masvs
    • #app-security

    Frequently asked questions

    What is a format string vulnerability?
    It is passing attacker-controlled data where a function expects a format string. Formatting functions take a format string containing specifiers like %d or %s that tell the function how to interpret and substitute the following arguments, and they trust that format string. When the format string itself is externally controlled, an attacker can put specifiers in it that the function acts on, reading values from the stack or memory that were never intended as arguments. The core mistake is conflating data and format: input that should be printed becomes instructions the function follows.
    What can an attacker do with it?
    Depending on the platform, read process memory, leak sensitive data, or crash the app, and in C write memory. Specifiers like %x or %s can make the function read memory off the stack, exposing data or crashing the process. In native C and NDK code the %n specifier can write to memory, which historically has led to crashes and even code execution, making C format string bugs the most severe. In Objective-C and Swift the impact is typically memory reads, leaks, and crashes rather than the memory write that %n allows in C.
    Does this affect Swift and Objective-C, not just C?
    Yes. In Objective-C, passing input to NSLog or stringWithFormat: as the format string lets an attacker read memory, leak data, or crash the app. In Swift, the format-string APIs like String(format:) carry the same risk if fed attacker input, though Swift's normal string interpolation is not a format string and is safe to use with any value. In Java and Kotlin, String.format with attacker input is less catastrophic, with no %n write, but can still throw or produce unintended output. The same mistake spans all of them.
    How do I prevent format string vulnerabilities?
    Follow one rule: never pass externally controlled data as the format string. Always use a literal format string and supply the input as an argument, for example a printf or NSLog call with a fixed %s or %@ format and the value passed separately, rather than passing the input itself as the format. This applies everywhere you format or log, including debug logging where the mistake often hides. In Swift, prefer normal string interpolation, and only use format-string APIs with literal formats.
    Can a scan catch format string bugs?
    Format string vulnerabilities are code-level, so the most direct way to catch them is enabling compiler and static-analysis warnings for non-literal format strings, which flag a format argument that is a variable rather than a constant. A pre-submission scan such as PTKD.com reads the binary against OWASP MASVS and assesses input handling broadly, which is part of identifying where untrusted input is processed. The specific fix, using a literal format string and passing input as an argument, is something you apply and verify in your own code.

    Keep reading

    Scan your app in minutes

    Upload an APK, AAB, or IPA. PTKD returns an OWASP-aligned report with copy-paste fixes.

    Try PTKD free