Skip to content

Fix errno clobbered by cleanup in gerb_fopen()#319

Merged
spe-ciellt merged 1 commit intogerbv:developfrom
SourceParts:fix/errno-clobber-gerb-fopen
Mar 3, 2026
Merged

Fix errno clobbered by cleanup in gerb_fopen()#319
spe-ciellt merged 1 commit intogerbv:developfrom
SourceParts:fix/errno-clobber-gerb-fopen

Conversation

@rampageservices
Copy link
Copy Markdown
Contributor

@rampageservices rampageservices commented Mar 1, 2026

Summary

  • Preserve errno across cleanup calls (fclose, g_free, munmap) in all error paths of gerb_fopen()
  • Fix a latent NULL pointer dereference on the mmap failure path

Found while reviewing #302 — the strerror(errno) pattern used by all three callers (gerbv_open_image, gerbv_create_rs274x_image_from_filename, gerbv_create_excellon_image_from_filename) relies on gerb_fopen() preserving errno, but the cleanup calls before return NULL could clobber it.

Details

gerb_fopen() has six error return paths after the initial allocation. In each case, cleanup functions (fclose(), g_free(), munmap()) run between the failing call and return NULL. These cleanup functions are allowed to modify errno, so callers that use strerror(errno) may get an unrelated error message.

The fix uses the standard POSIX saved_errno idiom — save errno immediately after the failing call, perform cleanup, then restore it before returning. This is the established pattern in GLib/POSIX C code (used throughout glibc internals, libgit2, curl, and GLib itself) for any wrapper function that performs cleanup between a failing syscall and its return.

Additionally, the mmap failure path previously set fd = NULL and fell through to fd->filename = g_strdup(...), which would dereference NULL. This is now a proper return NULL.

Affected error paths

Failing call Cleanup before return errno preserved
g_fopen() g_free() now yes
fstat() fclose(), g_free() now yes
mmap() fclose(), g_free() now yes (+ now returns NULL)
g_malloc() munmap(), fclose(), g_free() now yes
calloc() fclose(), g_free() now yes
fread() fclose(), g_free() x2 now yes

Test plan

  • Clean build, zero warnings
  • 94/104 regression tests pass (10 pre-existing image comparison mismatches)

Ref: #302

Callers of gerb_fopen() use strerror(errno) to report why a file
failed to open, but the cleanup calls inside gerb_fopen() (fclose,
g_free, munmap) could clobber errno before it was returned to the
caller, producing misleading error messages.

Save errno immediately after the failing call and restore it after
cleanup, before returning NULL.  This affects all six error paths:
g_fopen failure, fstat failure, mmap failure, g_malloc failure,
calloc failure, and fread failure.

Also fixes a latent NULL pointer dereference on the mmap failure
path, which previously set fd = NULL and fell through to
fd->filename = g_strdup(...) instead of returning.

Ref: gerbv#302
@spe-ciellt spe-ciellt added the fix Solution for a potential problem or omission. label Mar 3, 2026
@spe-ciellt
Copy link
Copy Markdown
Contributor

g_malloc() NULL path is dead code — saved_errno there is never reached

GLib's g_malloc() is documented to never return NULL: on allocation failure it calls g_error() (which terminates the process). The if (buf == NULL) block at line 116 is therefore dead code, and the saved_errno additions there:

char *buf = (char *)g_malloc(fd->datalen + 1);
if (buf == NULL) {          /* ← can never be true */
    int saved_errno = errno;
    munmap(fd->data, fd->datalen);
    fclose(fd->fd);
    g_free(fd);
    errno = saved_errno;
    return NULL;
}

are unreachable. The PR did not introduce the dead if (buf == NULL) guard (it's pre-existing), and applying the idiom uniformly is defensively correct, so this is not a blocker. However, it's worth a comment that g_malloc is abort-on-OOM in GLib.

Similarly, g_new() at the top of the function (fd = g_new(gerb_file_t, 1)) never returns NULL, making the if (fd == NULL) return NULL check at line 64 also unreachable. Same pre-existing issue, not introduced here.

Considered minor and save for future updates.

@spe-ciellt spe-ciellt merged commit 0305491 into gerbv:develop Mar 3, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fix Solution for a potential problem or omission.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants