On Wed, 02 Aug 2023, Ziqi Zhao <astrajoan@xxxxxxxxx> wrote: > In the bug reported by Syzbot, the variable `den == (1 << 22)` and > `mode->vscan == (1 << 10)`, causing the multiplication to overflow and > accidentally make `den == 0`. To prevent any chance of overflow, we > replace `num` and `den` with 64-bit unsigned integers, and explicitly > check if the divisor `den` will overflow. If so, we employ full 64-bit > division with rounding; otherwise we keep the 64-bit to 32-bit division > that could potentially be better optimized. > > In order to minimize the performance overhead, the overflow check for > `den` is wrapped with an `unlikely` condition. Please let me know if > this usage is appropriate. > > Reported-by: syzbot+622bba18029bcde672e1@xxxxxxxxxxxxxxxxxxxxxxxxx > Signed-off-by: Ziqi Zhao <astrajoan@xxxxxxxxx> Reviewed-by: Jani Nikula <jani.nikula@xxxxxxxxx> > --- > V1 -> V2: address style comments suggested by Jani Nikula > <jani.nikula@xxxxxxxxxxxxxxx> > V2 -> V3: change title to include context on overflow causing the > division by zero > > drivers/gpu/drm/drm_modes.c | 11 +++++++---- > 1 file changed, 7 insertions(+), 4 deletions(-) > > diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c > index ac9a406250c5..137101960690 100644 > --- a/drivers/gpu/drm/drm_modes.c > +++ b/drivers/gpu/drm/drm_modes.c > @@ -1285,13 +1285,13 @@ EXPORT_SYMBOL(drm_mode_set_name); > */ > int drm_mode_vrefresh(const struct drm_display_mode *mode) > { > - unsigned int num, den; > + u64 num, den; > > if (mode->htotal == 0 || mode->vtotal == 0) > return 0; > > - num = mode->clock; > - den = mode->htotal * mode->vtotal; > + num = mul_u32_u32(mode->clock, 1000); > + den = mul_u32_u32(mode->htotal, mode->vtotal); > > if (mode->flags & DRM_MODE_FLAG_INTERLACE) > num *= 2; > @@ -1300,7 +1300,10 @@ int drm_mode_vrefresh(const struct drm_display_mode *mode) > if (mode->vscan > 1) > den *= mode->vscan; > > - return DIV_ROUND_CLOSEST_ULL(mul_u32_u32(num, 1000), den); > + if (unlikely(den > UINT_MAX)) > + return DIV64_U64_ROUND_CLOSEST(num, den); > + > + return DIV_ROUND_CLOSEST_ULL(num, (u32) den); > } > EXPORT_SYMBOL(drm_mode_vrefresh); -- Jani Nikula, Intel Open Source Graphics Center