Skip to content
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

Currently tail recursion is not guaranteed in Swift #6

Closed
jazzbox opened this issue Feb 26, 2015 · 2 comments
Closed

Currently tail recursion is not guaranteed in Swift #6

jazzbox opened this issue Feb 26, 2015 · 2 comments

Comments

@jazzbox
Copy link

jazzbox commented Feb 26, 2015

This is especially problematic in Debug mode, where no tail recursion exists! I found this when some JPEGs crashed my App. This could also easily be used as an attack vector with specially crafted data.

My Solution for parsing JPEGs is to translate the parse function into a looped version:

private typealias JPEGParseTuple = (data: NSData, offset: Int, segment: JPEGHeaderSegment)

private enum JPEGParseResult {
    case Size(CGSize)
    case Tuple(JPEGParseTuple)
}

private static func parseJPEG(tuple: JPEGParseTuple) -> JPEGParseResult {
    let data = tuple.data
    let offset = tuple.offset
    let segment = tuple.segment

    if segment == .EOISegment
        || (data.length <= offset + 1)
        || (data.length <= offset + 2) && segment == .SkipSegment
        || (data.length <= offset + 7) && segment == .ParseSegment {
            return .Size(CGSizeZero)
    }
    switch segment {
    case .NextSegment:
        let newOffset = offset + 1
        var byte = 0x0; data.getBytes(&byte, range: NSRange(location: newOffset, length: 1))

        if byte == 0xFF {
            return .Tuple(JPEGParseTuple(data, offset: newOffset, segment: .SOFSegment))
        } else {
            return .Tuple(JPEGParseTuple(data, offset: newOffset, segment: .NextSegment))
        }
    case .SOFSegment:
        let newOffset = offset + 1
        var byte = 0x0; data.getBytes(&byte, range: NSRange(location: newOffset, length: 1))

        switch byte {
        case 0xE0...0xEF:
            return .Tuple(JPEGParseTuple(data, offset: newOffset, segment: .SkipSegment))
        case 0xC0...0xC3, 0xC5...0xC7, 0xC9...0xCB, 0xCD...0xCF:
            return .Tuple(JPEGParseTuple(data, offset: newOffset, segment: .ParseSegment))
        case 0xFF:
            return .Tuple(JPEGParseTuple(data, offset: newOffset, segment: .SOFSegment))
        case 0xD9:
            return .Tuple(JPEGParseTuple(data, offset: newOffset, segment: .EOISegment))
        default:
            return .Tuple(JPEGParseTuple(data, offset: newOffset, segment: .SkipSegment))
        }

    case .SkipSegment:
        var length = UInt16(0)
        data.getBytes(&length, range: NSRange(location: offset + 1, length: 2))

        let newOffset = offset + Int(CFSwapInt16(length)) - 1
        return .Tuple(JPEGParseTuple(data, offset: Int(newOffset), segment: .NextSegment))

    case .ParseSegment:
        var size = JPEGSize(); data.getBytes(&size, range: NSRange(location: offset + 4, length: 4))
        return .Size(CGSize(width: Int(CFSwapInt16(size.width)), height: Int(CFSwapInt16(size.height))))
    default:
        return .Size(CGSizeZero)
    }

}

private static func parseJPEGData(data: NSData, offset: Int, segment: JPEGHeaderSegment) -> CGSize {
    var tuple: JPEGParseResult = .Tuple(JPEGParseTuple(data, offset: offset, segment: segment))
    while true {
        switch tuple {
        case .Size(let size):
            return size
        case .Tuple(let newTuple):
            tuple = parseJPEG(newTuple)
        }
    }
}
@bananita
Copy link
Contributor

hello @kaishin ! are you going to fix it?

@kaishin
Copy link
Owner

kaishin commented Jan 28, 2016

@bananita It might take me a while, so I'd be happy to review a PR instead to speed things up.

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

3 participants