Handling out-of-memory
Heikki Linnakangas <[email protected]>
| Newsgroups | gmane.comp.db.unixodbc.devel |
|---|---|
| Organization | VMware |
| Message-ID | <[email protected]> |
Hi, While working on the PostgreSQL driver, I bumped into two little bugs in handling out-of-memory situation. Patch attached. It's probably self-evident from the patch what the bugs are, but I'll explain anyway: 1. In __post_internal_error_ex, check for NULL return from malloc, and avoid some unnecessary allocations so that we don't need to handle the case that they fail. (In my test case, the reason that __post_internal_error_ex got called in the first place was that dlopen() failed with Out-of-Memory while loading the driver, so it's not surprising that those allocations failed too) 2. In __alloc_desc, there is a check for calloc returning NULL, but some of the initialization code was misplaced, and was being called on the NULL pointer anyway. There are a lot more places where we don't check for malloc returning NULL, but fixing those two made my test case work. In the test case, I opened a large number of connections, and to induce the OOM condition, I ran it with a small "ulimit -v". - Heikki _______________________________________________ unixODBC-dev mailing list [email protected] http://mailman.unixodbc.org/mailman/listinfo/unixodbc-dev
fix-oom-crash.patch
(text/x-diff, 9 KB)
*** DriverManager/__info.c (revision 72)
--- DriverManager/__info.c (working copy)
***************
*** 3832,3838 ****
SQLCHAR msg[ SQL_MAX_MESSAGE_LENGTH + 32 ];
ERROR *e1, *e2;
- SQLWCHAR *tmp;
/*
* add our prefix
--- 3832,3837 ----
***************
*** 3842,3862 ****
strcat((char*) msg, (char*) message_text );
e1 = malloc( sizeof( ERROR ));
e2 = malloc( sizeof( ERROR ));
memset( e1, 0, sizeof( *e1 ));
memset( e2, 0, sizeof( *e2 ));
e1 -> native_error = native_error;
e2 -> native_error = native_error;
! tmp = ansi_to_unicode_alloc( sqlstate, SQL_NTS, __get_connection( error_header ));
! wide_strcpy( e1 -> sqlstate, tmp );
! wide_strcpy( e2 -> sqlstate, tmp );
! free( tmp );
! tmp = ansi_to_unicode_alloc( msg, SQL_NTS, __get_connection( error_header ) );
! e1 -> msg = wide_strdup( tmp );
! e2 -> msg = wide_strdup( tmp );
! free( tmp );
e1 -> return_val = SQL_ERROR;
e2 -> return_val = SQL_ERROR;
--- 3841,3880 ----
strcat((char*) msg, (char*) message_text );
e1 = malloc( sizeof( ERROR ));
+ if (e1 == NULL)
+ return;
e2 = malloc( sizeof( ERROR ));
+ if (e2 == NULL)
+ {
+ free(e1);
+ return;
+ }
memset( e1, 0, sizeof( *e1 ));
memset( e2, 0, sizeof( *e2 ));
e1 -> native_error = native_error;
e2 -> native_error = native_error;
! ansi_to_unicode_copy(e1 -> sqlstate,
! sqlstate, SQL_NTS, __get_connection( error_header ));
! wide_strcpy( e2 -> sqlstate, e1 -> sqlstate );
!
! e1 -> msg = ansi_to_unicode_alloc( msg, SQL_NTS, __get_connection( error_header ) );
! if ( !e1 -> msg )
! {
! free( e1 );
! free( e2 );
! return;
! }
! e2 -> msg = wide_strdup( e1 -> msg );
! if ( !e2 -> msg )
! {
! free( e1 -> msg);
! free( e1 );
! free( e2 );
! return;
! }
!
e1 -> return_val = SQL_ERROR;
e2 -> return_val = SQL_ERROR;
***************
*** 3879,3908 ****
e2 -> diag_row_number = 0;
if ( class_origin == SUBCLASS_ODBC )
! tmp = ansi_to_unicode_alloc((SQLCHAR*) "ODBC 3.0", SQL_NTS, __get_connection( error_header ) );
else
! tmp = ansi_to_unicode_alloc((SQLCHAR*) "ISO 9075", SQL_NTS, __get_connection( error_header ) );
! wide_strcpy( e1 -> diag_class_origin, tmp );
! wide_strcpy( e2 -> diag_class_origin, tmp );
! free( tmp );
if ( subclass_origin == SUBCLASS_ODBC )
! tmp = ansi_to_unicode_alloc((SQLCHAR*) "ODBC 3.0", SQL_NTS, __get_connection( error_header ) );
else
! tmp = ansi_to_unicode_alloc((SQLCHAR*) "ISO 9075", SQL_NTS, __get_connection( error_header ) );
! wide_strcpy( e1 -> diag_subclass_origin, tmp );
! wide_strcpy( e2 -> diag_subclass_origin, tmp );
! free( tmp );
! tmp = ansi_to_unicode_alloc((SQLCHAR*) "", SQL_NTS, __get_connection( error_header ) );
! wide_strcpy( e1 -> diag_connection_name, tmp );
! wide_strcpy( e2 -> diag_connection_name, tmp );
! free( tmp );
! tmp = ansi_to_unicode_alloc((SQLCHAR*) "", SQL_NTS, __get_connection( error_header ) );
! wide_strcpy( e1 -> diag_server_name, tmp );
! wide_strcpy( e2 -> diag_server_name, tmp );
! free( tmp );
/*
* the list for SQLError puts both local and driver
--- 3897,3924 ----
e2 -> diag_row_number = 0;
if ( class_origin == SUBCLASS_ODBC )
! ansi_to_unicode_copy( e1 -> diag_class_origin, (SQLCHAR*) "ODBC 3.0",
! SQL_NTS, __get_connection( error_header ) );
else
! ansi_to_unicode_copy( e1 -> diag_class_origin, (SQLCHAR*) "ISO 9075",
! SQL_NTS, __get_connection( error_header ) );
! wide_strcpy( e2 -> diag_class_origin, e1 -> diag_class_origin );
if ( subclass_origin == SUBCLASS_ODBC )
! ansi_to_unicode_copy( e1 -> diag_subclass_origin, (SQLCHAR*) "ODBC 3.0",
! SQL_NTS, __get_connection( error_header ) );
else
! ansi_to_unicode_copy( e1 -> diag_subclass_origin, (SQLCHAR*) "ISO 9075",
! SQL_NTS, __get_connection( error_header ) );
! wide_strcpy( e2 -> diag_subclass_origin, e1 -> diag_subclass_origin );
! ansi_to_unicode_copy( e1 -> diag_connection_name, (SQLCHAR*) "", SQL_NTS,
! __get_connection( error_header ) );
! wide_strcpy( e2 -> diag_connection_name, e1 -> diag_connection_name );
! ansi_to_unicode_copy( e1 -> diag_server_name, (SQLCHAR*) "", SQL_NTS,
! __get_connection( error_header ) );
! wide_strcpy( e2 -> diag_server_name, e1 -> diag_server_name );
/*
* the list for SQLError puts both local and driver
***************
*** 3925,3944 ****
* leave space for the error prefix
*/
! SQLWCHAR msg[ SQL_MAX_MESSAGE_LENGTH + 32 ], *tmp;
ERROR *e1, *e2;
/*
* add our prefix
*/
! tmp = ansi_to_unicode_alloc((SQLCHAR*) ERROR_PREFIX, SQL_NTS, __get_connection( error_header ));
! wide_strcpy( msg, tmp );
! free( tmp );
wide_strcat( msg, message_text );
e1 = malloc( sizeof( ERROR ));
e2 = malloc( sizeof( ERROR ));
memset( e1, 0, sizeof( *e1 ));
memset( e2, 0, sizeof( *e2 ));
--- 3941,3966 ----
* leave space for the error prefix
*/
! SQLWCHAR msg[ SQL_MAX_MESSAGE_LENGTH + 32 ];
ERROR *e1, *e2;
/*
* add our prefix
*/
! ansi_to_unicode_copy(msg, (SQLCHAR*) ERROR_PREFIX, SQL_NTS,
! __get_connection( error_header ));
wide_strcat( msg, message_text );
e1 = malloc( sizeof( ERROR ));
+ if ( !e1 )
+ return;
e2 = malloc( sizeof( ERROR ));
+ if ( !e2 )
+ {
+ free(e1);
+ return;
+ }
memset( e1, 0, sizeof( *e1 ));
memset( e2, 0, sizeof( *e2 ));
***************
*** 3971,3990 ****
e2 -> diag_row_number = 0;
if ( class_origin == SUBCLASS_ODBC )
! tmp = ansi_to_unicode_alloc((SQLCHAR*) "ODBC 3.0", SQL_NTS, __get_connection( error_header ) );
else
! tmp = ansi_to_unicode_alloc((SQLCHAR*) "ISO 9075", SQL_NTS, __get_connection( error_header ) );
! wide_strcpy( e1 -> diag_class_origin, tmp );
! wide_strcpy( e2 -> diag_class_origin, tmp );
! free( tmp );
if ( subclass_origin == SUBCLASS_ODBC )
! tmp = ansi_to_unicode_alloc((SQLCHAR*) "ODBC 3.0", SQL_NTS, __get_connection( error_header ) );
else
! tmp = ansi_to_unicode_alloc((SQLCHAR*) "ISO 9075", SQL_NTS, __get_connection( error_header ) );
! wide_strcpy( e1 -> diag_subclass_origin, tmp );
! wide_strcpy( e2 -> diag_subclass_origin, tmp );
! free( tmp );
e1 -> diag_connection_name[ 0 ] = 0;
e2 -> diag_connection_name[ 0 ] = 0;
--- 3993,4012 ----
e2 -> diag_row_number = 0;
if ( class_origin == SUBCLASS_ODBC )
! ansi_to_unicode_copy( e1 -> diag_class_origin, (SQLCHAR*) "ODBC 3.0",
! SQL_NTS, __get_connection( error_header ) );
else
! ansi_to_unicode_copy( e1 -> diag_class_origin, (SQLCHAR*) "ISO 9075",
! SQL_NTS, __get_connection( error_header ) );
! wide_strcpy( e2 -> diag_class_origin, e1 -> diag_class_origin );
if ( subclass_origin == SUBCLASS_ODBC )
! ansi_to_unicode_copy( e1 -> diag_subclass_origin, (SQLCHAR*) "ODBC 3.0",
! SQL_NTS, __get_connection( error_header ) );
else
! ansi_to_unicode_copy( e1 ->diag_subclass_origin, (SQLCHAR*) "ISO 9075",
! SQL_NTS, __get_connection( error_header ) );
! wide_strcpy( e2 -> diag_subclass_origin, e1 -> diag_subclass_origin );
e1 -> diag_connection_name[ 0 ] = 0;
e2 -> diag_connection_name[ 0 ] = 0;
*** DriverManager/__handles.c (revision 72)
--- DriverManager/__handles.c (working copy)
***************
*** 1354,1360 ****
DMHDESC __alloc_desc( void )
{
! DMHDESC descriptor = NULL;
mutex_entry( &mutex_lists );
--- 1354,1360 ----
DMHDESC __alloc_desc( void )
{
! DMHDESC descriptor;
mutex_entry( &mutex_lists );
***************
*** 1375,1392 ****
#endif
descriptor_root = descriptor;
descriptor -> type = HDESC_MAGIC;
- }
! setup_error_head( &descriptor -> error, descriptor,
! SQL_HANDLE_DESC );
#ifdef HAVE_LIBPTH
! pth_mutex_init( &descriptor -> mutex );
#elif HAVE_LIBPTHREAD
! pthread_mutex_init( &descriptor -> mutex, NULL );
#elif HAVE_LIBTHREAD
! mutex_init( &descriptor -> mutex, USYNC_THREAD, NULL );
#endif
mutex_exit( &mutex_lists );
--- 1375,1391 ----
#endif
descriptor_root = descriptor;
descriptor -> type = HDESC_MAGIC;
! setup_error_head( &descriptor -> error, descriptor, SQL_HANDLE_DESC );
#ifdef HAVE_LIBPTH
! pth_mutex_init( &descriptor -> mutex );
#elif HAVE_LIBPTHREAD
! pthread_mutex_init( &descriptor -> mutex, NULL );
#elif HAVE_LIBTHREAD
! mutex_init( &descriptor -> mutex, USYNC_THREAD, NULL );
#endif
+ }
mutex_exit( &mutex_lists );