On Sat, Aug 01, 2026 at 10:29:22PM +0000, Deep Shah wrote:
> ptp_clock_adjtime() validates an ADJ_FREQUENCY request by converting the
> requested scaled ppm to ppb and comparing it against ops->max_adj:
>
> long ppb = scaled_ppm_to_ppb(tx->freq);
> if (ppb > ops->max_adj || ppb < -ops->max_adj)
> return -ERANGE;
>
> scaled_ppm_to_ppb() computes (1 + ppm) * 125 >> 13 in s64. For a
> sufficiently large tx->freq the multiplication overflows s64 and wraps,
> so the resulting ppb can fall back within [-max_adj, max_adj] and pass
> the check. The unclamped tx->freq is then handed to ->adjfine(), where
> drivers scale it again (e.g. scaled_ppm * 762939453125 in ptp_idt82p33)
> and program a bogus frequency word.
>
> For example tx->freq = 147573952589676412 makes (1 + ppm) * 125 equal
> 2^64 + 9, which wraps to ppb == 0 and is accepted.
>
> The caller already has write access to the PHC, so this hardens the
> max_adj sanity check rather than crossing a privilege boundary, and
> well-behaved user space (e.g. ptp4l) never requests such values. It is
> a follow-up to commit 475b92f93216 ("ptp: improve max_adj check against
> unreasonable values"), which handled the analogous s32 narrowing but not
> this multiplication overflow.
>
> Detect the overflow with check_*_overflow() and reject the request in
> ptp_clock_adjtime() instead of acting on the wrapped value.
>
> Signed-off-by: Deep Shah <[email protected]>
> Reviewed-by: Vadim Fedorenko <[email protected]>
Acked-by: Richard Cochran <[email protected]>