Skip to content
Haiku OSHow-To Published Updated 3 min readViews unavailable

How to Design a Haiku Translation Kit Add-On Without Corrupting Input Data

A robust Haiku translator design covering format identification, bounded parsing, streaming conversion, metadata, roster discovery, and hostile-file tests.

Haiku’s Translation Kit lets applications convert media through a common interface rather than embedding every format codec. A translator add-on advertises supported input and output formats, identifies a stream, and translates it. Because the input can be malformed or hostile, format parsing—not pixel conversion—is the highest-risk part of the implementation.

Specify the format before coding

Write down the byte order, magic values, header length, dimensions, channel layout, compression, offsets, and legal limits. Include integer overflow rules and whether trailing data is permitted. If the format has revisions, treat each revision explicitly rather than guessing from file extensions.

Use a unique MIME type and stable translator identifiers according to the Translation Kit contract. Confidence and quality values influence translator selection, so report high confidence only after validating enough bytes to distinguish the format. An extension or MIME hint can guide detection but must not override contradictory content.

Identify without consuming the caller’s stream incorrectly

Identify() receives a BPositionIO stream. Read the minimum required header at a known position, validate it, populate translator_info, and leave stream positioning consistent with the API expectations. Check every Read() result for short input. A file ending halfway through a header is an ordinary error case, not permission to read uninitialized memory.

Parsing dimensions requires overflow-safe arithmetic before allocation:

if (width == 0 || height == 0 || width > kMaxDimension
    || height > kMaxDimension)
    return B_BAD_DATA;

size_t rowBytes;
if (__builtin_mul_overflow((size_t)width, (size_t)4, &rowBytes))
    return B_BAD_DATA;
if (rowBytes > kMaxDecodedBytes / height)
    return B_BAD_DATA;

Use a portable checked-multiplication helper if the supported compiler does not expose the builtin. The important order is validate first, multiply safely, allocate last.

Translate as a bounded stream

Implement Translate() so it writes the requested output format to the destination BMessage/BPositionIO contract and propagates write failures. For large images, decode one row or bounded block at a time rather than trusting a header-controlled full-image allocation. Reject impossible strides, overlapping offsets, invalid palette indexes, decompression expansion beyond the declared limit, and metadata lengths that exceed remaining input.

When producing Haiku’s standard bitmap interchange format, fill every header field with the correct endianness and color-space meaning. Zero padding bytes so output is deterministic and cannot leak old heap contents. If alpha or color-profile information cannot be represented faithfully, document the conversion instead of silently inventing values.

Settings and extension messages are also inputs. Validate option names and types and provide stable defaults. An unsupported output type should return a precise status, leaving no partial file that a caller might mistake for success.

Install and test through the roster

Build against the same Haiku target and ABI as the host, install the add-on in the appropriate user or system translator directory, and let BTranslatorRoster discover it. Do not overwrite a system translator during development. Use a user-scoped directory and keep an easy removal path if the add-on crashes a host.

Test direct calls and real roster selection. The corpus should include a minimal valid file, every supported revision, truncated input at every structural boundary, huge dimensions, arithmetic edge cases, malformed compressed runs, repeated metadata, random bytes, and round trips where the format is lossless. Fuzzing Identify() and Translate() with memory diagnostics is especially valuable because these functions process untrusted files inside client applications.

A trustworthy translator is conservative: it recognizes only what it can prove, bounds all work before allocation, emits deterministic output, and fails without leaving corrupt data behind.

Related:

Sources:

Comments