diff mbox series

[FFmpeg-devel,03/25] avfilter/af_headphone: Check for the existence of samples

Message ID 20200908211856.16290-3-andreas.rheinhardt@gmail.com
State Accepted
Commit dfd46e2d160afcb7e453d0e2394a6978cb447712
Headers show
Series [FFmpeg-devel,01/25] avfilter/af_headphone: Don't use uninitialized buffer in log message | expand

Checks

Context Check Description
andriy/default pending
andriy/make success Make finished
andriy/make_fate success Make fate finished

Commit Message

Andreas Rheinhardt Sept. 8, 2020, 9:18 p.m. UTC
Not providing any samples makes no sense at all. And if no samples
were provided for one of the HRIR streams, one would either run into
an av_assert1 in ff_inlink_consume_samples() or into a segfault in
take_samples() in avfilter.c.

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@gmail.com>
---
 libavfilter/af_headphone.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

Comments

Nicolas George Sept. 9, 2020, 2:27 p.m. UTC | #1
Andreas Rheinhardt (12020-09-08):
> Not providing any samples makes no sense at all. And if no samples
> were provided for one of the HRIR streams, one would either run into
> an av_assert1 in ff_inlink_consume_samples() or into a segfault in
> take_samples() in avfilter.c.
> 
> Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@gmail.com>
> ---
>  libavfilter/af_headphone.c | 8 +++++++-
>  1 file changed, 7 insertions(+), 1 deletion(-)
> 
> diff --git a/libavfilter/af_headphone.c b/libavfilter/af_headphone.c
> index 1024ff57b1..f488e0e28d 100644
> --- a/libavfilter/af_headphone.c
> +++ b/libavfilter/af_headphone.c
> @@ -631,8 +631,14 @@ static int activate(AVFilterContext *ctx)
>              if ((ret = check_ir(ctx->inputs[i], i)) < 0)
>                  return ret;
>  
> -                if (ff_outlink_get_status(ctx->inputs[i]) == AVERROR_EOF)
> +            if (ff_outlink_get_status(ctx->inputs[i]) == AVERROR_EOF) {
> +                if (!ff_inlink_queued_samples(ctx->inputs[i])) {
> +                    av_log(ctx, AV_LOG_ERROR, "No samples provided for "
> +                           "HRIR stream %d.\n", i - 1);
> +                    return AVERROR_INVALIDDATA;
> +                }
>                      s->in[i].eof = 1;
> +            }

No, this is bogus. A filter should not call ff_outlink_get_status() on
its input, that is breaking the abstraction provided by the API. In
fact, everything in `git grep 'ff_outlink_get_status.*input'` is a bug.

>          }
>  
>          for (i = 1; i < s->nb_inputs; i++) {

Regards,
Andreas Rheinhardt Sept. 9, 2020, 2:59 p.m. UTC | #2
Nicolas George:
> Andreas Rheinhardt (12020-09-08):
>> Not providing any samples makes no sense at all. And if no samples
>> were provided for one of the HRIR streams, one would either run into
>> an av_assert1 in ff_inlink_consume_samples() or into a segfault in
>> take_samples() in avfilter.c.
>>
>> Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@gmail.com>
>> ---
>>  libavfilter/af_headphone.c | 8 +++++++-
>>  1 file changed, 7 insertions(+), 1 deletion(-)
>>
>> diff --git a/libavfilter/af_headphone.c b/libavfilter/af_headphone.c
>> index 1024ff57b1..f488e0e28d 100644
>> --- a/libavfilter/af_headphone.c
>> +++ b/libavfilter/af_headphone.c
>> @@ -631,8 +631,14 @@ static int activate(AVFilterContext *ctx)
>>              if ((ret = check_ir(ctx->inputs[i], i)) < 0)
>>                  return ret;
>>  
>> -                if (ff_outlink_get_status(ctx->inputs[i]) == AVERROR_EOF)
>> +            if (ff_outlink_get_status(ctx->inputs[i]) == AVERROR_EOF) {
>> +                if (!ff_inlink_queued_samples(ctx->inputs[i])) {
>> +                    av_log(ctx, AV_LOG_ERROR, "No samples provided for "
>> +                           "HRIR stream %d.\n", i - 1);
>> +                    return AVERROR_INVALIDDATA;
>> +                }
>>                      s->in[i].eof = 1;
>> +            }
> 
> No, this is bogus. A filter should not call ff_outlink_get_status() on
> its input, that is breaking the abstraction provided by the API. In
> fact, everything in `git grep 'ff_outlink_get_status.*input'` is a bug.
> 

How should one then check whether an input is finished? Check for
ff_inlink_check_available_samples(ctx->inputs[i],
ff_inlink_queued_samples(ctx->inputs[i]) + 1) being 1?

- Andreas
Nicolas George Sept. 9, 2020, 3:03 p.m. UTC | #3
Andreas Rheinhardt (12020-09-09):
> How should one then check whether an input is finished? Check for
> ff_inlink_check_available_samples(ctx->inputs[i],
> ff_inlink_queued_samples(ctx->inputs[i]) + 1) being 1?

No, these tell you what can be obtained immediately.

The test for a finished input is ff_inlink_acknowledge_status().

The need for this test is probably the sign of a flawed logic: the
filter is trying to anticipate things when it should just be organized
to do as necessary.

Regards,
Andreas Rheinhardt Sept. 9, 2020, 3:18 p.m. UTC | #4
Nicolas George:
> Andreas Rheinhardt (12020-09-09):
>> How should one then check whether an input is finished? Check for
>> ff_inlink_check_available_samples(ctx->inputs[i],
>> ff_inlink_queued_samples(ctx->inputs[i]) + 1) being 1?
> 
> No, these tell you what can be obtained immediately.
> 

The documentation of ff_inlink_check_available_samples() contains the
note "on EOF and error, min becomes 1". Which means that
ff_inlink_check_available_samples() can be used to peek into the error
code of this inlink by setting min so high that the not enough samples
can be available. You seem to want to forbid it absolutely that a filter
knows whether there will be more data forthcoming after the data that is
already queued, so I am surprised that you have not also closed this
loophole in your patch just now.

> The test for a finished input is ff_inlink_acknowledge_status().
> 
> The need for this test is probably the sign of a flawed logic: the
> filter is trying to anticipate things when it should just be organized
> to do as necessary.
> 
For this filter to work it is necessary to know whether all inputs
except input 0 are finished; it can only start when they are finished.

- Andreas
Nicolas George Sept. 9, 2020, 3:29 p.m. UTC | #5
Andreas Rheinhardt (12020-09-09):
> The documentation of ff_inlink_check_available_samples() contains the
> note "on EOF and error, min becomes 1". Which means that
> ff_inlink_check_available_samples() can be used to peek into the error
> code of this inlink by setting min so high that the not enough samples
> can be available. You seem to want to forbid it absolutely that a filter
> knows whether there will be more data forthcoming after the data that is
> already queued, so I am surprised that you have not also closed this
> loophole in your patch just now.

It can be used to do that, but a filter that uses it is doing something
very wrong. But in the meantime, ff_inlink_check_available_samples() is
doing exactly what it is supposed to do, i.e. predict that
ff_inlink_consume_samples() will succeed.

I do not want to "close loopholes" because we are not in an adversarial
situation.

> For this filter to work it is necessary to know whether all inputs
> except input 0 are finished; it can only start when they are finished.

Then it is easy: the filter should consume the samples on its input, if
there are none try to acknowledge the status change: if the status has
changed, the input is finished. Checking beforehand is quite
unnecessary.

Regards,
diff mbox series

Patch

diff --git a/libavfilter/af_headphone.c b/libavfilter/af_headphone.c
index 1024ff57b1..f488e0e28d 100644
--- a/libavfilter/af_headphone.c
+++ b/libavfilter/af_headphone.c
@@ -631,8 +631,14 @@  static int activate(AVFilterContext *ctx)
             if ((ret = check_ir(ctx->inputs[i], i)) < 0)
                 return ret;
 
-                if (ff_outlink_get_status(ctx->inputs[i]) == AVERROR_EOF)
+            if (ff_outlink_get_status(ctx->inputs[i]) == AVERROR_EOF) {
+                if (!ff_inlink_queued_samples(ctx->inputs[i])) {
+                    av_log(ctx, AV_LOG_ERROR, "No samples provided for "
+                           "HRIR stream %d.\n", i - 1);
+                    return AVERROR_INVALIDDATA;
+                }
                     s->in[i].eof = 1;
+            }
         }
 
         for (i = 1; i < s->nb_inputs; i++) {