DF-3885 / fix.diff
--- a/sys/dev/drm/drm_fb_helper.c 2026-09-16 21:05:17.781704991 +0000 +++ b/sys/dev/drm/drm_fb_helper.c 2026-09-16 21:05:17.793704839 +0000 @@ -2422,7 +2422,8 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, struct drm_fb_helper_crtc **best_crtcs, struct drm_display_mode **modes, - int n, int width, int height) + int n, int width, int height, + unsigned long *budget) { int c, o; struct drm_connector *connector; @@ -2433,11 +2434,23 @@ if (n == fb_helper->connector_count) return 0; + /* + * The recursion below is (crtcs + 1)^connectors calls when every + * connector has a mode. A hostile MST hub can enumerate dozens of + * connectors, which would pin a kms worker (holding fb_helper->lock) + * for days. Bail out of the exhaustive search once the budget is + * spent and keep the best configuration found so far. + */ + if (!*budget) + return 0; + (*budget)--; + fb_helper_conn = fb_helper->connector_info[n]; connector = fb_helper_conn->connector; best_crtcs[n] = NULL; - best_score = drm_pick_crtcs(fb_helper, best_crtcs, modes, n+1, width, height); + best_score = drm_pick_crtcs(fb_helper, best_crtcs, modes, n+1, width, height, + budget); if (modes[n] == NULL) return best_score; @@ -2481,7 +2494,9 @@ crtcs[n] = crtc; memcpy(crtcs, best_crtcs, n * sizeof(struct drm_fb_helper_crtc *)); score = my_score + drm_pick_crtcs(fb_helper, crtcs, modes, n + 1, - width, height); + width, height, budget); + if (!*budget) + break; /* budget exhausted, keep best so far */ if (score > best_score) { best_score = score; memcpy(best_crtcs, crtcs, @@ -2599,7 +2614,13 @@ DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", width, height); - drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height); + { + /* ~1M pick_crtcs invocations is already seconds of KMS work */ + unsigned long budget = 1 << 20; + + drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height, + &budget); + } } mutex_unlock(&fb_helper->dev->mode_config.mutex); |