Skip to content

Ignored return values in drflac__decode_subframe (dr_flac.h:5368) leading to use of uninit data #310

Description

@DanielFi

In accordance with your security policy, I'm disclosing this issue publicly.

Summary

The return value of drflac__decode_samples__* is ignored in drflac__decode_subframe. These functions can fail before writing enough values to pSubframe->pSamplesS32. In that case, the previous values will be used as decoded sample data. If the current frame's block size is larger than those of all previous frames, uninitialized data from the heap will be used as decoded sample data.

    switch (pSubframe->subframeType)
    {
        case DRFLAC_SUBFRAME_CONSTANT:
        {
            drflac__decode_samples__constant(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32);
        } break;

        case DRFLAC_SUBFRAME_VERBATIM:
        {
            drflac__decode_samples__verbatim(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32);
        } break;

        case DRFLAC_SUBFRAME_FIXED:
        {
            drflac__decode_samples__fixed(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32);
        } break;

        case DRFLAC_SUBFRAME_LPC:
        {
            drflac__decode_samples__lpc(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32);
        } break;

        default: return DRFLAC_FALSE;
    }

    return DRFLAC_TRUE;

Most failures are triggered by reaching the end of bs, in which case the CRC check will fail before using the poisoned data. But some failures can be triggered with invalid data in bs instead. For example, an invalid partition order (> 8) which leads to an early return in drflac__decode_samples_with_residual.

    if (!drflac__read_uint8(bs, 4, &partitionOrder)) {
        return DRFLAC_FALSE;
    }

    /*
    From the FLAC spec:
      The Rice partition order in a Rice-coded residual section must be less than or equal to 8.
    */
    if (partitionOrder > 8) {
        return DRFLAC_FALSE;
    }

In that case, if there's a valid CRC immediately following the corrupt data, these poisoned values will be returned to the user as decoded sample data.

Reproduction

Attached are:

> python3 make_poc_flac.py poc.flac
wrote /poc.flac (53 bytes)
> clang -fsanitize=memory -fsanitize-memory-track-origins=2 -g -O1 msan_harness.c -o msan_harness
> ./msan_harness poc.flac
frames=16
Uninitialized bytes in __interceptor_fwrite at offset 0 inside [0x7ffffffcaa40, 64)
==11==WARNING: MemorySanitizer: use-of-uninitialized-value
    #0 0x55555562c086 in main /msan_harness.c:29:9
    #1 0x7fffff50a249  (/lib/x86_64-linux-gnu/libc.so.6+0x27249) (BuildId: 6196744a316dbd57c0fd8968df1680aac482cec4)
    #2 0x7fffff50a304 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x27304) (BuildId: 6196744a316dbd57c0fd8968df1680aac482cec4)
    #3 0x5555555752d0 in _start (/msan_harness+0x212d0) (BuildId: 2050e690069b925548a3ebc86683e24af73d3847)

  Uninitialized value was stored to memory at
    #0 0x555555605cab in drflac_read_pcm_frames_s32 /dr_flac.h:9915:56

  Uninitialized value was created by a heap allocation
    #0 0x5555555a9cc0 in __interceptor_malloc (/msan_harness+0x55cc0) (BuildId: 2050e690069b925548a3ebc86683e24af73d3847)
    #1 0x55555562c4a4 in drflac__malloc_default /dr_flac.h:6353:12

SUMMARY: MemorySanitizer: use-of-uninitialized-value /msan_harness.c:29:9 in main
Exiting

Suggested Fix

Check the return value of drflac__decode_samples__* and return DRFLAC_FALSE if they fail.

    switch (pSubframe->subframeType)
    {
        case DRFLAC_SUBFRAME_CONSTANT:
        {
            if (!drflac__decode_samples__constant(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32)) {
                return DRFLAC_FALSE;
            }
        } break;

        case DRFLAC_SUBFRAME_VERBATIM:
        {
            if (!drflac__decode_samples__verbatim(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32)) {
                return DRFLAC_FALSE;
            }
        } break;

        case DRFLAC_SUBFRAME_FIXED:
        {
            if (!drflac__decode_samples__fixed(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32)) {
                return DRFLAC_FALSE;
            }
        } break;

        case DRFLAC_SUBFRAME_LPC:
        {
            if (!drflac__decode_samples__lpc(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32)) {
                return DRFLAC_FALSE;
            }
        } break;

        default: return DRFLAC_FALSE;
    }

    return DRFLAC_TRUE;

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions