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

Closed
madsmtm opened this issue Apr 16, 2025 · 1 comment
Closed

CFStringGetCStringPtr truncates on internal NUL byte #5200

madsmtm opened this issue Apr 16, 2025 · 1 comment
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.

@jmschonfeld
Copy link
Contributor

I'm unable to reproduce this issue on a recent main snapshot from Linux (with some tweaking to the code to prevent a compiler crash around the as! CFString) so I don't believe this issue applies to swift-corelibs-foundation (which is non-Darwin only). I was able to reproduce this on macOS, so this sounds like an issue with the Darwin Foundation.framework/CoreFoundation.framework. Since this is an issue specific with an apple platform, could you please file a feedback via Apple's Feedback assistant? I'm going to go ahead and close this issue since this issue isn't present in swift-corelibs-foundation, but feel free to send the feedback number here in the thread so we can help make sure that feedback gets to the right place to address this on Apple's platforms

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