diff mbox series

[FFmpeg-devel,1/2,v2] avformat/avisynth: remove atexit() handler

Message ID 20240719165627.5490-1-qyot27@gmail.com
State New
Headers show
Series [FFmpeg-devel,1/2,v2] avformat/avisynth: remove atexit() handler | expand

Commit Message

Stephen Hutchinson July 19, 2024, 4:56 p.m. UTC
The atexit() handler in the avisynth demuxer was added because
there was a conflict in AvxSynth that arose due to their use
of C++ global objects, particularly in relation to having
added a logging function relying on log4cpp.

This conflict was responsible for causing a segfault on exit.
It did not affect Windows with the (at the time) upstream
AviSynth 2.5 and 2.6, nor does it affect AviSynth+.

Unfortunately, none of this was actually shielded by ifdefs
indicating the fact it was only needed for AvxSynth, so four
years ago when AviSynth+ replaced AvxSynth as the handler
for AviSynth scripts on Unix-like OSes, the fact that the
atexit handler was no longer necessary was overlooked.

Signed-off-by: Stephen Hutchinson <qyot27@gmail.com>
---
Changes compared to v1:
* Added missing dlclose invocation to read_close

 libavformat/avisynth.c | 46 +-----------------------------------------
 1 file changed, 1 insertion(+), 45 deletions(-)

Comments

Stephen Hutchinson July 28, 2024, 5:37 a.m. UTC | #1
On 7/19/24 12:56 PM, Stephen Hutchinson wrote:
> The atexit() handler in the avisynth demuxer was added because
> there was a conflict in AvxSynth that arose due to their use
> of C++ global objects, particularly in relation to having
> added a logging function relying on log4cpp.
> 
> This conflict was responsible for causing a segfault on exit.
> It did not affect Windows with the (at the time) upstream
> AviSynth 2.5 and 2.6, nor does it affect AviSynth+.
> 
> Unfortunately, none of this was actually shielded by ifdefs
> indicating the fact it was only needed for AvxSynth, so four
> years ago when AviSynth+ replaced AvxSynth as the handler
> for AviSynth scripts on Unix-like OSes, the fact that the
> atexit handler was no longer necessary was overlooked.
> 
> Signed-off-by: Stephen Hutchinson <qyot27@gmail.com>
> ---
> Changes compared to v1:
> * Added missing dlclose invocation to read_close
> 
>   libavformat/avisynth.c | 46 +-----------------------------------------
>   1 file changed, 1 insertion(+), 45 deletions(-)
> 
> diff --git a/libavformat/avisynth.c b/libavformat/avisynth.c
> index 625bdf7e3a..26747671c0 100644
> --- a/libavformat/avisynth.c
> +++ b/libavformat/avisynth.c
> @@ -115,9 +115,6 @@ typedef struct AviSynthContext {
>       int error;
>   
>       uint32_t flags;
> -
> -    /* Linked list pointers. */
> -    struct AviSynthContext *next;
>   } AviSynthContext;
>   
>   static const int avs_planes_packed[1] = { 0 };
> @@ -133,15 +130,7 @@ static const int avs_planes_rgba[4]   = { AVS_PLANAR_G, AVS_PLANAR_B,
>   
>   static AVMutex avisynth_mutex = AV_MUTEX_INITIALIZER;
>   
> -/* A conflict between C++ global objects, atexit, and dynamic loading requires
> - * us to register our own atexit handler to prevent double freeing. */
>   static AviSynthLibrary avs_library;
> -static int avs_atexit_called        = 0;
> -
> -/* Linked list of AviSynthContexts. An atexit handler destroys this list. */
> -static AviSynthContext *avs_ctx_list = NULL;
> -
> -static av_cold void avisynth_atexit_handler(void);
>   
>   static av_cold int avisynth_load_library(void)
>   {
> @@ -185,7 +174,6 @@ static av_cold int avisynth_load_library(void)
>       LOAD_AVS_FUNC(avs_get_env_property, 1);
>   #undef LOAD_AVS_FUNC
>   
> -    atexit(avisynth_atexit_handler);
>       return 0;
>   
>   fail:
> @@ -214,30 +202,11 @@ static av_cold int avisynth_context_create(AVFormatContext *s)
>           }
>       }
>   
> -    if (!avs_ctx_list) {
> -        avs_ctx_list = avs;
> -    } else {
> -        avs->next    = avs_ctx_list;
> -        avs_ctx_list = avs;
> -    }
> -
>       return 0;
>   }
>   
>   static av_cold void avisynth_context_destroy(AviSynthContext *avs)
>   {
> -    if (avs_atexit_called)
> -        return;
> -
> -    if (avs == avs_ctx_list) {
> -        avs_ctx_list = avs->next;
> -    } else {
> -        AviSynthContext *prev = avs_ctx_list;
> -        while (prev->next != avs)
> -            prev = prev->next;
> -        prev->next = avs->next;
> -    }
> -
>       if (avs->clip) {
>           avs_library.avs_release_clip(avs->clip);
>           avs->clip = NULL;
> @@ -248,20 +217,6 @@ static av_cold void avisynth_context_destroy(AviSynthContext *avs)
>       }
>   }
>   
> -static av_cold void avisynth_atexit_handler(void)
> -{
> -    AviSynthContext *avs = avs_ctx_list;
> -
> -    while (avs) {
> -        AviSynthContext *next = avs->next;
> -        avisynth_context_destroy(avs);
> -        avs = next;
> -    }
> -    dlclose(avs_library.library);
> -
> -    avs_atexit_called = 1;
> -}
> -
>   /* Create AVStream from audio and video data. */
>   static int avisynth_create_stream_video(AVFormatContext *s, AVStream *st)
>   {
> @@ -1134,6 +1089,7 @@ static av_cold int avisynth_read_close(AVFormatContext *s)
>           return AVERROR_UNKNOWN;
>   
>       avisynth_context_destroy(s->priv_data);
> +    dlclose(avs_library.library);
>       ff_mutex_unlock(&avisynth_mutex);
>       return 0;
>   }

