diff mbox

[FFmpeg-devel] dvenc: Prevent out-of-bounds read

Message ID 20171117162055.7128-1-derek.buitenhuis@gmail.com
State New
Headers show

Commit Message

Derek Buitenhuis Nov. 17, 2017, 4:20 p.m. UTC
mb_area_start has 5 entries, and 'a' is iterated through from 0 to 3.
'a2' is set to 'a + 1', and mb_area_start[a2 + 1] is accessed, so if
a is 3, then we try to access mb_area_start[5].

Signed-off-by: Derek Buitenhuis <derek.buitenhuis@gmail.com>
---
I'm not 100% sure if this fix is /correct/, so hopefully someone
knows the DV code...
---
 libavcodec/dvenc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Comments

Martin Vignali Nov. 17, 2017, 4:42 p.m. UTC | #1
2017-11-17 17:20 GMT+01:00 Derek Buitenhuis <derek.buitenhuis@gmail.com>:

> mb_area_start has 5 entries, and 'a' is iterated through from 0 to 3.
> 'a2' is set to 'a + 1', and mb_area_start[a2 + 1] is accessed, so if
> a is 3, then we try to access mb_area_start[5].
>
> Signed-off-by: Derek Buitenhuis <derek.buitenhuis@gmail.com>
> ---
> I'm not 100% sure if this fix is /correct/, so hopefully someone
> knows the DV code...
> ---
>  libavcodec/dvenc.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/libavcodec/dvenc.c b/libavcodec/dvenc.c
> index ce2fc75daa..b79cbebb04 100644
> --- a/libavcodec/dvenc.c
> +++ b/libavcodec/dvenc.c
> @@ -383,7 +383,7 @@ static inline void dv_guess_qnos(EncBlockInfo *blks,
> int *qnos)
>                                  prev            = k;
>                              } else {
>                                  if (b->next[k] >= mb_area_start[a + 1] &&
> b->next[k] < 64) {
> -                                    for (a2 = a + 1; b->next[k] >=
> mb_area_start[a2 + 1]; a2++)
> +                                    for (a2 = a + 1; a2 < 4 && b->next[k]
> >= mb_area_start[a2 + 1]; a2++)
>                                          b->prev[a2] = prev;
>                                      av_assert2(a2 < 4);
>                                      av_assert2(b->mb[b->next[k]]);
> --
>
>
Hello,

doesn't know the dvenc code,
but you seems to test the assert of the next line

Maybe move the assert (a2 < 4); before the for loop, if it's a theorical
case,
or remove it if this case can really happen.

Martin
Derek Buitenhuis Nov. 17, 2017, 5:05 p.m. UTC | #2
On 11/17/2017 4:42 PM, Martin Vignali wrote:
> doesn't know the dvenc code,
> but you seems to test the assert of the next line
> 
> Maybe move the assert (a2 < 4); before the for loop, if it's a theorical
> case,
> or remove it if this case can really happen.

I don't see anything that would prevent it from happening, but the code
is also about as clear as mud.

In general, I think I prefer a real check to an assert, unless someone
can explain why it *wouldn't* happen.

After git blame-ing my way through roughly 166393765431 "K&R formatting"
and "refactor" commits, the original code comes from the ancient commit of 
d2d230a7569154306a1625ca37dbfa4c36627ec6 which provides no info whatsoever
on why it is correct at all.

CCing Michael, as he authored that commit - maybe he can provide insight.

- Derek
Michael Niedermayer Nov. 17, 2017, 5:37 p.m. UTC | #3
On Fri, Nov 17, 2017 at 04:20:55PM +0000, Derek Buitenhuis wrote:
> mb_area_start has 5 entries, and 'a' is iterated through from 0 to 3.
> 'a2' is set to 'a + 1', and mb_area_start[a2 + 1] is accessed, so if
> a is 3, then we try to access mb_area_start[5].
> 
> Signed-off-by: Derek Buitenhuis <derek.buitenhuis@gmail.com>
> ---
> I'm not 100% sure if this fix is /correct/, so hopefully someone
> knows the DV code...
> ---
>  libavcodec/dvenc.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/libavcodec/dvenc.c b/libavcodec/dvenc.c
> index ce2fc75daa..b79cbebb04 100644
> --- a/libavcodec/dvenc.c
> +++ b/libavcodec/dvenc.c
> @@ -383,7 +383,7 @@ static inline void dv_guess_qnos(EncBlockInfo *blks, int *qnos)
>                                  prev            = k;
>                              } else {
>                                  if (b->next[k] >= mb_area_start[a + 1] && b->next[k] < 64) {
> -                                    for (a2 = a + 1; b->next[k] >= mb_area_start[a2 + 1]; a2++)
> +                                    for (a2 = a + 1; a2 < 4 && b->next[k] >= mb_area_start[a2 + 1]; a2++)
>                                          b->prev[a2] = prev;
>                                      av_assert2(a2 < 4);

hmm, i cant really remember this clearly but from looking at the code
it looks like this is the logic:
b->next[k] < 64
b->next[k] >= mb_area_start[a + 1] implies mb_area_start[a + 1] < 64
which implies a < 3
and a2 < 4 on the first iteration so the first is still in the array
subsequently, b->next[k] >= mb_area_start[a2 + 1] exists before the end
as b->next[k] < 64 and the last entry being 64


[...]
Derek Buitenhuis Nov. 17, 2017, 5:54 p.m. UTC | #4
On 11/17/2017 5:37 PM, Michael Niedermayer wrote:
> hmm, i cant really remember this clearly but from looking at the code
> it looks like this is the logic:
> b->next[k] < 64
> b->next[k] >= mb_area_start[a + 1] implies mb_area_start[a + 1] < 64
> which implies a < 3
> and a2 < 4 on the first iteration so the first is still in the array
> subsequently, b->next[k] >= mb_area_start[a2 + 1] exists before the end
> as b->next[k] < 64 and the last entry being 64

Seems to be the case, though it is incredibly non-obvious.

Is there a better way to have that assert run than to check we'd have
already run the OOB access?

- Derek
diff mbox

Patch

diff --git a/libavcodec/dvenc.c b/libavcodec/dvenc.c
index ce2fc75daa..b79cbebb04 100644
--- a/libavcodec/dvenc.c
+++ b/libavcodec/dvenc.c
@@ -383,7 +383,7 @@  static inline void dv_guess_qnos(EncBlockInfo *blks, int *qnos)
                                 prev            = k;
                             } else {
                                 if (b->next[k] >= mb_area_start[a + 1] && b->next[k] < 64) {
-                                    for (a2 = a + 1; b->next[k] >= mb_area_start[a2 + 1]; a2++)
+                                    for (a2 = a + 1; a2 < 4 && b->next[k] >= mb_area_start[a2 + 1]; a2++)
                                         b->prev[a2] = prev;
                                     av_assert2(a2 < 4);
                                     av_assert2(b->mb[b->next[k]]);