Skip to content

chd_open_core_file_callbacks: whether the handle is closed on failure depends on where it failed #193

Description

@rtissera

chd_open_core_file_callbacks() closes the caller's handle on some failure paths and not on others, and the caller cannot tell which happened.

Once it has allocated its chd_file, a failure goes through cleanup:chd_close()core_fclose(), which calls the caller's fclose callback. The three earliest failures — callbacks == NULL, an invalid parent, and malloc of the chd_file itself — return before that and leave the handle open. Both kinds can report CHDERR_OUT_OF_MEMORY.

Every bridge in contrib/ assumes the handle is still theirs on failure and closes it:

  • contrib/esp32p4/chd/chd_esp_vfs.c — its callback does fclose(f), and on failure the bridge does fclose(f) again. A second fclose() on the same FILE* is undefined behaviour and can corrupt the heap. Reachable from any image that fails after allocation: bad header, corrupt map, allocation failure while opening.
  • contrib/tangcore-bl616/chd/chd_fatfs.c — same shape, but FatFs's f_close() rejects an object that is already closed, so it was harmless. FatFs bridges: build a cluster link map on open #194 makes that callback explicitly idempotent because it now frees a cluster link map there.

Two ways to close it:

  1. Make the contract simple: on failure the handle is always the caller's. Clear newchd->file.callbacks before chd_close() on the cleanup: path. That matches what every bridge already assumes, but it changes behaviour for any caller relying on libchdr closing on failure — worth checking chd_open()'s own stdio path, and DuckStation's CHD_OPEN_TRANSFER_FILE, which exists precisely because of this ownership question.
  2. Keep the behaviour, document it, and make each bridge's close idempotent — which a plain FILE* cannot be without a wrapper.

The first is the better contract. Either way, chd_esp_vfs.c needs fixing.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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