[FFmpeg-devel] [PATCH] avcodec/cbs: Allocate more CodedBitstreamUnit at once in cbs_insert_unit()

James Almer jamrial at gmail.com
Sat Apr 11 05:49:01 EEST 2020


On 4/10/2020 9:00 PM, James Almer wrote:
> On 4/10/2020 8:53 PM, Michael Niedermayer wrote:
>> On Fri, Apr 10, 2020 at 05:44:25PM -0300, James Almer wrote:
>>> On 4/10/2020 5:23 PM, Michael Niedermayer wrote:
>>>> Fixes: Timeout (85sec -> 0.5sec)
>>>> Fixes: 20791/clusterfuzz-testcase-minimized-ffmpeg_BSF_AV1_FRAME_SPLIT_fuzzer-5659537719951360
>>>> Fixes: 21214/clusterfuzz-testcase-minimized-ffmpeg_BSF_MPEG2_METADATA_fuzzer-5165560875974656
>>>> Fixes: 21247/clusterfuzz-testcase-minimized-ffmpeg_BSF_H264_METADATA_fuzzer-5715175257931776
>>>>
>>>> Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
>>>> Signed-off-by: Michael Niedermayer <michael at niedermayer.cc>
>>>> ---
>>>>  libavcodec/cbs.c | 4 ++--
>>>>  1 file changed, 2 insertions(+), 2 deletions(-)
>>>>
>>>> diff --git a/libavcodec/cbs.c b/libavcodec/cbs.c
>>>> index 0bd5e1ac5d..42cb9711fa 100644
>>>> --- a/libavcodec/cbs.c
>>>> +++ b/libavcodec/cbs.c
>>>> @@ -693,11 +693,11 @@ static int cbs_insert_unit(CodedBitstreamContext *ctx,
>>>>              memmove(units + position + 1, units + position,
>>>>                      (frag->nb_units - position) * sizeof(*units));
>>>>      } else {
>>>> -        units = av_malloc_array(frag->nb_units + 1, sizeof(*units));
>>>> +        units = av_malloc_array(frag->nb_units*2 + 1, sizeof(*units));
>>>>          if (!units)
>>>>              return AVERROR(ENOMEM);
>>>>  
>>>> -        ++frag->nb_units_allocated;
>>>> +        frag->nb_units_allocated = 2*frag->nb_units_allocated + 1;
>>>
>>> Use ff_fast_malloc(), please. This is quite ugly and the *2 undocumented
>>> and not obvious.
>>
>> not sure i understood your suggestion correctly but 
>> ff_fast_malloc() deallocates its buffer and clears the size on error, 
>> which cbs_insert_unit() does not do.
>> so using ff_fast_malloc() feels a bit like fitting a square peg in a round
>> hole. But it works too
>> is that what you had in mind ? (your comment sounded like something simpler ...)
>> so maybe iam missing something
> 
> No, after thinking about it i realized it was not the best option and i
> sent a follow up reply about it, but i guess it was too late. If you
> have to change the implementation of ff_fast_malloc() then it's clearly
> not what should be used here. I didn't intend you to do this much work.
> 
> av_fast_realloc() could work instead as i mentioned in the follow up
> reply, i think. Or porting this to AVTreeNode instead of a flat array,
> but that's also quite a bit of work and will still allocate one node per
> call, so your fuzzer cases may still timeout. So if av_fast_realloc() is
> also not an option then maybe increase the buffer by a
> small-but-bigger-than-1 amount of units instead of duplicating its size
> each call, which can get quite big pretty fast.

Here's a version using av_fast_realloc(). FATE passes. Does it solve the
timeout?

diff --git a/libavcodec/cbs.c b/libavcodec/cbs.c
index 0bd5e1ac5d..d6cb94589f 100644
--- a/libavcodec/cbs.c
+++ b/libavcodec/cbs.c
@@ -161,6 +161,7 @@ void ff_cbs_fragment_free(CodedBitstreamContext *ctx,

     av_freep(&frag->units);
     frag->nb_units_allocated = 0;
+    frag->unit_buffer_size = 0;
 }

 static int cbs_read_fragment_content(CodedBitstreamContext *ctx,
@@ -684,35 +685,29 @@ static int cbs_insert_unit(CodedBitstreamContext *ctx,
                            CodedBitstreamFragment *frag,
                            int position)
 {
-    CodedBitstreamUnit *units;
+    CodedBitstreamUnit *units = frag->units;

-    if (frag->nb_units < frag->nb_units_allocated) {
-        units = frag->units;
+    if (frag->nb_units >= frag->nb_units_allocated) {
+        size_t new_size;
+        void *tmp;

-        if (position < frag->nb_units)
-            memmove(units + position + 1, units + position,
-                    (frag->nb_units - position) * sizeof(*units));
-    } else {
-        units = av_malloc_array(frag->nb_units + 1, sizeof(*units));
-        if (!units)
+        if (av_size_mult(frag->nb_units + 1, sizeof(*units), &new_size))
             return AVERROR(ENOMEM);

-        ++frag->nb_units_allocated;
-
-        if (position > 0)
-            memcpy(units, frag->units, position * sizeof(*units));
+        tmp = av_fast_realloc(units, &frag->unit_buffer_size,
+                              new_size * sizeof(*units));
+        if (!tmp)
+            return AVERROR(ENOMEM);

-        if (position < frag->nb_units)
-            memcpy(units + position + 1, frag->units + position,
-                   (frag->nb_units - position) * sizeof(*units));
+        frag->units = units = tmp;
+        ++frag->nb_units_allocated;
     }

-    memset(units + position, 0, sizeof(*units));
+    if (position < frag->nb_units)
+        memmove(units + position + 1, units + position,
+                (frag->nb_units - position) * sizeof(*units));

-    if (units != frag->units) {
-        av_free(frag->units);
-        frag->units = units;
-    }
+    memset(units + position, 0, sizeof(*units));

     ++frag->nb_units;

diff --git a/libavcodec/cbs.h b/libavcodec/cbs.h
index 9ca1fbd609..4833a8a3db 100644
--- a/libavcodec/cbs.h
+++ b/libavcodec/cbs.h
@@ -153,6 +153,13 @@ typedef struct CodedBitstreamFragment {
      */
      int             nb_units_allocated;

+    /**
+     * Size of allocated unit buffer.
+     *
+     * Must always be >= nb_units_allocated; designed for internal use
by cbs.
+     */
+     unsigned int    unit_buffer_size;
+
     /**
      * Pointer to an array of units of length nb_units_allocated.
      * Only the first nb_units are valid.


More information about the ffmpeg-devel mailing list