- /* corner case; if this is both the first and last audio page,
- then spec says the end is cut, not beginning */
- if(vb->eofflag){
- /* trim the end */
- /* no preceeding granulepos; assume we started at zero (we'd
- have to in a short single-page stream) */
- /* granulepos could be -1 due to a seek, but that would result
- in a long count, not short count */
-
- v->pcm_current-=(b->sample_count-v->granulepos)>>hs;
- }else{
- /* trim the beginning */
- v->pcm_returned+=(b->sample_count-v->granulepos)>>hs;
- if(v->pcm_returned>v->pcm_current)
- v->pcm_returned=v->pcm_current;
- }
+ /* corner case; if this is both the first and last audio page,
+ then spec says the end is cut, not beginning */
+ long extra=b->sample_count-vb->granulepos;
+
+ /* we use ogg_int64_t for granule positions because a
+ uint64 isn't universally available. Unfortunately,
+ that means granposes can be 'negative' and result in
+ extra being negative */
+ if(extra<0)
+ extra=0;
+
+ if(vb->eofflag){
+ /* trim the end */
+ /* no preceding granulepos; assume we started at zero (we'd
+ have to in a short single-page stream) */
+ /* granulepos could be -1 due to a seek, but that would result
+ in a long count, not short count */
+
+ /* Guard against corrupt/malicious frames that set EOP and
+ a backdated granpos; don't rewind more samples than we
+ actually have */
+ if(extra > (v->pcm_current - v->pcm_returned)<<hs)
+ extra = (v->pcm_current - v->pcm_returned)<<hs;
+
+ v->pcm_current-=extra>>hs;
+ }else{
+ /* trim the beginning */
+ v->pcm_returned+=extra>>hs;
+ if(v->pcm_returned>v->pcm_current)
+ v->pcm_returned=v->pcm_current;
+ }