Skip to content

CFStringGetCStringPtr truncates on internal NUL byte #5200

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
madsmtm opened this issue Apr 16, 2025 · 0 comments
Open

CFStringGetCStringPtr truncates on internal NUL byte #5200

madsmtm opened this issue Apr 16, 2025 · 0 comments
Assignees

Comments

@madsmtm
Copy link

madsmtm commented Apr 16, 2025

CFStringGetCStringPtr is meant as an optimization to allow users to avoid a copy when the string is known to be valid ASCII, UTF-8 or similar 8-bit encoding.

It has a flaw though: It does not check whether the string contains interior NUL bytes. Consider the following Swift code (tested on macOS 14.7.4) (not specific to Swift, the problem is present in plain Objective-C too):

import CoreFoundation

// All of Swift.String, NSString and CFString support strings with interior NUL bytes.
let s = "A string with a \0 <- NUL right there"
print(s) // Prints the full string, i.e. preserves the content after the NUL byte.
print(s.count) // Prints 36 as expected.

func cf_roundtrip(_ s: String) -> String? {
    let cfstr = s as! CFString
    guard let ptr = CFStringGetCStringPtr(cfstr, CFStringBuiltInEncodings.UTF8.rawValue) else {
        return nil
    }
    return String(cString: ptr)
}

// Most strings can be round-tripped through `CFStringGetCStringPtr`, and if it can't, will return `nil`.
print(cf_roundtrip("Hello World!") as Any)           // Prints `Optional("Hello World!")`
print(cf_roundtrip("Contains non-ASCII: 😀") as Any) // Prints `nil`

// Round-tripping strings with interior NUL bytes through `CFStringGetCStringPtr` doesn't work correctly though:
print(cf_roundtrip(s) as Any) // Prints `Optional("A string with a ")`
// !!! The string was truncated, should have returned `nil` instead!

That is, CFStringGetCStringPtr ends up returning the string pointer, but because of consumers assuming that the internal NUL byte is the final NUL, it silently truncates the rest of the string. A mitigation here would be to only use ASCII and to check CFStringGetLength as done in 860956a and 8422c1a when Swift.String itself was affected by this bug in the past, but I suspect only the vast minority of people will find that solution.

I actually found this by using CFURLCreateWithBytes with an interior NUL byte, which makes it unexpectedly fail because of this check that was added in newer versions.

I suspect this is potentially a security issue (see e.g. CWE-158 / CWE-626 and RUSTSEC-2021-0123). Semi related to #5164.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants