Skip to content

fix(media): prevent getimagesize loopback deadlocks on missing images - #1030

Open
faisalahammad wants to merge 2 commits into
litespeedtech:devfrom
faisalahammad:fix/894-getimagesize-deadlock
Open

fix(media): prevent getimagesize loopback deadlocks on missing images#1030
faisalahammad wants to merge 2 commits into
litespeedtech:devfrom
faisalahammad:fix/894-getimagesize-deadlock

Conversation

@faisalahammad

Copy link
Copy Markdown
Contributor

Summary

Fix getimagesize() LSAPI deadlocks on missing image URLs. When image URLs belong to internal site or CDN host but the file is missing locally, prevent getimagesize() from making self-referencing HTTP loopback requests back to OpenLiteSpeed web server.

Fixes #894

Changes

Media (src/media.cls.php)

Before:

private function _detect_dimensions( $src ) {
    $pathinfo = Utility::is_internal_file( $src );
    if ( $pathinfo ) {
        $src = $pathinfo[0];
    } elseif ( apply_filters( 'litespeed_media_ignore_remote_missing_sizes', false ) ) {
        return false;
    }
    // ...
    $sizes = getimagesize( $src );

After:

private function _detect_dimensions( $src ) {
    $pathinfo = Utility::is_internal_file( $src );
    if ( $pathinfo ) {
        $src = $pathinfo[0];
    } elseif ( Utility::is_internal_url( $src ) || apply_filters( 'litespeed_media_ignore_remote_missing_sizes', false ) ) {
        return false;
    }
    // ...
    $sizes = getimagesize( $src );

Why: If is_internal_file() returns false on an internal URL because the image is missing on disk, checking Utility::is_internal_url( $src ) returns false early. This skips getimagesize() so PHP workers do not make HTTP loopback GET calls to the same web server.

Utility (src/utility.cls.php)

Before:

// Only is_internal_file existed, which requires file to exist on local disk

After:

public static function is_internal_url( $url ) {
    if ( 'data:' === substr( $url, 0, 5 ) ) {
        return false;
    }

    $url_parsed = wp_parse_url( $url );
    if ( empty( $url_parsed['host'] ) ) {
        return true;
    }

    if ( self::internal( $url_parsed['host'] ) || CDN::internal( $url_parsed['host'] ) ) {
        return true;
    }

    return false;
}

Why: Checks if a URL host matches the internal site or internal CDN domain without checking file existence on disk.

Testing

Test 1: Missing Local Image URL

  1. Enable Add Missing Sizes in Media optimization settings.
  2. Load page containing an image tag with a missing local uploads URL.
    Result: Page loads immediately without delays or PHP getimagesize warnings in error log.

Test 2: Valid Local Image

  1. Load page with an existing upload image without width/height attributes.
    Result: Width and height dimensions are calculated and injected from local filesystem path as expected.

@hi-hai

hi-hai commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

Please use dev branch as base.

@faisalahammad
faisalahammad changed the base branch from master to dev August 7, 2026 17:17
@timotei-litespeed

Copy link
Copy Markdown
Contributor

@faisalahammad please make sure there is 1 bug fix per PR.
Thank you

@faisalahammad
faisalahammad force-pushed the fix/894-getimagesize-deadlock branch from ddfd6cd to e05fcca Compare August 7, 2026 20:52
@faisalahammad

Copy link
Copy Markdown
Contributor Author

Rebased on dev branch with single commit for issue #894 fix. Thank you!

@timotei-litespeed

Copy link
Copy Markdown
Contributor

@faisalahammad there are some conflicts to solve

- Check if image URL host is internal domain in Utility::is_internal_url()
- Return false early in _detect_dimensions() when local file is missing for internal URLs
- Eliminate loopback HTTP GET requests to web server on missing images

Fixes litespeedtech#894
@faisalahammad
faisalahammad force-pushed the fix/894-getimagesize-deadlock branch from e05fcca to 0cf7fc2 Compare August 8, 2026 11:05
@faisalahammad

Copy link
Copy Markdown
Contributor Author

I have rebased the branch cleanly on dev branch with 1 commit. The merge conflicts are resolved.

@timotei-litespeed

Copy link
Copy Markdown
Contributor

@faisalahammad thank you! :)
I will look over the change on Monday

… method

- Remove public Utility::is_internal_url() added in this PR
- Move host check into private Media::_is_internal_url()
- Reuse Utility::internal() and CDN::internal() for host matching
- Keep loopback deadlock prevention for missing internal images
- Keep Throwable catch to handle PHP 8 ValueError on empty source

Addresses PR review feedback.

Refs litespeedtech#894
@faisalahammad

Copy link
Copy Markdown
Contributor Author

Addressed reviewer feedback in b80438c:\n\n- The deadlock fix is the internal site/CDN host check before getimagesize(); is_internal_file() alone returns false when the internal file is missing, which is the deadlock case.\n- Removed the new public Utility::is_internal_url() method. Host matching now reuses Utility::internal() and CDN::internal() through a private Media helper.\n- Retained the \Throwable catch because PHP 8 can raise ValueError for an empty filename.\n\nManual testing confirmed. No inline review threads were available through the GitHub API, so this is posted as the PR-level response.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] getimagesize() on missing image causes LSAPI deadlock and full site outage (PHP 8.3.24, LSCache 7.4)

3 participants