Overflow in calculating audio timestamp

Jaroslav Kysela perex at perex.cz
Sat Feb 4 16:40:08 CET 2023


On 04. 02. 23 10:11, Alan Young wrote:
> 
> On 03/02/2023 18:02, Jaroslav Kysela wrote:
>> Thank you for your suggestion, but I think that the *whole* code for 
>> !get_time_info in update_audio_tstamp() should be recoded. The calling of 
>> ns_to_timespec64() is not enough to handle the boundary wraps in a decent 
>> range (tenths years for 24x7 operation) 
> 
> Yes, indeed. My ambition was unnecessarily short.
> 
> 
>> and the bellow code is dangerous for 32-bit apps / system:
>>
>>      if (crossed_boundary) {
>>                 snd_BUG_ON(crossed_boundary != 1);
>>                 runtime->hw_ptr_wrap += runtime->boundary;
>>      }
>>
> I don't understand why?
 >
>> I would probably propose to have just hw_ptr_wrap +1 counter (we can 
>> reconstruct the frame position back by multiplication and do range check 
>> later), 
> 
> Would that really help that much? It would extend the total possible duration 
> but perhaps ~1523287 years(below) is sufficient.
> 
>> remove snd_BUG_ON
> 
> Again, why?

For 32-bit apps the boundary is near to UINT32_MAX (see recalculate_boundary() 
function). So only one crossing point is not enough to cover a decent time range.

There should be a better check, if the add operation crosses the U64 range for 
snd_BUG_ON. In my eyes, it looks safer to use counter here and do the checks
in the function which uses this value.

>> and improve the timespec64 calculation.
>>
>> The calculation should be split to two parts (tv_sec / tv_nsec):
>>
>> 1) calculate seconds: (frames / rate)
>> 2) calculate the remainder (ns): ((frames % rate) * NSEC_PER_SEC) / rate
>>
>> With 64-bit integer range, we should go up to (for 384000Hz rate):
>>
>> 2**64 / 384000 / 3600 / 24 / 365 = ~1523287 years
> 
> 
> Yes indeed. How about this?
> 
> static inline void snd_pcm_lib_frames_to_timespec64(u64 frames, unsigned int rate, struct timespec64 *audio_tstamp)
> {
> 	u32 remainder;
> 	audio_tstamp->tv_sec = div_u64_rem(frames, rate, &remainder);
> 	audio_tstamp->tv_nsec = div_u64(mul_u32_u32(remainder, NSEC_PER_SEC), rate);

Yes, this looks fine.

					Jaroslav

-- 
Jaroslav Kysela <perex at perex.cz>
Linux Sound Maintainer; ALSA Project; Red Hat, Inc.



More information about the Alsa-devel mailing list