Since there's not been any more feedback, I'll push both of these
refined patches in a day or two.
Ramiro Polla July 28, 2024, 1:38 p.m. UTC | #2
On Fri, Jul 19, 2024 at 7:51 PM Stephen Hutchinson <qyot27@gmail.com> wrote:
>
> The atexit() handler in the avisynth demuxer was added because
> there was a conflict in AvxSynth that arose due to their use
> of C++ global objects, particularly in relation to having
> added a logging function relying on log4cpp.
>
> This conflict was responsible for causing a segfault on exit.
> It did not affect Windows with the (at the time) upstream
> AviSynth 2.5 and 2.6, nor does it affect AviSynth+.
>
> Unfortunately, none of this was actually shielded by ifdefs
> indicating the fact it was only needed for AvxSynth, so four
> years ago when AviSynth+ replaced AvxSynth as the handler
> for AviSynth scripts on Unix-like OSes, the fact that the
> atexit handler was no longer necessary was overlooked.
>
> Signed-off-by: Stephen Hutchinson <qyot27@gmail.com>
> ---
> Changes compared to v1:
> * Added missing dlclose invocation to read_close
>
>  libavformat/avisynth.c | 46 +-----------------------------------------
>  1 file changed, 1 insertion(+), 45 deletions(-)
>
> diff --git a/libavformat/avisynth.c b/libavformat/avisynth.c
> index 625bdf7e3a..26747671c0 100644
> --- a/libavformat/avisynth.c
> +++ b/libavformat/avisynth.c
> @@ -115,9 +115,6 @@ typedef struct AviSynthContext {
>      int error;
>
>      uint32_t flags;
> -
> -    /* Linked list pointers. */
> -    struct AviSynthContext *next;
>  } AviSynthContext;
>
>  static const int avs_planes_packed[1] = { 0 };
> @@ -133,15 +130,7 @@ static const int avs_planes_rgba[4]   = { AVS_PLANAR_G, AVS_PLANAR_B,
>
>  static AVMutex avisynth_mutex = AV_MUTEX_INITIALIZER;
>
> -/* A conflict between C++ global objects, atexit, and dynamic loading requires
> - * us to register our own atexit handler to prevent double freeing. */
>  static AviSynthLibrary avs_library;
> -static int avs_atexit_called        = 0;
> -
> -/* Linked list of AviSynthContexts. An atexit handler destroys this list. */
> -static AviSynthContext *avs_ctx_list = NULL;
> -
> -static av_cold void avisynth_atexit_handler(void);
>
>  static av_cold int avisynth_load_library(void)
>  {
> @@ -185,7 +174,6 @@ static av_cold int avisynth_load_library(void)
>      LOAD_AVS_FUNC(avs_get_env_property, 1);
>  #undef LOAD_AVS_FUNC
>
> -    atexit(avisynth_atexit_handler);
>      return 0;
>
>  fail:
> @@ -214,30 +202,11 @@ static av_cold int avisynth_context_create(AVFormatContext *s)
>          }
>      }
>
> -    if (!avs_ctx_list) {
> -        avs_ctx_list = avs;
> -    } else {
> -        avs->next    = avs_ctx_list;
> -        avs_ctx_list = avs;
> -    }
> -
>      return 0;
>  }
>
>  static av_cold void avisynth_context_destroy(AviSynthContext *avs)
>  {
> -    if (avs_atexit_called)
> -        return;
> -
> -    if (avs == avs_ctx_list) {
> -        avs_ctx_list = avs->next;
> -    } else {
> -        AviSynthContext *prev = avs_ctx_list;
> -        while (prev->next != avs)
> -            prev = prev->next;
> -        prev->next = avs->next;
> -    }
> -
>      if (avs->clip) {
>          avs_library.avs_release_clip(avs->clip);
>          avs->clip = NULL;
> @@ -248,20 +217,6 @@ static av_cold void avisynth_context_destroy(AviSynthContext *avs)
>      }
>  }
>
> -static av_cold void avisynth_atexit_handler(void)
> -{
> -    AviSynthContext *avs = avs_ctx_list;
> -
> -    while (avs) {
> -        AviSynthContext *next = avs->next;
> -        avisynth_context_destroy(avs);
> -        avs = next;
> -    }
> -    dlclose(avs_library.library);
> -
> -    avs_atexit_called = 1;
> -}
> -
>  /* Create AVStream from audio and video data. */
>  static int avisynth_create_stream_video(AVFormatContext *s, AVStream *st)
>  {
> @@ -1134,6 +1089,7 @@ static av_cold int avisynth_read_close(AVFormatContext *s)
>          return AVERROR_UNKNOWN;
>
>      avisynth_context_destroy(s->priv_data);
> +    dlclose(avs_library.library);

Maybe it's best to wrap this around an if (avs_library.library).

[...]

Besides that, the patch looks ok.

Ramiro
Stephen Hutchinson July 28, 2024, 3:59 p.m. UTC | #3
On 7/28/24 9:38 AM, Ramiro Polla wrote:
>> @@ -1134,6 +1089,7 @@ static av_cold int avisynth_read_close(AVFormatContext *s)
>>           return AVERROR_UNKNOWN;
>>
>>       avisynth_context_destroy(s->priv_data);
>> +    dlclose(avs_library.library);
> 
> Maybe it's best to wrap this around an if (avs_library.library).
> 

True. I had tried toying around with C11 _Thread_local since we now
use C17 as the default std, and in that case there was what I presume
was a double free happening that required adding a check for whether
avs_library.library still existed.  As that hadn't been happening prior
to that test I didn't really think much of it, but yeah, it would
make sense to check it anyway.

I abandoned that general idea after 1) finding out that while GCC and
Clang are fine, MSVC doesn't seem to yet (or if it does, only the
most recent versions of MSVC 2022 do) 2) C23 renamed it to thread_local,
and most importantly, 3) I probably hadn't quite used it entirely
correctly, because while the script could be parsed and played back just
fine, trying to encode anything from it would segfault.
Stephen Hutchinson Aug. 11, 2024, 8:08 p.m. UTC | #4
On 7/28/24 11:59 AM, Stephen Hutchinson wrote:
> On 7/28/24 9:38 AM, Ramiro Polla wrote:
>>> @@ -1134,6 +1089,7 @@ static av_cold int 
>>> avisynth_read_close(AVFormatContext *s)
>>>           return AVERROR_UNKNOWN;
>>>
>>>       avisynth_context_destroy(s->priv_data);
>>> +    dlclose(avs_library.library);
>>
>> Maybe it's best to wrap this around an if (avs_library.library).
>>
> 
> True. I had tried toying around with C11 _Thread_local since we now
> use C17 as the default std, and in that case there was what I presume
> was a double free happening that required adding a check for whether
> avs_library.library still existed.  As that hadn't been happening prior
> to that test I didn't really think much of it, but yeah, it would
> make sense to check it anyway.
> 
> I abandoned that general idea after 1) finding out that while GCC and
> Clang are fine, MSVC doesn't seem to yet (or if it does, only the
> most recent versions of MSVC 2022 do) 2) C23 renamed it to thread_local,
> and most importantly, 3) I probably hadn't quite used it entirely
> correctly, because while the script could be parsed and played back just
> fine, trying to encode anything from it would segfault.
> 

