mirror of
https://github.com/postgres/postgres.git
synced 2026-05-19 08:41:23 -04:00
Add missing connection validation in ECPG
ECPGdeallocate_all(), ECPGprepared_statement(), ECPGget_desc(), and ecpg_freeStmtCacheEntry() could crash with a SIGSEGV when called without an established connection (for example, when EXEC SQL CONNECT was forgotten or a non-existent connection name was used), because they dereferenced the result of ecpg_get_connection() without first checking it for NULL. Each site is fixed in the style of the surrounding code. New tests are added for these conditions. Author: Shruthi Gowda <gowdashru@gmail.com> Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us> Reviewed-by: Fujii Masao <masao.fujii@gmail.com> Reviewed-by: Mahendra Singh Thalor <mahi6run@gmail.com> Reviewed-by: Nishant Sharma <nishant.sharma@enterprisedb.com> Discussion: https://postgr.es/m/3007317.1765210195@sss.pgh.pa.us Backpatch-through: 14
This commit is contained in:
parent
b772f3fcad
commit
c34a280c85
10 changed files with 312 additions and 12 deletions
|
|
@ -476,6 +476,16 @@ ECPGget_desc(int lineno, const char *desc_name, int index,...)
|
|||
memset(&stmt, 0, sizeof stmt);
|
||||
stmt.lineno = lineno;
|
||||
|
||||
/* desperate try to guess something sensible */
|
||||
stmt.connection = ecpg_get_connection(NULL);
|
||||
if (stmt.connection == NULL)
|
||||
{
|
||||
ecpg_raise(lineno, ECPG_NO_CONN, ECPG_SQLSTATE_CONNECTION_DOES_NOT_EXIST,
|
||||
ecpg_gettext("NULL"));
|
||||
va_end(args);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Make sure we do NOT honor the locale for numeric input */
|
||||
/* since the database gives the standard decimal point */
|
||||
/* (see comments in execute.c) */
|
||||
|
|
@ -505,8 +515,6 @@ ECPGget_desc(int lineno, const char *desc_name, int index,...)
|
|||
setlocale(LC_NUMERIC, "C");
|
||||
#endif
|
||||
|
||||
/* desperate try to guess something sensible */
|
||||
stmt.connection = ecpg_get_connection(NULL);
|
||||
ecpg_store_result(ECPGresult, index, &stmt, &data_var);
|
||||
|
||||
#ifdef HAVE_USELOCALE
|
||||
|
|
|
|||
|
|
@ -381,8 +381,12 @@ ecpg_deallocate_all_conn(int lineno, enum COMPAT_MODE c, struct connection *con)
|
|||
bool
|
||||
ECPGdeallocate_all(int lineno, int compat, const char *connection_name)
|
||||
{
|
||||
return ecpg_deallocate_all_conn(lineno, compat,
|
||||
ecpg_get_connection(connection_name));
|
||||
struct connection *con = ecpg_get_connection(connection_name);
|
||||
|
||||
if (!ecpg_init(con, connection_name, lineno))
|
||||
return false;
|
||||
|
||||
return ecpg_deallocate_all_conn(lineno, compat, con);
|
||||
}
|
||||
|
||||
char *
|
||||
|
|
@ -395,13 +399,15 @@ ecpg_prepared(const char *name, struct connection *con)
|
|||
}
|
||||
|
||||
/* return the prepared statement */
|
||||
/* lineno is not used here, but kept in to not break API */
|
||||
char *
|
||||
ECPGprepared_statement(const char *connection_name, const char *name, int lineno)
|
||||
{
|
||||
(void) lineno; /* keep the compiler quiet */
|
||||
struct connection *con = ecpg_get_connection(connection_name);
|
||||
|
||||
return ecpg_prepared(name, ecpg_get_connection(connection_name));
|
||||
if (!ecpg_init(con, connection_name, lineno))
|
||||
return NULL;
|
||||
|
||||
return ecpg_prepared(name, con);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -498,10 +504,18 @@ ecpg_freeStmtCacheEntry(int lineno, int compat,
|
|||
|
||||
con = ecpg_get_connection(entry->connection);
|
||||
|
||||
/* free the 'prepared_statement' list entry */
|
||||
this = ecpg_find_prepared_statement(entry->stmtID, con, &prev);
|
||||
if (this && !deallocate_one(lineno, compat, con, prev, this))
|
||||
return -1;
|
||||
/*
|
||||
* If the connection is gone, the prepared_statement list it owned is
|
||||
* already unreachable, so just skip that cleanup. We must still clear
|
||||
* the cache slot below so it can be reused.
|
||||
*/
|
||||
if (con)
|
||||
{
|
||||
/* free the 'prepared_statement' list entry */
|
||||
this = ecpg_find_prepared_statement(entry->stmtID, con, &prev);
|
||||
if (this && !deallocate_one(lineno, compat, con, prev, this))
|
||||
return -1;
|
||||
}
|
||||
|
||||
entry->stmtID[0] = '\0';
|
||||
|
||||
|
|
|
|||
2
src/interfaces/ecpg/test/connect/.gitignore
vendored
2
src/interfaces/ecpg/test/connect/.gitignore
vendored
|
|
@ -8,3 +8,5 @@
|
|||
/test4.c
|
||||
/test5
|
||||
/test5.c
|
||||
/test6
|
||||
/test6.c
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ TESTS = test1 test1.c \
|
|||
test2 test2.c \
|
||||
test3 test3.c \
|
||||
test4 test4.c \
|
||||
test5 test5.c
|
||||
test5 test5.c \
|
||||
test6 test6.c
|
||||
|
||||
all: $(TESTS)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ pgc_files = [
|
|||
'test3',
|
||||
'test4',
|
||||
'test5',
|
||||
'test6',
|
||||
]
|
||||
|
||||
foreach pgc_file : pgc_files
|
||||
|
|
|
|||
68
src/interfaces/ecpg/test/connect/test6.pgc
Normal file
68
src/interfaces/ecpg/test/connect/test6.pgc
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* This test verifies that ecpg functions properly handle NULL connections
|
||||
* (i.e., when a connection name doesn't exist or has been disconnected).
|
||||
* Before the fix, these operations would cause a segmentation fault.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
exec sql include ../regression;
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
exec sql begin declare section;
|
||||
int val1output = 2;
|
||||
int val1 = 1;
|
||||
char val2[6] = "data1";
|
||||
char *stmt1 = "SELECT * from test1 where a = $1 and b = $2";
|
||||
exec sql end declare section;
|
||||
|
||||
ECPGdebug(1, stderr);
|
||||
|
||||
/* Connect to the database */
|
||||
exec sql connect to REGRESSDB1 as myconn;
|
||||
|
||||
/* Test 1: Try to get descriptor on a disconnected connection */
|
||||
printf("Test 1: Try to get descriptor on a disconnected connection\n");
|
||||
exec sql create table test1 (a int, b text);
|
||||
exec sql insert into test1 (a,b) values (1, 'data1');
|
||||
|
||||
exec sql allocate descriptor indesc;
|
||||
exec sql allocate descriptor outdesc;
|
||||
|
||||
exec sql prepare foo2 from :stmt1;
|
||||
|
||||
exec sql set descriptor indesc value 1 DATA = :val1;
|
||||
exec sql set descriptor indesc value 2 DATA = :val2;
|
||||
|
||||
exec sql execute foo2 using sql descriptor indesc into sql descriptor outdesc;
|
||||
|
||||
exec sql rollback;
|
||||
exec sql disconnect;
|
||||
exec sql get descriptor outdesc value 1 :val1output = DATA;
|
||||
printf("sqlca.sqlcode = %ld\n", sqlca.sqlcode);
|
||||
|
||||
/* Test 2: Try to deallocate all on a non-existent connection */
|
||||
printf("Test 2: deallocate all with non-existent connection\n");
|
||||
exec sql at nonexistent deallocate all;
|
||||
printf("sqlca.sqlcode = %ld\n", sqlca.sqlcode);
|
||||
|
||||
/* Test 3: deallocate on disconnected connection */
|
||||
printf("Test 3: deallocate all on disconnected connection\n");
|
||||
exec sql deallocate all;
|
||||
printf("sqlca.sqlcode = %ld\n", sqlca.sqlcode);
|
||||
|
||||
/* Test 4: Use prepared statement from non-existent connection */
|
||||
printf("Test 4: Use prepared statement from non-existent connection\n");
|
||||
exec sql at nonexistent prepare stmt1 FROM "SELECT 1";
|
||||
exec sql at nonexistent declare cur1 cursor for stmt1;
|
||||
exec sql at nonexistent open cur1;
|
||||
printf("sqlca.sqlcode = %ld\n", sqlca.sqlcode);
|
||||
|
||||
printf("All tests completed !\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ test: connect/test2
|
|||
test: connect/test3
|
||||
test: connect/test4
|
||||
test: connect/test5
|
||||
test: connect/test6
|
||||
test: pgtypeslib/dt_test
|
||||
test: pgtypeslib/dt_test2
|
||||
test: pgtypeslib/num_test
|
||||
|
|
|
|||
146
src/interfaces/ecpg/test/expected/connect-test6.c
Normal file
146
src/interfaces/ecpg/test/expected/connect-test6.c
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
/* Processed by ecpg (regression mode) */
|
||||
/* These include files are added by the preprocessor */
|
||||
#include <ecpglib.h>
|
||||
#include <ecpgerrno.h>
|
||||
#include <sqlca.h>
|
||||
/* End of automatic include section */
|
||||
#define ECPGdebug(X,Y) ECPGdebug((X)+100,(Y))
|
||||
|
||||
#line 1 "test6.pgc"
|
||||
/*
|
||||
* This test verifies that ecpg functions properly handle NULL connections
|
||||
* (i.e., when a connection name doesn't exist or has been disconnected).
|
||||
* Before the fix, these operations would cause a segmentation fault.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
#line 1 "regression.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#line 11 "test6.pgc"
|
||||
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
/* exec sql begin declare section */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#line 17 "test6.pgc"
|
||||
int val1output = 2 ;
|
||||
|
||||
#line 18 "test6.pgc"
|
||||
int val1 = 1 ;
|
||||
|
||||
#line 19 "test6.pgc"
|
||||
char val2 [ 6 ] = "data1" ;
|
||||
|
||||
#line 20 "test6.pgc"
|
||||
char * stmt1 = "SELECT * from test1 where a = $1 and b = $2" ;
|
||||
/* exec sql end declare section */
|
||||
#line 21 "test6.pgc"
|
||||
|
||||
|
||||
ECPGdebug(1, stderr);
|
||||
|
||||
/* Connect to the database */
|
||||
{ ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , "myconn", 0); }
|
||||
#line 26 "test6.pgc"
|
||||
|
||||
|
||||
/* Test 1: Try to get descriptor on a disconnected connection */
|
||||
printf("Test 1: Try to get descriptor on a disconnected connection\n");
|
||||
{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "create table test1 ( a int , b text )", ECPGt_EOIT, ECPGt_EORT);}
|
||||
#line 30 "test6.pgc"
|
||||
|
||||
{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into test1 ( a , b ) values ( 1 , 'data1' )", ECPGt_EOIT, ECPGt_EORT);}
|
||||
#line 31 "test6.pgc"
|
||||
|
||||
|
||||
ECPGallocate_desc(__LINE__, "indesc");
|
||||
#line 33 "test6.pgc"
|
||||
|
||||
ECPGallocate_desc(__LINE__, "outdesc");
|
||||
#line 34 "test6.pgc"
|
||||
|
||||
|
||||
{ ECPGprepare(__LINE__, NULL, 0, "foo2", stmt1);}
|
||||
#line 36 "test6.pgc"
|
||||
|
||||
|
||||
{ ECPGset_desc(__LINE__, "indesc", 1,ECPGd_data,
|
||||
ECPGt_int,&(val1),(long)1,(long)1,sizeof(int), ECPGd_EODT);
|
||||
}
|
||||
#line 38 "test6.pgc"
|
||||
|
||||
{ ECPGset_desc(__LINE__, "indesc", 2,ECPGd_data,
|
||||
ECPGt_char,(val2),(long)6,(long)1,(6)*sizeof(char), ECPGd_EODT);
|
||||
}
|
||||
#line 39 "test6.pgc"
|
||||
|
||||
|
||||
{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, "foo2",
|
||||
ECPGt_descriptor, "indesc", 1L, 1L, 1L,
|
||||
ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT,
|
||||
ECPGt_descriptor, "outdesc", 1L, 1L, 1L,
|
||||
ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);}
|
||||
#line 41 "test6.pgc"
|
||||
|
||||
|
||||
{ ECPGtrans(__LINE__, NULL, "rollback");}
|
||||
#line 43 "test6.pgc"
|
||||
|
||||
{ ECPGdisconnect(__LINE__, "CURRENT");}
|
||||
#line 44 "test6.pgc"
|
||||
|
||||
{ ECPGget_desc(__LINE__, "outdesc", 1,ECPGd_data,
|
||||
ECPGt_int,&(val1output),(long)1,(long)1,sizeof(int), ECPGd_EODT);
|
||||
}
|
||||
#line 45 "test6.pgc"
|
||||
|
||||
printf("sqlca.sqlcode = %ld\n", sqlca.sqlcode);
|
||||
|
||||
/* Test 2: Try to deallocate all on a non-existent connection */
|
||||
printf("Test 2: deallocate all with non-existent connection\n");
|
||||
{ ECPGdeallocate_all(__LINE__, 0, "nonexistent");}
|
||||
#line 50 "test6.pgc"
|
||||
|
||||
printf("sqlca.sqlcode = %ld\n", sqlca.sqlcode);
|
||||
|
||||
/* Test 3: deallocate on disconnected connection */
|
||||
printf("Test 3: deallocate all on disconnected connection\n");
|
||||
{ ECPGdeallocate_all(__LINE__, 0, NULL);}
|
||||
#line 55 "test6.pgc"
|
||||
|
||||
printf("sqlca.sqlcode = %ld\n", sqlca.sqlcode);
|
||||
|
||||
/* Test 4: Use prepared statement from non-existent connection */
|
||||
printf("Test 4: Use prepared statement from non-existent connection\n");
|
||||
{ ECPGprepare(__LINE__, "nonexistent", 0, "stmt1", "SELECT 1");}
|
||||
#line 60 "test6.pgc"
|
||||
|
||||
/* declare cur1 cursor for $1 */
|
||||
#line 61 "test6.pgc"
|
||||
|
||||
{ ECPGdo(__LINE__, 0, 1, "nonexistent", 0, ECPGst_normal, "declare cur1 cursor for $1",
|
||||
ECPGt_char_variable,(ECPGprepared_statement("nonexistent", "stmt1", __LINE__)),(long)1,(long)1,(1)*sizeof(char),
|
||||
ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);}
|
||||
#line 62 "test6.pgc"
|
||||
|
||||
printf("sqlca.sqlcode = %ld\n", sqlca.sqlcode);
|
||||
|
||||
printf("All tests completed !\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
50
src/interfaces/ecpg/test/expected/connect-test6.stderr
Normal file
50
src/interfaces/ecpg/test/expected/connect-test6.stderr
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
[NO_PID]: ECPGdebug: set to 1
|
||||
[NO_PID]: sqlca: code: 0, state: 00000
|
||||
[NO_PID]: ECPGconnect: opening database ecpg1_regression on <DEFAULT> port <DEFAULT>
|
||||
[NO_PID]: sqlca: code: 0, state: 00000
|
||||
[NO_PID]: ecpg_execute on line 30: query: create table test1 ( a int , b text ); with 0 parameter(s) on connection myconn
|
||||
[NO_PID]: sqlca: code: 0, state: 00000
|
||||
[NO_PID]: ecpg_execute on line 30: using PQexec
|
||||
[NO_PID]: sqlca: code: 0, state: 00000
|
||||
[NO_PID]: ecpg_process_output on line 30: OK: CREATE TABLE
|
||||
[NO_PID]: sqlca: code: 0, state: 00000
|
||||
[NO_PID]: ecpg_execute on line 31: query: insert into test1 ( a , b ) values ( 1 , 'data1' ); with 0 parameter(s) on connection myconn
|
||||
[NO_PID]: sqlca: code: 0, state: 00000
|
||||
[NO_PID]: ecpg_execute on line 31: using PQexec
|
||||
[NO_PID]: sqlca: code: 0, state: 00000
|
||||
[NO_PID]: ecpg_process_output on line 31: OK: INSERT 0 1
|
||||
[NO_PID]: sqlca: code: 0, state: 00000
|
||||
[NO_PID]: prepare_common on line 36: name foo2; query: "SELECT * from test1 where a = $1 and b = $2"
|
||||
[NO_PID]: sqlca: code: 0, state: 00000
|
||||
[NO_PID]: ecpg_execute on line 41: query: SELECT * from test1 where a = $1 and b = $2; with 2 parameter(s) on connection myconn
|
||||
[NO_PID]: sqlca: code: 0, state: 00000
|
||||
[NO_PID]: ecpg_execute on line 41: using PQexecPrepared for "SELECT * from test1 where a = $1 and b = $2"
|
||||
[NO_PID]: sqlca: code: 0, state: 00000
|
||||
[NO_PID]: ecpg_free_params on line 41: parameter 1 = 1
|
||||
[NO_PID]: sqlca: code: 0, state: 00000
|
||||
[NO_PID]: ecpg_free_params on line 41: parameter 2 = data1
|
||||
[NO_PID]: sqlca: code: 0, state: 00000
|
||||
[NO_PID]: ecpg_process_output on line 41: correctly got 1 tuples with 2 fields
|
||||
[NO_PID]: sqlca: code: 0, state: 00000
|
||||
[NO_PID]: ecpg_process_output on line 41: putting result (1 tuples) into descriptor outdesc
|
||||
[NO_PID]: sqlca: code: 0, state: 00000
|
||||
[NO_PID]: ECPGtrans on line 43: action "rollback"; connection "myconn"
|
||||
[NO_PID]: sqlca: code: 0, state: 00000
|
||||
[NO_PID]: deallocate_one on line 0: name foo2
|
||||
[NO_PID]: sqlca: code: 0, state: 00000
|
||||
[NO_PID]: ecpg_finish: connection myconn closed
|
||||
[NO_PID]: sqlca: code: 0, state: 00000
|
||||
[NO_PID]: ECPGget_desc: reading items for tuple 1
|
||||
[NO_PID]: sqlca: code: 0, state: 00000
|
||||
[NO_PID]: raising sqlcode -220 on line 45: connection "NULL" does not exist on line 45
|
||||
[NO_PID]: sqlca: code: -220, state: 08003
|
||||
[NO_PID]: raising sqlcode -220 on line 50: connection "nonexistent" does not exist on line 50
|
||||
[NO_PID]: sqlca: code: -220, state: 08003
|
||||
[NO_PID]: raising sqlcode -220 on line 55: connection "NULL" does not exist on line 55
|
||||
[NO_PID]: sqlca: code: -220, state: 08003
|
||||
[NO_PID]: raising sqlcode -220 on line 60: connection "nonexistent" does not exist on line 60
|
||||
[NO_PID]: sqlca: code: -220, state: 08003
|
||||
[NO_PID]: raising sqlcode -220 on line 63: connection "nonexistent" does not exist on line 63
|
||||
[NO_PID]: sqlca: code: -220, state: 08003
|
||||
[NO_PID]: raising sqlcode -220 on line 62: connection "nonexistent" does not exist on line 62
|
||||
[NO_PID]: sqlca: code: -220, state: 08003
|
||||
9
src/interfaces/ecpg/test/expected/connect-test6.stdout
Normal file
9
src/interfaces/ecpg/test/expected/connect-test6.stdout
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
Test 1: Try to get descriptor on a disconnected connection
|
||||
sqlca.sqlcode = -220
|
||||
Test 2: deallocate all with non-existent connection
|
||||
sqlca.sqlcode = -220
|
||||
Test 3: deallocate all on disconnected connection
|
||||
sqlca.sqlcode = -220
|
||||
Test 4: Use prepared statement from non-existent connection
|
||||
sqlca.sqlcode = -220
|
||||
All tests completed !
|
||||
Loading…
Reference in a new issue