A funny and useless OOB read in the iMelody parser (macOS)

Jul 13, 2026

Recently I discovered, almost by accident, that:

  1. CoreAudio in macOS contains a parser for the iMelody format. This is a non-polyphonic ringtone format that Sony Ericsson developed for its mobile phones back in the first half of the 2000s. Don’t ask me why a parser for this format ever ended up in a macOS system library, and — more importantly — why it’s still there. I have no idea.

  2. This parser has a bug that leads to an OOB read. The bug has probably gone unnoticed for decades. Right now it’s completely useless from an exploitation standpoint, for several reasons I’ll get into below.

So get comfortable — this is going to be a short and fairly fun blog post (assuming your idea of fun is as peculiar as mine).

General information about the iMelody format

The format is quite simple:

An example .imy file:

BEGIN:IMELODY
VERSION:1.2
FORMAT:CLASS1.0
NAME:Test
BEAT:120
STYLE:S1
VOLUME:V15
MELODY:c1d2e3f4g5
END:IMELODY

This is, of course, far from a complete description of the iMelody format. You can read more about it on Wikipedia or in the specification “Sony Ericsson W800i White Paper”, which I see referenced everywhere but cannot find anywhere (no surprise — more than 20 years have passed). I also highly recommend taking a look at the documentation for the fan project https://imyplay.sourceforge.io/; that’s where I learned most of the details about iMelody.

Overall, the format is catastrophically outdated, so there’s very little information about it.

The MELODY format

This rather long and detailed section is entirely dedicated to the format of the MELODY field. And this format is not exactly all that fun. I promised a short and fun blog post, so you can skip this section and jump straight to the bug. It’s ok, just remember that if there is an opening parenthesis '(' in the MELODY field, there must also be a closing one ')'.

But if you are still interested in reading about the format of the MELODY field, let me start with a warning.

WARNING! I wrote this section together with Claude, since I don’t understand anything about musical notation myself. If someone with a musical education reads this and finds mistakes — apologies, Claude and I did what we could.

So… the MELODY field is a sequence of one or more elements joined together with no separators between them. Each element is one of: a note, a silence (rest), a volume change, an LED control, a vibrate control, a backlight control, or a repeat block. The player processes them left to right.

Notes

A note is written as several parts in this order:

  1. Optional octave prefix*0 through *8, setting which octave the note sounds in. *4 means A = 880 Hz and is the default if you omit the prefix. The chosen octave stays in effect until you specify a different one.
  2. The pitch (mandatory) — one of:
    • Basic notes: c d e f g a b
    • Flat notes: &d &e &g &a &b (the & prefixes the letter)
    • Sharp notes: #c #d #f #g #a (the # prefixes the letter)
  3. Duration digit (mandatory) — 0=whole, 1=½, 2=¼, 3=⅛, 4=1/16, 5=1/32.
  4. Optional duration specifier — modifies the duration: .=dotted, :=double-dotted, ;=two-thirds length. Omitting it means no modification.

Example: *4g3 is a G in octave 4 as an eighth note; #d1 is a D-sharp half note.

Silences (rests)

A rest is the letter r followed by a duration digit and an optional duration specifier, exactly like a note’s timing part. Example: r3 is an eighth-note rest.

Volume

Volume elements can appear anywhere in the melody:

A volume setting persists until changed.

Non-audio controls

These toggle device features inline and can appear between notes:

Repeat blocks

A repeat groups a run of elements to be played more than once. The syntax is:

( <elements> @ <repeat-count> [<volume-modifier>] )

Two things to note about volume inside a repeat:

Example: (#d1r3d2e2@3V+) plays the group #d1 r3 d2 e2 three times, stepping the volume up.

Style interaction (context)

The MELODY content itself doesn’t encode note-vs-rest spacing; that comes from the separate STYLE field (S0 natural with a rest between notes, S1 continuous, S2 staccato), and tempo comes from BEAT. The melody string only specifies pitches, durations, volume, and device toggles.

Example

Let’s look at an example:

V7&b2#c3V-c2*4g3d3V+#d1r3d2e2:d1V+f2f3.

Reading it out: set volume 7 → B-flat ¼ note → C-sharp ⅛ note → volume down → C ¼ note → switch to octave 4, G ⅛ → D ⅛ → volume up → D-sharp ½ → ⅛ rest → D ¼ → E double-dotted ¼ → D ½ → volume up → F ¼ → F dotted ⅛.

The bug

This section describes the actual bug and why it is not a vulnerability.

The OOB read in the loop

To see the actual bug, let’s make sure that on your Mac:

If dyld-shared-cache-extractor isn’t installed, let’s install it:

brew install keith/formulae/dyld-shared-cache-extractor

First of all, extract dyld_shared_cache_arm64e:

dyld-shared-cache-extractor \
  /System/Volumes/Preboot/Cryptexes/OS/System/Library/dyld/dyld_shared_cache_arm64e \
  ./macos26.5.2-libs/

Among the extracted files, find

./macos26.5.2-libs/System/Library/Frameworks/AudioToolbox.framework/Versions/A/AudioToolbox

and open this file in any disassembler. I use Binary Ninja, but Ghidra or Hopper will also work. Then, using symbol navigation, find the Sequence::ReadiMelodyFile method and the loop inside it:

int64_t Sequence::ReadiMelodyFile(TStream& arg1, uint32_t arg2) + 1156:

    @loop:
        ; look for the closing parenthesis ')'
        mov     x0, x19           ; start of the loop: x0 = this (the code uses a special reader object to read bytes)
        bl      sub_1943e12e0     ; call the reader: read the next byte into x0
        cmp     w0, #0x29         ; is it ')'?
        b.ne    @loop             ; no! back to the start of the loop — read another byte!

        ...

This loop starts executing after an opening parenthesis '(' is encountered in the MELODY string. Its purpose is to find the matching closing parenthesis ')'. Suppose our iMelody file looks like this:

BEGIN:IMELODY
VERSION:1.2
FORMAT:CLASS1.0
MELODY:(c1@5V+
END:IMELODY

(there’s no closing parenthesis in MELODY). In this case the loop will read byte after byte, and will only stop once a ')' shows up somewhere in memory, or the allocated memory simply runs out. A textbook OOB read.

Why this is a bug, not a vulnerability

There are two main reasons:

Zero security impact. That’s exactly why I didn’t report this OOB read to Apple.

Speaking of Apple…

…if they ever read this post, I have one recommendation.

Don’t fix anything!

Just remove the iMelody parser from CoreAudio. It’s 2026, it’s about time.

This is a personal website, it is not affiliated with any franchise, brand, or organization. All opinions are the author’s own. Information is provided for educational and research purposes only; see the Disclaimer for details. The website uses software, assets, and hosting listed on the Credits page.