Thread: initdb SegFault

initdb SegFault

From
vignesh C
Date:
Hi,

While checking initdb code, I found one segmentation fault, stack
trace for the same is:
Core was generated by `./initdb -D data6'.
Program terminated with signal 11, Segmentation fault.
#0  0x000000000040ea22 in main (argc=3, argv=0x7ffc82237308) at initdb.c:3340
3340        printf(_("\nSuccess. You can now start the database server
using:\n\n"

Analysis for the same is given below:
createPQExpBuffer allocates memory and returns the pointer, there is a
possibility that createPQExpBuffer can return NULL pointer in case of
malloc failiure, but initdb's main function does not check this
condition. During malloc failure when pointer is accessed it results
in segmentation fault. Made changes to check and exit if
createPQExpBuffer return's NULL pointer. Patch for the same is
attached.

Let me know your thoughts for the same. Similar issue exists in few
other places, if changes are ok, I can check and fix the issue in
other places also.

Regards,
Vignesh
EnterpriseDB: http://www.enterprisedb.com

Attachment

Re: initdb SegFault

From
Tom Lane
Date:
vignesh C <vignesh21@gmail.com> writes:
> createPQExpBuffer allocates memory and returns the pointer, there is a
> possibility that createPQExpBuffer can return NULL pointer in case of
> malloc failiure, but initdb's main function does not check this
> condition. During malloc failure when pointer is accessed it results
> in segmentation fault. Made changes to check and exit if
> createPQExpBuffer return's NULL pointer. Patch for the same is
> attached.

I can't get excited about this, for several reasons.

1) The probability of it happening in the field is not
distinguishable from zero, surely.  I imagine you forced this
failure by making a debugging malloc fail occasionally.

2) If we really are out of memory at this point, we'd have just as good
odds that some allocation request inside pg_log_error() would fail.
There's no practical way to ensure that that code path remains free
of malloc attempts.  (Not to mention cleanup_directories_atexit().)

3) In the end, an initdb failure is an initdb failure.  This change
doesn't improve robustness by any useful metric, it just adds an
untestable code path.  If we could recover somehow, it'd be more
interesting to spend time on.

BTW, looking at the small minority of places that bother to test
for createPQExpBuffer failure, the correct test for that seems
to be PQExpBufferBroken().

            regards, tom lane



Re: initdb SegFault

From
Andres Freund
Date:
Hi,

On 2019-11-19 10:16:02 -0500, Tom Lane wrote:
> vignesh C <vignesh21@gmail.com> writes:
> > createPQExpBuffer allocates memory and returns the pointer, there is a
> > possibility that createPQExpBuffer can return NULL pointer in case of
> > malloc failiure, but initdb's main function does not check this
> > condition. During malloc failure when pointer is accessed it results
> > in segmentation fault. Made changes to check and exit if
> > createPQExpBuffer return's NULL pointer. Patch for the same is
> > attached.
> 
> I can't get excited about this, for several reasons.
> 
> 1) The probability of it happening in the field is not
> distinguishable from zero, surely.  I imagine you forced this
> failure by making a debugging malloc fail occasionally.

Agreed wrt this specific failure scenario. It does however seem not
great that callsites for PQExpBuffer ought to check every call for
allocation failures, in the general case.

I do think it'd be reasonable to move the cases where "graceful" dealing
with OOM isn't necessary ought to really use an interface that
internally errors out on memory allocation failures. Kinda thinking we
ought to slowly move such paths towards stringinfo...


> 2) If we really are out of memory at this point, we'd have just as good
> odds that some allocation request inside pg_log_error() would fail.
> There's no practical way to ensure that that code path remains free
> of malloc attempts.  (Not to mention cleanup_directories_atexit().)

I wonder if, for frontend paths, a simplified error handling path would
be worthwhile for OOM paths. Doing only a write() or such to print an
error message.

Greetings,

Andres Freund



Re: initdb SegFault

From
Tom Lane
Date:
Andres Freund <andres@anarazel.de> writes:
> Agreed wrt this specific failure scenario. It does however seem not
> great that callsites for PQExpBuffer ought to check every call for
> allocation failures, in the general case.

It is possible to check just once at the end, using the PQExpBufferBroken
API, and I believe that libpq for instance is fairly careful about that.

I agree that programs that just need to print something and exit could
perhaps ask pqexpbuffer.c to handle that for them.  (But initdb still
doesn't fall in that category, because of its very nontrivial atexit
handler :-(.)

> I wonder if, for frontend paths, a simplified error handling path would
> be worthwhile for OOM paths. Doing only a write() or such to print an
> error message.

Perhaps.  You wouldn't get any translation --- but then, gettext is
probably going to fail anyway under such conditions.

            regards, tom lane



Re: initdb SegFault

From
Kyotaro Horiguchi
Date:
At Tue, 19 Nov 2019 12:06:50 -0500, Tom Lane <tgl@sss.pgh.pa.us> wrote in 
> Andres Freund <andres@anarazel.de> writes:
> > Agreed wrt this specific failure scenario. It does however seem not
> > great that callsites for PQExpBuffer ought to check every call for
> > allocation failures, in the general case.
> 
> It is possible to check just once at the end, using the PQExpBufferBroken
> API, and I believe that libpq for instance is fairly careful about that.

FWIW, I looked though the callers of PQExpBuffer.

pqGetErrorNotice3 seems ingoring OOM on message buffer when !isError,
then sets NULL to res->errMsg. getParameterStatus doesn't check that
before use, too.

Most of the callers of PQExpBufferDataBroken use libpq_gettext("out of
memory"). And some of them do strdup(libpq_gettext()).

Not restricting to libpq functions, 

dblink_connstr_check complains as "password is required" when
PQconninfoParse hits OOM.

libpqrcv_check_conninfo() will show '(null)' or maybe get SEGV on some
platforms when PQconninfoParse() hits OOM, since it uses err without
null checking. pg_basebackup, pg_dumpall and pg_isready is doing the
same thing.


> I agree that programs that just need to print something and exit could
> perhaps ask pqexpbuffer.c to handle that for them.  (But initdb still
> doesn't fall in that category, because of its very nontrivial atexit
> handler :-(.)
> 
> > I wonder if, for frontend paths, a simplified error handling path would
> > be worthwhile for OOM paths. Doing only a write() or such to print an
> > error message.
> 
> Perhaps.  You wouldn't get any translation --- but then, gettext is
> probably going to fail anyway under such conditions.

I think we should refrain from translating in the cases.

regards.

-- 
Kyotaro Horiguchi
NTT Open Source Software Center