I had been ready to push the first two patches two weeks ago, but
the addition of #s 3 and 4 delayed that so I could test a bit more.
I didn't run into any issues with the two newer patches either, so
I'll wait another couple days for any additional comments before
pushing the whole set.
Stephen Hutchinson Aug. 13, 2024, 7:19 p.m. UTC | #5
On 7/19/24 12:56 PM, Stephen Hutchinson wrote:
> The atexit() handler in the avisynth demuxer was added because
> there was a conflict in AvxSynth that arose due to their use
> of C++ global objects, particularly in relation to having
> added a logging function relying on log4cpp.
> 
> This conflict was responsible for causing a segfault on exit.
> It did not affect Windows with the (at the time) upstream
> AviSynth 2.5 and 2.6, nor does it affect AviSynth+.
> 
> Unfortunately, none of this was actually shielded by ifdefs
> indicating the fact it was only needed for AvxSynth, so four
> years ago when AviSynth+ replaced AvxSynth as the handler
> for AviSynth scripts on Unix-like OSes, the fact that the
> atexit handler was no longer necessary was overlooked.
> 
> Signed-off-by: Stephen Hutchinson <qyot27@gmail.com>
> ---
> Changes compared to v1:
> * Added missing dlclose invocation to read_close
> 
>   libavformat/avisynth.c | 46 +-----------------------------------------
>   1 file changed, 1 insertion(+), 45 deletions(-)
> 
> diff --git a/libavformat/avisynth.c b/libavformat/avisynth.c
> index 625bdf7e3a..26747671c0 100644
> --- a/libavformat/avisynth.c
> +++ b/libavformat/avisynth.c
> @@ -115,9 +115,6 @@ typedef struct AviSynthContext {
>       int error;
>   
>       uint32_t flags;
> -
> -    /* Linked list pointers. */
> -    struct AviSynthContext *next;
>   } AviSynthContext;
>   
>   static const int avs_planes_packed[1] = { 0 };
> @@ -133,15 +130,7 @@ static const int avs_planes_rgba[4]   = { AVS_PLANAR_G, AVS_PLANAR_B,
>   
>   static AVMutex avisynth_mutex = AV_MUTEX_INITIALIZER;
>   
> -/* A conflict between C++ global objects, atexit, and dynamic loading requires
> - * us to register our own atexit handler to prevent double freeing. */
>   static AviSynthLibrary avs_library;
> -static int avs_atexit_called        = 0;
> -
> -/* Linked list of AviSynthContexts. An atexit handler destroys this list. */
> -static AviSynthContext *avs_ctx_list = NULL;
> -
> -static av_cold void avisynth_atexit_handler(void);
>   
>   static av_cold int avisynth_load_library(void)
>   {
> @@ -185,7 +174,6 @@ static av_cold int avisynth_load_library(void)
>       LOAD_AVS_FUNC(avs_get_env_property, 1);
>   #undef LOAD_AVS_FUNC
>   
> -    atexit(avisynth_atexit_handler);
>       return 0;
>   
>   fail:
> @@ -214,30 +202,11 @@ static av_cold int avisynth_context_create(AVFormatContext *s)
>           }
>       }
>   
> -    if (!avs_ctx_list) {
> -        avs_ctx_list = avs;
> -    } else {
> -        avs->next    = avs_ctx_list;
> -        avs_ctx_list = avs;
> -    }
> -
>       return 0;
>   }
>   
>   static av_cold void avisynth_context_destroy(AviSynthContext *avs)
>   {
> -    if (avs_atexit_called)
> -        return;
> -
> -    if (avs == avs_ctx_list) {
> -        avs_ctx_list = avs->next;
> -    } else {
> -        AviSynthContext *prev = avs_ctx_list;
> -        while (prev->next != avs)
> -            prev = prev->next;
> -        prev->next = avs->next;
> -    }
> -
>       if (avs->clip) {
>           avs_library.avs_release_clip(avs->clip);
>           avs->clip = NULL;
> @@ -248,20 +217,6 @@ static av_cold void avisynth_context_destroy(AviSynthContext *avs)
>       }
>   }
>   
> -static av_cold void avisynth_atexit_handler(void)
> -{
> -    AviSynthContext *avs = avs_ctx_list;
> -
> -    while (avs) {
> -        AviSynthContext *next = avs->next;
> -        avisynth_context_destroy(avs);
> -        avs = next;
> -    }
> -    dlclose(avs_library.library);
> -
> -    avs_atexit_called = 1;
> -}
> -
>   /* Create AVStream from audio and video data. */
>   static int avisynth_create_stream_video(AVFormatContext *s, AVStream *st)
>   {
> @@ -1134,6 +1089,7 @@ static av_cold int avisynth_read_close(AVFormatContext *s)
>           return AVERROR_UNKNOWN;
>   
>       avisynth_context_destroy(s->priv_data);
> +    dlclose(avs_library.library);
>       ff_mutex_unlock(&avisynth_mutex);
>       return 0;
>   }

Pushed.
diff mbox series

Patch

diff --git a/libavformat/avisynth.c b/libavformat/avisynth.c
index 625bdf7e3a..26747671c0 100644
--- a/libavformat/avisynth.c
+++ b/libavformat/avisynth.c
@@ -115,9 +115,6 @@  typedef struct AviSynthContext {
     int error;
 
     uint32_t flags;
-
-    /* Linked list pointers. */
-    struct AviSynthContext *next;
 } AviSynthContext;
 
 static const int avs_planes_packed[1] = { 0 };
@@ -133,15 +130,7 @@  static const int avs_planes_rgba[4]   = { AVS_PLANAR_G, AVS_PLANAR_B,
 
 static AVMutex avisynth_mutex = AV_MUTEX_INITIALIZER;
 
-/* A conflict between C++ global objects, atexit, and dynamic loading requires
- * us to register our own atexit handler to prevent double freeing. */
 static AviSynthLibrary avs_library;
-static int avs_atexit_called        = 0;
-
-/* Linked list of AviSynthContexts. An atexit handler destroys this list. */
-static AviSynthContext *avs_ctx_list = NULL;
-
-static av_cold void avisynth_atexit_handler(void);
 
 static av_cold int avisynth_load_library(void)
 {
@@ -185,7 +174,6 @@  static av_cold int avisynth_load_library(void)
     LOAD_AVS_FUNC(avs_get_env_property, 1);
 #undef LOAD_AVS_FUNC
 
-    atexit(avisynth_atexit_handler);
     return 0;
 
 fail:
@@ -214,30 +202,11 @@  static av_cold int avisynth_context_create(AVFormatContext *s)
         }
     }
 
-    if (!avs_ctx_list) {
-        avs_ctx_list = avs;
-    } else {
-        avs->next    = avs_ctx_list;
-        avs_ctx_list = avs;
-    }
-
     return 0;
 }
 
 static av_cold void avisynth_context_destroy(AviSynthContext *avs)
 {
-    if (avs_atexit_called)
-        return;
-
-    if (avs == avs_ctx_list) {
-        avs_ctx_list = avs->next;
-    } else {
-        AviSynthContext *prev = avs_ctx_list;
-        while (prev->next != avs)
-            prev = prev->next;
-        prev->next = avs->next;
-    }
-
     if (avs->clip) {
         avs_library.avs_release_clip(avs->clip);
         avs->clip = NULL;
@@ -248,20 +217,6 @@  static av_cold void avisynth_context_destroy(AviSynthContext *avs)
     }
 }
 
-static av_cold void avisynth_atexit_handler(void)
-{
-    AviSynthContext *avs = avs_ctx_list;
-
-    while (avs) {
-        AviSynthContext *next = avs->next;
-        avisynth_context_destroy(avs);
-        avs = next;
-    }
-    dlclose(avs_library.library);
-
-    avs_atexit_called = 1;
-}
-
 /* Create AVStream from audio and video data. */
 static int avisynth_create_stream_video(AVFormatContext *s, AVStream *st)
 {
@@ -1134,6 +1089,7 @@  static av_cold int avisynth_read_close(AVFormatContext *s)
         return AVERROR_UNKNOWN;
 
     avisynth_context_destroy(s->priv_data);
+    dlclose(avs_library.library);
     ff_mutex_unlock(&avisynth_mutex);
     return 0;
 }