Skip to content

ArrowReader.fromFile crashes on files smaller than the Arrow file marker #176

Description

@maltzsama

Describe the bug, including details regarding any error messages, version, and platform.

Description

ArrowReader.fromFile() crashes with a fatal error when processing files smaller than the Arrow file marker size (6 bytes), instead of returning a .failure result.

Root Cause

The validateFileData() function in ArrowReaderHelper.swift attempts to read the first and last 6 bytes of the input data without first checking if the data is large enough:

func validateFileData(_ data: Data) -> Bool {
    let markerLength = FILEMARKER.utf8.count
    let startString = String(decoding: data[..<markerLength], as: UTF8.self)
    let endString = String(decoding: data[(data.count - markerLength)...], as: UTF8.self)
    return startString == FILEMARKER && endString == FILEMARKER
}

When data.count < markerLength (6 bytes), the subscript operations data[..<6] and data[(data.count - 6)...] fail with a range error, causing a crash instead of gracefully returning false.

Impact

Any application using ArrowReader.fromFile() with user-supplied files can be crashed by providing a file smaller than 12 bytes (or any non-Arrow file that happens to be truncated). This is a denial of service vulnerability for any application that doesn't perform pre-validation on file size.

Expected Behavior

ArrowReader.fromFile() should gracefully handle files of any size and return a .failure result with an appropriate ArrowError when the file is not a valid Arrow file.

Steps to Reproduce

let reader = ArrowReader()
let emptyFile = URL(fileURLWithPath: "/tmp/empty.bin")
try Data().write(to: emptyFile)
let result = reader.fromFile(emptyFile)  // Crashes instead of returning .failure

Suggested Fix

Add a bounds check before accessing the data:

func validateFileData(_ data: Data) -> Bool {
    let markerLength = FILEMARKER.utf8.count
    guard data.count >= markerLength * 2 else { return false }
    let startString = String(decoding: data.prefix(markerLength), as: UTF8.self)
    let endString = String(decoding: data.suffix(markerLength), as: UTF8.self)
    return startString == FILEMARKER && endString == FILEMARKER
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions