Thread: protect dll lib initialisation against any exception, for 8.5

protect dll lib initialisation against any exception, for 8.5

From
Pavel Stehule
Date:
Hello

attached patch allows raising exception from _PG_init function as was
discussed before.

regards
Pavel Stehule

Attachment

Re: protect dll lib initialisation against any exception, for 8.5

From
Tom Lane
Date:
Pavel Stehule <pavel.stehule@gmail.com> writes:
> attached patch allows raising exception from _PG_init function as was
> discussed before.

I fooled around with this and came up with the attached improved
version, which allows reporting the full error status.  However,
after thinking some more I feel that this is probably a cure worse
than the disease.  If we simply leave the code as it stands, an
elog(ERROR) in an init function doesn't corrupt dfmgr.c's internal list,
which is what I had been fearing when I complained about the issue.
The worst that happens is that we leave the library loaded and leak
a little bit of memory.  Unloading the library, as the patch does,
could easily make things worse not better.  Consider the not-unlikely
case that the library installs itself in a few callback hooks and
then fails.  If we unload the library, those hooks represent
*guaranteed* core dumps on next use.  If we don't unload, the hook
functions might or might not work too well --- presumably not everything
they need has been initialized --- but it's hard to imagine an outcome
that's worse than a guaranteed core dump.

So I'm thinking this is really unnecessary and we should leave well
enough alone.

            regards, tom lane

Index: dfmgr.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/fmgr/dfmgr.c,v
retrieving revision 1.98
diff -c -r1.98 dfmgr.c
*** dfmgr.c    1 Jan 2009 17:23:51 -0000    1.98
--- dfmgr.c    1 Apr 2009 23:41:37 -0000
***************
*** 178,184 ****
  static void *
  internal_load_library(const char *libname)
  {
!     DynamicFileList *file_scanner;
      PGModuleMagicFunction magic_func;
      char       *load_error;
      struct stat stat_buf;
--- 178,184 ----
  static void *
  internal_load_library(const char *libname)
  {
!     DynamicFileList * volatile file_scanner;
      PGModuleMagicFunction magic_func;
      char       *load_error;
      struct stat stat_buf;
***************
*** 277,287 ****
          }

          /*
!          * If the library has a _PG_init() function, call it.
           */
          PG_init = (PG_init_t) pg_dlsym(file_scanner->handle, "_PG_init");
          if (PG_init)
!             (*PG_init) ();

          /* OK to link it into list */
          if (file_list == NULL)
--- 277,329 ----
          }

          /*
!          * If the library has a _PG_init() function, call it.  Guard against
!          * the function possibly throwing elog(ERROR).
           */
          PG_init = (PG_init_t) pg_dlsym(file_scanner->handle, "_PG_init");
          if (PG_init)
!         {
!             MemoryContext    oldcontext = CurrentMemoryContext;
!
!             PG_TRY();
!             {
!                 (*PG_init) ();
!             }
!             PG_CATCH();
!             {
!                 ErrorData  *edata;
!
!                 /* fetch the error status so we can change it */
!                 MemoryContextSwitchTo(oldcontext);
!                 edata = CopyErrorData();
!                 FlushErrorState();
!
!                 /*
!                  * The const pointers in the error status very likely point
!                  * at constant strings in the library, which we are about to
!                  * unload.  Copy them so we don't dump core while reporting
!                  * the error.  This might leak a bit of memory but it
!                  * beats the alternatives.
!                  */
!                 if (edata->filename)
!                     edata->filename = pstrdup(edata->filename);
!                 if (edata->funcname)
!                     edata->funcname = pstrdup(edata->funcname);
!                 if (edata->domain)
!                     edata->domain = pstrdup(edata->domain);
!
!                 /* library might have already called some of its functions */
!                 clear_external_function_hash(file_scanner->handle);
!
!                 /* try to unlink library */
!                 pg_dlclose(file_scanner->handle);
!                 free((char *) file_scanner);
!
!                 /* complain */
!                 ReThrowError(edata);
!             }
!             PG_END_TRY();
!         }

          /* OK to link it into list */
          if (file_list == NULL)

Re: protect dll lib initialisation against any exception, for 8.5

From
Pavel Stehule
Date:
2009/4/2 Tom Lane <tgl@sss.pgh.pa.us>:
> Pavel Stehule <pavel.stehule@gmail.com> writes:
>> attached patch allows raising exception from _PG_init function as was
>> discussed before.
>
> I fooled around with this and came up with the attached improved
> version, which allows reporting the full error status.  However,
> after thinking some more I feel that this is probably a cure worse
> than the disease.  If we simply leave the code as it stands, an
> elog(ERROR) in an init function doesn't corrupt dfmgr.c's internal list,
> which is what I had been fearing when I complained about the issue.
> The worst that happens is that we leave the library loaded and leak
> a little bit of memory.  Unloading the library, as the patch does,
> could easily make things worse not better.  Consider the not-unlikely
> case that the library installs itself in a few callback hooks and
> then fails.  If we unload the library, those hooks represent
> *guaranteed* core dumps on next use.  If we don't unload, the hook
> functions might or might not work too well --- presumably not everything
> they need has been initialized --- but it's hard to imagine an outcome
> that's worse than a guaranteed core dump.
>
> So I'm thinking this is really unnecessary and we should leave well
> enough alone.
>

I see it. I thing , an safety of this exception should be solved only
by programmer. It's important to release all hooks, and then raise an
exception. It is in developer responsibility.

regards
Pavel Stehule

>                        regards, tom lane
>
>
> Index: dfmgr.c
> ===================================================================
> RCS file: /cvsroot/pgsql/src/backend/utils/fmgr/dfmgr.c,v
> retrieving revision 1.98
> diff -c -r1.98 dfmgr.c
> *** dfmgr.c     1 Jan 2009 17:23:51 -0000       1.98
> --- dfmgr.c     1 Apr 2009 23:41:37 -0000
> ***************
> *** 178,184 ****
>  static void *
>  internal_load_library(const char *libname)
>  {
> !       DynamicFileList *file_scanner;
>        PGModuleMagicFunction magic_func;
>        char       *load_error;
>        struct stat stat_buf;
> --- 178,184 ----
>  static void *
>  internal_load_library(const char *libname)
>  {
> !       DynamicFileList * volatile file_scanner;
>        PGModuleMagicFunction magic_func;
>        char       *load_error;
>        struct stat stat_buf;
> ***************
> *** 277,287 ****
>                }
>
>                /*
> !                * If the library has a _PG_init() function, call it.
>                 */
>                PG_init = (PG_init_t) pg_dlsym(file_scanner->handle, "_PG_init");
>                if (PG_init)
> !                       (*PG_init) ();
>
>                /* OK to link it into list */
>                if (file_list == NULL)
> --- 277,329 ----
>                }
>
>                /*
> !                * If the library has a _PG_init() function, call it.  Guard against
> !                * the function possibly throwing elog(ERROR).
>                 */
>                PG_init = (PG_init_t) pg_dlsym(file_scanner->handle, "_PG_init");
>                if (PG_init)
> !               {
> !                       MemoryContext   oldcontext = CurrentMemoryContext;
> !
> !                       PG_TRY();
> !                       {
> !                               (*PG_init) ();
> !                       }
> !                       PG_CATCH();
> !                       {
> !                               ErrorData  *edata;
> !
> !                               /* fetch the error status so we can change it */
> !                               MemoryContextSwitchTo(oldcontext);
> !                               edata = CopyErrorData();
> !                               FlushErrorState();
> !
> !                               /*
> !                                * The const pointers in the error status very likely point
> !                                * at constant strings in the library, which we are about to
> !                                * unload.  Copy them so we don't dump core while reporting
> !                                * the error.  This might leak a bit of memory but it
> !                                * beats the alternatives.
> !                                */
> !                               if (edata->filename)
> !                                       edata->filename = pstrdup(edata->filename);
> !                               if (edata->funcname)
> !                                       edata->funcname = pstrdup(edata->funcname);
> !                               if (edata->domain)
> !                                       edata->domain = pstrdup(edata->domain);
> !
> !                               /* library might have already called some of its functions */
> !                               clear_external_function_hash(file_scanner->handle);
> !
> !                               /* try to unlink library */
> !                               pg_dlclose(file_scanner->handle);
> !                               free((char *) file_scanner);
> !
> !                               /* complain */
> !                               ReThrowError(edata);
> !                       }
> !                       PG_END_TRY();
> !               }
>
>                /* OK to link it into list */
>                if (file_list == NULL)
>
>

Re: protect dll lib initialisation against any exception, for 8.5

From
Tom Lane
Date:
Pavel Stehule <pavel.stehule@gmail.com> writes:
> 2009/4/2 Tom Lane <tgl@sss.pgh.pa.us>:
>> So I'm thinking this is really unnecessary and we should leave well
>> enough alone.

> I see it. I thing , an safety of this exception should be solved only
> by programmer. It's important to release all hooks, and then raise an
> exception. It is in developer responsibility.

Well, if the init function is sufficiently carefully coded to back out
just the changes it's managed to apply, then good for it.  But we still
aren't losing much by leaving dfmgr as-is.
        regards, tom lane


Re: protect dll lib initialisation against any exception, for 8.5

From
Greg Stark
Date:
Hmm. One case where this logic might not be true would be if the dll  
relies on c++ style static initializers and destructors. In that case  
it may very well leave hooks in place in case of an error and only  
clean them up when you call dlclose().



-- 
Greg


On 1 Apr 2009, at 22:58, Tom Lane <tgl@sss.pgh.pa.us> wrote:

> Pavel Stehule <pavel.stehule@gmail.com> writes:
>> 2009/4/2 Tom Lane <tgl@sss.pgh.pa.us>:
>>> So I'm thinking this is really unnecessary and we should leave well
>>> enough alone.
>
>> I see it. I thing , an safety of this exception should be solved only
>> by programmer. It's important to release all hooks, and then raise an
>> exception. It is in developer responsibility.
>
> Well, if the init function is sufficiently carefully coded to back out
> just the changes it's managed to apply, then good for it.  But we  
> still
> aren't losing much by leaving dfmgr as-is.
>
>            regards, tom lane
>
> -- 
> Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
> To make changes to your subscription:
> http://www.postgresql.org/mailpref/pgsql-hackers


Re: protect dll lib initialisation against any exception, for 8.5

From
Pavel Stehule
Date:
2009/4/2 Tom Lane <tgl@sss.pgh.pa.us>:
> Pavel Stehule <pavel.stehule@gmail.com> writes:
>> 2009/4/2 Tom Lane <tgl@sss.pgh.pa.us>:
>>> So I'm thinking this is really unnecessary and we should leave well
>>> enough alone.
>
>> I see it. I thing , an safety of this exception should be solved only
>> by programmer. It's important to release all hooks, and then raise an
>> exception. It is in developer responsibility.
>
> Well, if the init function is sufficiently carefully coded to back out
> just the changes it's managed to apply, then good for it.  But we still
> aren't losing much by leaving dfmgr as-is.
>

Maybe an safe minimum is cleaning symbols table without closing
library. Then the code from lib will be accessible, but functionality
will be disabled (for Postgres)?

regards
Pavel Stehule


>                        regards, tom lane
>


Re: protect dll lib initialisation against any exception, for 8.5

From
Tom Lane
Date:
Pavel Stehule <pavel.stehule@gmail.com> writes:
> Maybe an safe minimum is cleaning symbols table without closing
> library. Then the code from lib will be accessible, but functionality
> will be disabled (for Postgres)?

If the library doesn't get added to the list in dfmgr.c, we'll never
look for symbols within it anyway.  So I don't think there's any
particular cleaning to be done --- even assuming that the platform
supports removing symbols without dlclose'ing the library, which
seems rather unlikely.
        regards, tom lane


Re: protect dll lib initialisation against any exception, for 8.5

From
Tom Lane
Date:
Greg Stark <greg.stark@enterprisedb.com> writes:
> Hmm. One case where this logic might not be true would be if the dll  
> relies on c++ style static initializers and destructors. In that case  
> it may very well leave hooks in place in case of an error and only  
> clean them up when you call dlclose().

Interesting point, but considering that we don't support or encourage
use of C++ anyway, it shouldn't carry much weight in our estimate
of how an init function is likely to behave.

Also, wouldn't C++ initializers most likely get called by the dynamic
loader itself, not during the PG_init function?
        regards, tom lane