Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit7e3bb08

Browse files
committed
Fix access-to-already-freed-memory issue in plpython's error handling.
PLy_elog() could attempt to access strings that Python had already freed,because the strings that PLy_get_spi_error_data() returns are simplypointers into storage associated with the error "val" PyObject. That'sfine at the instant PLy_get_spi_error_data() returns them, but just afterthat PLy_traceback() intentionally releases the only refcount on thatobject, allowing it to be freed --- so that the strings we pass toereport() are dangling pointers.In principle this could result in garbage output or a coredump. Inpractice, I think the risk is pretty low, because there are no Pythonoperations between where we decrement that refcount and where we use thestrings (and copy them into PG storage), and thus no reason for Pythonto recycle the storage. Still, it's clearly hazardous, and it leads toValgrind complaints when running under a Valgrind that hasn't beenlobotomized to ignore Python memory allocations.The code was a mess anyway: we fetched the error data out of Python(clearing Python's error indicator) with PyErr_Fetch, examined it, pushedit back into Python with PyErr_Restore (re-setting the error indicator),then immediately pulled it back out with another PyErr_Fetch. Just toconfuse matters even more, there were some gratuitous-and-yet-hazardousPyErr_Clear calls in the "examine" step, and we didn't get around to doingPyErr_NormalizeException until after the second PyErr_Fetch, making it evenless clear which object was being manipulated where and whether we stillhad a refcount on it. (If PyErr_NormalizeException did substitute adifferent "val" object, it's possible that the problem could manifest forreal, because then we'd be doing assorted Python stuff with no refcounton the object we have string pointers into.)So, rearrange all that into some semblance of sanity, and don't decrementthe refcount on the Python error objects until the end of PLy_elog().In HEAD, I failed to resist the temptation to reformat some messy bitsfrom5c3c3cd along the way.Back-patch as far as 9.2, because the code is substantially the samethat far back. I believe that 9.1 has the bug as well; but the codearound it is rather different and I don't want to take a chance onbreaking something for what seems a low-probability problem.
1 parent008608b commit7e3bb08

File tree

1 file changed

+48
-43
lines changed

1 file changed

+48
-43
lines changed

‎src/pl/plpython/plpy_elog.c

Lines changed: 48 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ PyObject *PLy_exc_fatal = NULL;
2121
PyObject*PLy_exc_spi_error=NULL;
2222

2323

24-
staticvoidPLy_traceback(char**xmsg,char**tbmsg,int*tb_depth);
24+
staticvoidPLy_traceback(PyObject*e,PyObject*v,PyObject*tb,
25+
char**xmsg,char**tbmsg,int*tb_depth);
2526
staticvoidPLy_get_spi_error_data(PyObject*exc,int*sqlerrcode,char**detail,
2627
char**hint,char**query,int*position,
2728
char**schema_name,char**table_name,char**column_name,
@@ -65,22 +66,27 @@ PLy_elog(int elevel, const char *fmt,...)
6566
char*constraint_name=NULL;
6667

6768
PyErr_Fetch(&exc,&val,&tb);
69+
6870
if (exc!=NULL)
6971
{
72+
PyErr_NormalizeException(&exc,&val,&tb);
73+
7074
if (PyErr_GivenExceptionMatches(val,PLy_exc_spi_error))
71-
PLy_get_spi_error_data(val,&sqlerrcode,&detail,&hint,&query,&position,
72-
&schema_name,&table_name,&column_name,
73-
&datatype_name,&constraint_name);
75+
PLy_get_spi_error_data(val,&sqlerrcode,
76+
&detail,&hint,&query,&position,
77+
&schema_name,&table_name,&column_name,
78+
&datatype_name,&constraint_name);
7479
elseif (PyErr_GivenExceptionMatches(val,PLy_exc_error))
7580
PLy_get_error_data(val,&sqlerrcode,&detail,&hint,
76-
&schema_name,&table_name,&column_name,
77-
&datatype_name,&constraint_name);
81+
&schema_name,&table_name,&column_name,
82+
&datatype_name,&constraint_name);
7883
elseif (PyErr_GivenExceptionMatches(val,PLy_exc_fatal))
7984
elevel=FATAL;
8085
}
81-
PyErr_Restore(exc,val,tb);
8286

83-
PLy_traceback(&xmsg,&tbmsg,&tb_depth);
87+
/* this releases our refcount on tb! */
88+
PLy_traceback(exc,val,tb,
89+
&xmsg,&tbmsg,&tb_depth);
8490

8591
if (fmt)
8692
{
@@ -122,11 +128,16 @@ PLy_elog(int elevel, const char *fmt,...)
122128
(hint) ?errhint("%s",hint) :0,
123129
(query) ?internalerrquery(query) :0,
124130
(position) ?internalerrposition(position) :0,
125-
(schema_name) ?err_generic_string(PG_DIAG_SCHEMA_NAME,schema_name) :0,
126-
(table_name) ?err_generic_string(PG_DIAG_TABLE_NAME,table_name) :0,
127-
(column_name) ?err_generic_string(PG_DIAG_COLUMN_NAME,column_name) :0,
128-
(datatype_name) ?err_generic_string(PG_DIAG_DATATYPE_NAME,datatype_name) :0,
129-
(constraint_name) ?err_generic_string(PG_DIAG_CONSTRAINT_NAME,constraint_name) :0));
131+
(schema_name) ?err_generic_string(PG_DIAG_SCHEMA_NAME,
132+
schema_name) :0,
133+
(table_name) ?err_generic_string(PG_DIAG_TABLE_NAME,
134+
table_name) :0,
135+
(column_name) ?err_generic_string(PG_DIAG_COLUMN_NAME,
136+
column_name) :0,
137+
(datatype_name) ?err_generic_string(PG_DIAG_DATATYPE_NAME,
138+
datatype_name) :0,
139+
(constraint_name) ?err_generic_string(PG_DIAG_CONSTRAINT_NAME,
140+
constraint_name) :0));
130141
}
131142
PG_CATCH();
132143
{
@@ -136,6 +147,9 @@ PLy_elog(int elevel, const char *fmt,...)
136147
pfree(xmsg);
137148
if (tbmsg)
138149
pfree(tbmsg);
150+
Py_XDECREF(exc);
151+
Py_XDECREF(val);
152+
139153
PG_RE_THROW();
140154
}
141155
PG_END_TRY();
@@ -146,21 +160,24 @@ PLy_elog(int elevel, const char *fmt,...)
146160
pfree(xmsg);
147161
if (tbmsg)
148162
pfree(tbmsg);
163+
Py_XDECREF(exc);
164+
Py_XDECREF(val);
149165
}
150166

151167
/*
152-
* Extract a Python traceback from thecurrent exception.
168+
* Extract a Python traceback from thegiven exception data.
153169
*
154170
* The exception error message is returned in xmsg, the traceback in
155171
* tbmsg (both as palloc'd strings) and the traceback depth in
156172
* tb_depth.
173+
*
174+
* We release refcounts on all the Python objects in the traceback stack,
175+
* but not on e or v.
157176
*/
158177
staticvoid
159-
PLy_traceback(char**xmsg,char**tbmsg,int*tb_depth)
178+
PLy_traceback(PyObject*e,PyObject*v,PyObject*tb,
179+
char**xmsg,char**tbmsg,int*tb_depth)
160180
{
161-
PyObject*e,
162-
*v,
163-
*tb;
164181
PyObject*e_type_o;
165182
PyObject*e_module_o;
166183
char*e_type_s=NULL;
@@ -171,12 +188,7 @@ PLy_traceback(char **xmsg, char **tbmsg, int *tb_depth)
171188
StringInfoDatatbstr;
172189

173190
/*
174-
* get the current exception
175-
*/
176-
PyErr_Fetch(&e,&v,&tb);
177-
178-
/*
179-
* oops, no exception, return
191+
* if no exception, return nulls
180192
*/
181193
if (e==NULL)
182194
{
@@ -187,8 +199,6 @@ PLy_traceback(char **xmsg, char **tbmsg, int *tb_depth)
187199
return;
188200
}
189201

190-
PyErr_NormalizeException(&e,&v,&tb);
191-
192202
/*
193203
* Format the exception and its value and put it in xmsg.
194204
*/
@@ -355,8 +365,6 @@ PLy_traceback(char **xmsg, char **tbmsg, int *tb_depth)
355365
Py_XDECREF(e_type_o);
356366
Py_XDECREF(e_module_o);
357367
Py_XDECREF(vob);
358-
Py_XDECREF(v);
359-
Py_DECREF(e);
360368
}
361369

362370
/*
@@ -388,19 +396,21 @@ PLy_get_sqlerrcode(PyObject *exc, int *sqlerrcode)
388396
*/
389397
staticvoid
390398
PLy_get_spi_error_data(PyObject*exc,int*sqlerrcode,char**detail,
391-
char**hint,char**query,int*position,
392-
char**schema_name,char**table_name,char**column_name,
393-
char**datatype_name,char**constraint_name)
399+
char**hint,char**query,int*position,
400+
char**schema_name,char**table_name,
401+
char**column_name,
402+
char**datatype_name,char**constraint_name)
394403
{
395-
PyObject*spidata=NULL;
404+
PyObject*spidata;
396405

397406
spidata=PyObject_GetAttrString(exc,"spidata");
398407

399408
if (spidata!=NULL)
400409
{
401-
PyArg_ParseTuple(spidata,"izzzizzzzz",sqlerrcode,detail,hint,query,position,
402-
schema_name,table_name,column_name,
403-
datatype_name,constraint_name);
410+
PyArg_ParseTuple(spidata,"izzzizzzzz",
411+
sqlerrcode,detail,hint,query,position,
412+
schema_name,table_name,column_name,
413+
datatype_name,constraint_name);
404414
}
405415
else
406416
{
@@ -411,33 +421,28 @@ PLy_get_spi_error_data(PyObject *exc, int *sqlerrcode, char **detail,
411421
PLy_get_sqlerrcode(exc,sqlerrcode);
412422
}
413423

414-
PyErr_Clear();
415-
/* no elog here, we simply won't report the errhint, errposition etc */
416424
Py_XDECREF(spidata);
417425
}
418426

419427
/*
420428
* Extract the error data from an Error.
429+
*
421430
* Note: position and query attributes are never set for Error so, unlike
422431
* PLy_get_spi_error_data, this function doesn't return them.
423432
*/
424433
staticvoid
425434
PLy_get_error_data(PyObject*exc,int*sqlerrcode,char**detail,char**hint,
426-
char**schema_name,char**table_name,char**column_name,
427-
char**datatype_name,char**constraint_name)
435+
char**schema_name,char**table_name,char**column_name,
436+
char**datatype_name,char**constraint_name)
428437
{
429438
PLy_get_sqlerrcode(exc,sqlerrcode);
430-
431439
get_string_attr(exc,"detail",detail);
432440
get_string_attr(exc,"hint",hint);
433441
get_string_attr(exc,"schema_name",schema_name);
434442
get_string_attr(exc,"table_name",table_name);
435443
get_string_attr(exc,"column_name",column_name);
436444
get_string_attr(exc,"datatype_name",datatype_name);
437445
get_string_attr(exc,"constraint_name",constraint_name);
438-
439-
PyErr_Clear();
440-
/* no elog here, we simply won't report the errhint, errposition etc */
441446
}
442447

443448
/*

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp