Skip to content

CRITICAL: FDW binary removed before verifying new installation succeeds #4753

@e-gineer

Description

@e-gineer

Bug Description

The FDW installation code removes the existing FDW binary before verifying that the new binary can be successfully extracted. If the ungzip operation fails (due to corrupt download, disk full, etc.), the system is left without any FDW binary.

Severity: CRITICAL

Location

pkg/ociinstaller/fdw.go:70-74

Current Code

// NOTE: for Mac M1 machines, if the fdw binary is updated in place without deleting the existing file,
// the updated fdw may crash on execution - for an undetermined reason
// to avoid this, first remove the existing .so file
os.Remove(fdwBinFileDestPath)  // <-- Removes old binary FIRST
// now unzip the fdw file
if _, err := ociinstaller.Ungzip(fdwBinFileSourcePath, fdwBinDir); err != nil {
    return fmt.Errorf("could not unzip %s to %s: %s", fdwBinFileSourcePath, fdwBinDir, err.Error())
}

Problem Sequence

  1. User has working FDW v1.0 installed
  2. Upgrade to v2.0 begins
  3. os.Remove() deletes the v1.0 binary
  4. Ungzip() attempts to extract v2.0 binary
  5. If ungzip fails:
    • Old v1.0 binary is GONE (deleted in step 3)
    • New v2.0 binary FAILED to install (step 4)
    • System is now BROKEN with no FDW at all

Impact

  • Severity: CRITICAL
  • User Impact: System left in broken state without working FDW
  • Frequency: Occurs on any download corruption, disk errors, or extraction failures during upgrade

Reproduction

See test: TestInstallFdwFiles_CorruptGzipFile_BugDocumentation in pkg/ociinstaller/fdw_test.go

Recommended Fix

Ungzip to a temporary location first, verify success, then atomically swap the binaries:

// Ungzip to temp location first
tempBinaryPath := filepath.Join(tempDir, "steampipe-postgres-fdw.so.tmp")
if _, err := ociinstaller.Ungzip(fdwBinFileSourcePath, tempDir); err != nil {
    return fmt.Errorf("could not unzip %s: %s", fdwBinFileSourcePath, err.Error())
}

// Verify extraction succeeded before touching old binary
if _, err := os.Stat(tempBinaryPath); err != nil {
    return fmt.Errorf("ungzip succeeded but binary not found: %s", err.Error())
}

// NOW it's safe to remove the old binary and move new one
os.Remove(fdwBinFileDestPath)
if err := os.Rename(tempBinaryPath, fdwBinFileDestPath); err != nil {
    return fmt.Errorf("could not install binary: %s", err.Error())
}

Related Tests

  • pkg/ociinstaller/fdw_test.go::TestInstallFdwFiles_CorruptGzipFile_BugDocumentation

References

  • Task 7 completion report: .ai/milestones/wave-3-untested-packages/tasks/task-7-ociinstaller-COMPLETED.md

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinginstallationissues related to db and steampipe installation

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions