diff mbox

[FFmpeg-devel,V2] avutil/tx: add check against (*ctx)

Message ID 20190516050217.26004-1-ruiling.song@intel.com
State New
Headers show

Commit Message

Ruiling Song May 16, 2019, 5:02 a.m. UTC
ctx is a pointer to pointer here.

Signed-off-by: Ruiling Song <ruiling.song@intel.com>
---
 libavutil/tx.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Comments

Nicolas George May 16, 2019, 8:03 a.m. UTC | #1
Ruiling Song (12019-05-16):
> ctx is a pointer to pointer here.
> 
> Signed-off-by: Ruiling Song <ruiling.song@intel.com>
> ---
>  libavutil/tx.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/libavutil/tx.c b/libavutil/tx.c
> index 934ef27c81..1690604040 100644
> --- a/libavutil/tx.c
> +++ b/libavutil/tx.c
> @@ -697,7 +697,7 @@ static int gen_mdct_exptab(AVTXContext *s, int len4, double scale)
>  
>  av_cold void av_tx_uninit(AVTXContext **ctx)
>  {

> -    if (!ctx)
> +    if (!ctx || !(*ctx))

That would protect somebody stupid enough to call av_tx_uninit(NULL)
instead of av_tx_uninit(&var). A hard crass is completely warranted in
this case. An assert would be acceptable.

>          return;
>  
>      av_free((*ctx)->pfatab);

Regards,
Lynne May 16, 2019, 10:34 a.m. UTC | #2
May 16, 2019, 6:02 AM by ruiling.song@intel.com:

> ctx is a pointer to pointer here.
>
> Signed-off-by: Ruiling Song <> ruiling.song@intel.com <mailto:ruiling.song@intel.com>> >
> ---
>  libavutil/tx.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/libavutil/tx.c b/libavutil/tx.c
> index 934ef27c81..1690604040 100644
> --- a/libavutil/tx.c
> +++ b/libavutil/tx.c
> @@ -697,7 +697,7 @@ static int gen_mdct_exptab(AVTXContext *s, int len4, double scale)
>  
>  av_cold void av_tx_uninit(AVTXContext **ctx)
>  {
> -    if (!ctx)
> +    if (!ctx || !(*ctx))
>  return;
>  
>  av_free((*ctx)->pfatab);
>

LGTM
Nicolas George May 16, 2019, 10:35 a.m. UTC | #3
Lynne (12019-05-16):
> LGTM

As I said, not to me. Please take all comments into account.

Regards,
John Cox May 16, 2019, 10:54 a.m. UTC | #4
>Ruiling Song (12019-05-16):
>> ctx is a pointer to pointer here.
>> 
>> Signed-off-by: Ruiling Song <ruiling.song@intel.com>
>> ---
>>  libavutil/tx.c | 2 +-
>>  1 file changed, 1 insertion(+), 1 deletion(-)
>> 
>> diff --git a/libavutil/tx.c b/libavutil/tx.c
>> index 934ef27c81..1690604040 100644
>> --- a/libavutil/tx.c
>> +++ b/libavutil/tx.c
>> @@ -697,7 +697,7 @@ static int gen_mdct_exptab(AVTXContext *s, int len4, double scale)
>>  
>>  av_cold void av_tx_uninit(AVTXContext **ctx)
>>  {
>
>> -    if (!ctx)
>> +    if (!ctx || !(*ctx))
>
>That would protect somebody stupid enough to call av_tx_uninit(NULL)
>instead of av_tx_uninit(&var). A hard crass is completely warranted in
>this case. An assert would be acceptable.

Actually that is what the original code does.  What you appear to want
is

  if (!*ctx)

which protects against multi-free and is useful in that it can be called
unconditionally in cleanup code (assuming initial null assignments) and
crashes in what you describe as the "stupid" case.

>>          return;
>>  
>>      av_free((*ctx)->pfatab);
>
>Regards,

Regards

John Cox
Lynne May 16, 2019, 10:57 a.m. UTC | #5
May 16, 2019, 11:35 AM by george@nsup.org:

> Lynne (12019-05-16):
>
>> LGTM
>>
>
> As I said, not to me. Please take all comments into account.
>

Its fine by me. Adding an AVClass is unwarranted and absolutely no standalone API
we've ever used like av_fft or av_has has a class.
James Almer May 16, 2019, 2:28 p.m. UTC | #6
On 5/16/2019 5:03 AM, Nicolas George wrote:
> Ruiling Song (12019-05-16):
>> ctx is a pointer to pointer here.
>>
>> Signed-off-by: Ruiling Song <ruiling.song@intel.com>
>> ---
>>  libavutil/tx.c | 2 +-
>>  1 file changed, 1 insertion(+), 1 deletion(-)
>>
>> diff --git a/libavutil/tx.c b/libavutil/tx.c
>> index 934ef27c81..1690604040 100644
>> --- a/libavutil/tx.c
>> +++ b/libavutil/tx.c
>> @@ -697,7 +697,7 @@ static int gen_mdct_exptab(AVTXContext *s, int len4, double scale)
>>  
>>  av_cold void av_tx_uninit(AVTXContext **ctx)
>>  {
> 
>> -    if (!ctx)
>> +    if (!ctx || !(*ctx))
> 
> That would protect somebody stupid enough to call av_tx_uninit(NULL)
> instead of av_tx_uninit(&var). A hard crass is completely warranted in
> this case. An assert would be acceptable.

An assert is meant to detect developer errors, not user errors. Crashing
the user's whole application because they misused the API is not really
acceptable.

I can't find examples of such functions using asserts this way, but
there are several uninit/free/unref functions behave like the above
patch. See av_buffer_unref(), av_packet_free(), av_bsf_free().
Other functions instead just don't even consider the passed argument
could be NULL at all, like avcodec_free_context() and swr_free(), which
while not 100% safe, is a pretty realistic expectation.

I'd say either apply this patch as is, or apply the original one sent
last night. In both cases it will be following an existing precedent in
the codebase.

> 
>>          return;
>>  
>>      av_free((*ctx)->pfatab);
> 
> Regards,
> 
> 
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> 
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>
Nicolas George May 16, 2019, 2:30 p.m. UTC | #7
John Cox (12019-05-16):
> >> -    if (!ctx)
> >> +    if (!ctx || !(*ctx))
> >That would protect somebody stupid enough to call av_tx_uninit(NULL)
> >instead of av_tx_uninit(&var). A hard crass is completely warranted in
> >this case. An assert would be acceptable.
> Actually that is what the original code does.  What you appear to want
> is
> 
>   if (!*ctx)

Indeed.

It is for example what avfilter_link_free(), av_bsf_list_free(), etc.,
do. It correspond to a useful programming pattern. The current code is
clearly a mistake.

Regards,
Nicolas George May 16, 2019, 2:31 p.m. UTC | #8
James Almer (12019-05-16):
> An assert is meant to detect developer errors, not user errors. Crashing
> the user's whole application because they misused the API is not really
> acceptable.
> 
> I can't find examples of such functions using asserts this way, but
> there are several uninit/free/unref functions behave like the above
> patch. See av_buffer_unref(), av_packet_free(), av_bsf_free().
> Other functions instead just don't even consider the passed argument
> could be NULL at all, like avcodec_free_context() and swr_free(), which
> while not 100% safe, is a pretty realistic expectation.
> 
> I'd say either apply this patch as is, or apply the original one sent
> last night. In both cases it will be following an existing precedent in
> the codebase.

Re-read the code more carefully, and look what the existing predecent
does. You made the same mistake as me.

Regards,
James Almer May 16, 2019, 2:49 p.m. UTC | #9
On 5/16/2019 11:31 AM, Nicolas George wrote:
> James Almer (12019-05-16):
>> An assert is meant to detect developer errors, not user errors. Crashing
>> the user's whole application because they misused the API is not really
>> acceptable.
>>
>> I can't find examples of such functions using asserts this way, but
>> there are several uninit/free/unref functions behave like the above
>> patch. See av_buffer_unref(), av_packet_free(), av_bsf_free().
>> Other functions instead just don't even consider the passed argument
>> could be NULL at all, like avcodec_free_context() and swr_free(), which
>> while not 100% safe, is a pretty realistic expectation.
>>
>> I'd say either apply this patch as is, or apply the original one sent
>> last night. In both cases it will be following an existing precedent in
>> the codebase.
> 
> Re-read the code more carefully, and look what the existing predecent
> does. You made the same mistake as me.
> 
> Regards,

There are two precedents in the codebase, one checking for both the
passed argument and then the struct pointer pointed by it (av_bsf_free
and av_buffer_unref as i mentioned above), and one checking only the
struct pointer (av_bsf_list_free as you mentioned in another email).

The current code is wrong as already established. This patch applying
(!ctx || !*ctx), and the one sent last night applying (!*ctx) are both
correct, each of them implementing one of the two aforementioned
precedents. The former just takes extra precautions against user error.
Nicolas George May 16, 2019, 6:11 p.m. UTC | #10
James Almer (12019-05-16):
> There are two precedents in the codebase, one checking for both the
> passed argument and then the struct pointer pointed by it (av_bsf_free
> and av_buffer_unref as i mentioned above), and one checking only the
> struct pointer (av_bsf_list_free as you mentioned in another email).

There are a few other precedent. I think the majority dereference the
pointer and only accept indirect NULL. This is not surprising: freeing
something unconditionally is very useful when uniniting after a failure
case or different branches of programming. On the other hand, freeing
nothing is not a useful pattern.

> The current code is wrong as already established. This patch applying
> (!ctx || !*ctx), and the one sent last night applying (!*ctx) are both
> correct, each of them implementing one of the two aforementioned

Ok, I had missed there was another patch.

> precedents. The former just takes extra precautions against user error.

Not user: application programmer. This is a very important difference:
users are supposed clueless, application programmers are supposed
competent, even when they are not.

That kind of mistake in using the API is probably a simple but grave
mistake in the code, like writing av_foo_free(foo) instead of
av_foo_free(&foo) (and ignoring the resulting warning). If ignored, it
will lead to invalid frees or memory leaks in the application. This is
not a service for the programmer: better have the program crash hard
(either with an assert or a NULL deref) immediately, so they can fix it.

Regards,
James Almer May 16, 2019, 6:22 p.m. UTC | #11
On 5/16/2019 3:11 PM, Nicolas George wrote:
> James Almer (12019-05-16):
>> There are two precedents in the codebase, one checking for both the
>> passed argument and then the struct pointer pointed by it (av_bsf_free
>> and av_buffer_unref as i mentioned above), and one checking only the
>> struct pointer (av_bsf_list_free as you mentioned in another email).
> 
> There are a few other precedent. I think the majority dereference the
> pointer and only accept indirect NULL. This is not surprising: freeing
> something unconditionally is very useful when uniniting after a failure
> case or different branches of programming. On the other hand, freeing
> nothing is not a useful pattern.
> 
>> The current code is wrong as already established. This patch applying
>> (!ctx || !*ctx), and the one sent last night applying (!*ctx) are both
>> correct, each of them implementing one of the two aforementioned
> 
> Ok, I had missed there was another patch.
> 
>> precedents. The former just takes extra precautions against user error.
> 
> Not user: application programmer. This is a very important difference:
> users are supposed clueless, application programmers are supposed
> competent, even when they are not.

I meant it as library user, which is the application programmer and not
the consumer/end user, but yes, you're right they should know better.

> 
> That kind of mistake in using the API is probably a simple but grave
> mistake in the code, like writing av_foo_free(foo) instead of
> av_foo_free(&foo) (and ignoring the resulting warning). If ignored, it
> will lead to invalid frees or memory leaks in the application. This is
> not a service for the programmer: better have the program crash hard
> (either with an assert or a NULL deref) immediately, so they can fix it.
> 
> Regards,
In that case I'm fine with using the single (!*ctx) check from the first
patch.
Lynne May 16, 2019, 7:41 p.m. UTC | #12
May 16, 2019, 7:22 PM by jamrial@gmail.com:

> On 5/16/2019 3:11 PM, Nicolas George wrote:
>
>> James Almer (12019-05-16):
>>
>>> There are two precedents in the codebase, one checking for both the
>>> passed argument and then the struct pointer pointed by it (av_bsf_free
>>> and av_buffer_unref as i mentioned above), and one checking only the
>>> struct pointer (av_bsf_list_free as you mentioned in another email).
>>>
>>
>> There are a few other precedent. I think the majority dereference the
>> pointer and only accept indirect NULL. This is not surprising: freeing
>> something unconditionally is very useful when uniniting after a failure
>> case or different branches of programming. On the other hand, freeing
>> nothing is not a useful pattern.
>>
>>> The current code is wrong as already established. This patch applying
>>> (!ctx || !*ctx), and the one sent last night applying (!*ctx) are both
>>> correct, each of them implementing one of the two aforementioned
>>>
>>
>> Ok, I had missed there was another patch.
>>
>>> precedents. The former just takes extra precautions against user error.
>>>
>>
>> Not user: application programmer. This is a very important difference:
>> users are supposed clueless, application programmers are supposed
>> competent, even when they are not.
>>
>
> I meant it as library user, which is the application programmer and not
> the consumer/end user, but yes, you're right they should know better.
>
>>
>> That kind of mistake in using the API is probably a simple but grave
>> mistake in the code, like writing av_foo_free(foo) instead of
>> av_foo_free(&foo) (and ignoring the resulting warning). If ignored, it
>> will lead to invalid frees or memory leaks in the application. This is
>> not a service for the programmer: better have the program crash hard
>> (either with an assert or a NULL deref) immediately, so they can fix it.
>>
>> Regards,
>>
> In that case I'm fine with using the single (!*ctx) check from the first
> patch.
>

I'm not, I still want the 2 checks.
Nicolas George May 16, 2019, 7:43 p.m. UTC | #13
Lynne (12019-05-16):
> I'm not, I still want the 2 checks.

Arguments please. As I explained, the first check is harmful to
applications because it hides bug.

Regards,
Lynne May 16, 2019, 9:06 p.m. UTC | #14
May 16, 2019, 8:43 PM by george@nsup.org:

> Lynne (12019-05-16):
>
>> I'm not, I still want the 2 checks.
>>
>
> Arguments please. As I explained, the first check is harmful to
> applications because it hides bug.
>

Nevermind, its not really possible to give av_tx_uninit() a NULL double pointer so
whatever. Can the author just push a patch already?
James Almer May 16, 2019, 9:29 p.m. UTC | #15
On 5/16/2019 6:06 PM, Lynne wrote:
> May 16, 2019, 8:43 PM by george@nsup.org:
> 
>> Lynne (12019-05-16):
>>
>>> I'm not, I still want the 2 checks.
>>>
>>
>> Arguments please. As I explained, the first check is harmful to
>> applications because it hides bug.
>>
> 
> Nevermind, its not really possible to give av_tx_uninit() a NULL double pointer so
> whatever. Can the author just push a patch already?

I did it just now, but as the author and maintainer of the code in
question you're the most adequate person to review and push patches for it.
Lynne May 16, 2019, 9:34 p.m. UTC | #16
May 16, 2019, 10:29 PM by jamrial@gmail.com:

> On 5/16/2019 6:06 PM, Lynne wrote:
>
>> May 16, 2019, 8:43 PM by >> george@nsup.org <mailto:george@nsup.org>>> :
>>
>>> Lynne (12019-05-16):
>>>
>>>> I'm not, I still want the 2 checks.
>>>>
>>>
>>> Arguments please. As I explained, the first check is harmful to
>>> applications because it hides bug.
>>>
>>
>> Nevermind, its not really possible to give av_tx_uninit() a NULL double pointer so
>> whatever. Can the author just push a patch already?
>>
>
> I did it just now, but as the author and maintainer of the code in
> question you're the most adequate person to review and push patches for it.
>

Yes but the submitter has push access so I thought it best to leave it up to them to push.
Ruiling Song May 17, 2019, 1:02 a.m. UTC | #17
> -----Original Message-----

> From: ffmpeg-devel [mailto:ffmpeg-devel-bounces@ffmpeg.org] On Behalf

> Of James Almer

> Sent: Friday, May 17, 2019 5:30 AM

> To: ffmpeg-devel@ffmpeg.org

> Subject: Re: [FFmpeg-devel] [PATCH V2] avutil/tx: add check against (*ctx)

> 

> On 5/16/2019 6:06 PM, Lynne wrote:

> > May 16, 2019, 8:43 PM by george@nsup.org:

> >

> >> Lynne (12019-05-16):

> >>

> >>> I'm not, I still want the 2 checks.

> >>>

> >>

> >> Arguments please. As I explained, the first check is harmful to

> >> applications because it hides bug.

> >>

> >

> > Nevermind, its not really possible to give av_tx_uninit() a NULL double

> pointer so

> > whatever. Can the author just push a patch already?

> 

> I did it just now, but as the author and maintainer of the code in

> question you're the most adequate person to review and push patches for it.

After reading the discussion, I agree to use v1 to just crash if null pointer passed in and thanks James for approve and push the patch. And thank you all for the active discussion:)

Ruiling
> _______________________________________________

> ffmpeg-devel mailing list

> ffmpeg-devel@ffmpeg.org

> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

> 

> To unsubscribe, visit link above, or email

> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
diff mbox

Patch

diff --git a/libavutil/tx.c b/libavutil/tx.c
index 934ef27c81..1690604040 100644
--- a/libavutil/tx.c
+++ b/libavutil/tx.c
@@ -697,7 +697,7 @@  static int gen_mdct_exptab(AVTXContext *s, int len4, double scale)
 
 av_cold void av_tx_uninit(AVTXContext **ctx)
 {
-    if (!ctx)
+    if (!ctx || !(*ctx))
         return;
 
     av_free((*ctx)->pfatab);