diff --git a/ChangeLog.txt b/ChangeLog.txt index d27bec227af..99237045fe4 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -7,6 +7,13 @@ Entries may not always be in chronological/commit order. See license at the end of file. */ +2023-11-15 15:57 UTC+0100 Phil Krylov (phil a t krylov.eu) + * contrib/hbsqlit3/tests/backup.prg + + Simple change in test to provoke access to dangling pointer saved + by SQLITE3_TRACE(). + * contrib/hbsqlit3/core.c + ! Fixed dangling pointer access with SQLITE3_TRACE(), SQLITE3_PROFILE(). + 2023-11-14 22:27 UTC+0100 Phil Krylov (phil a t krylov.eu) * .github/workflows/linux-ci.yml * On Linux CI workflow, run tests with Valgrind. diff --git a/contrib/hbsqlit3/core.c b/contrib/hbsqlit3/core.c index c806f88d034..6e7e0f9e7d9 100644 --- a/contrib/hbsqlit3/core.c +++ b/contrib/hbsqlit3/core.c @@ -82,6 +82,9 @@ static int progress_handler( void * ); static int hook_commit( void * ); static void hook_rollback( void * ); static void func( sqlite3_context *, int, sqlite3_value ** ); +#if SQLITE_VERSION_NUMBER >= 3014000 +static int trace_handler( unsigned, void *, void *, void * ); +#endif typedef struct { @@ -92,6 +95,12 @@ typedef struct PHB_ITEM cbHookCommit; PHB_ITEM cbHookRollback; PHB_ITEM cbFunc; +#if SQLITE_VERSION_NUMBER >= 3014000 + PHB_ITEM cbTraceHandler; +#else + PHB_ITEM sProfileFileName; + PHB_ITEM sTraceFileName; +#endif } HB_SQLITE3, * PHB_SQLITE3; typedef struct @@ -154,6 +163,25 @@ static HB_GARBAGE_FUNC( hb_sqlite3_destructor ) pStructHolder->hbsqlite3->cbFunc = NULL; } +#if SQLITE_VERSION_NUMBER >= 3014000 + if( pStructHolder->hbsqlite3->cbTraceHandler ) + { + hb_itemRelease( pStructHolder->hbsqlite3->cbTraceHandler ); + pStructHolder->hbsqlite3->cbTraceHandler = NULL; + } +#else + if( pStructHolder->hbsqlite3->sProfileFileName ) + { + hb_itemRelease( pStructHolder->hbsqlite3->sProfileFileName ); + pStructHolder->hbsqlite3->sProfileFileName = NULL; + } + if( pStructHolder->hbsqlite3->sTraceFileName ) + { + hb_itemRelease( pStructHolder->hbsqlite3->sTraceFileName ); + pStructHolder->hbsqlite3->sTraceFileName = NULL; + } +#endif + hb_xfree( pStructHolder->hbsqlite3 ); pStructHolder->hbsqlite3 = NULL; } @@ -182,6 +210,22 @@ static HB_GARBAGE_FUNC( hb_sqlite3_mark ) if( pStructHolder->hbsqlite3->cbFunc ) hb_gcMark( pStructHolder->hbsqlite3->cbFunc ); + +#if SQLITE_VERSION_NUMBER >= 3014000 + if( pStructHolder->hbsqlite3->cbTraceHandler ) + { + hb_gcMark( pStructHolder->hbsqlite3->cbTraceHandler ); + } +#else + if( pStructHolder->hbsqlite3->sProfileFileName ) + { + hb_gcMark( pStructHolder->hbsqlite3->sProfileFileName ); + } + if( pStructHolder->hbsqlite3->sTraceFileName ) + { + hb_gcMark( pStructHolder->hbsqlite3->sTraceFileName ); + } +#endif } } @@ -1754,14 +1798,107 @@ HB_FUNC( SQLITE3_ENABLE_SHARED_CACHE ) hb_retni( sqlite3_enable_shared_cache( hb_parl( 1 ) ) ); } -/* TODO: implement sqlite3_trace_v2(), that replaces both of these deprecated functions */ /** Tracing And Profiling Functions - sqlite3_trace( db, lOnOff ) - sqlite3_profile( db, lOnOff ) + sqlite3_trace_v2( db, nMask, [ bCallback ] ) // Starting with 3.14.0 + + sqlite3_trace( db, lOnOff, [ filename ] ) // Deprecated in 3.14.0 + sqlite3_profile( db, lOnOff, [ filename ] ) // Deprecated in 3.14.0 */ +#if SQLITE_VERSION_NUMBER >= 3014000 + +static int trace_handler( unsigned uType, void *cbTraceHandler, void * p, void * x ) +{ + PHB_ITEM pCallback = ( PHB_ITEM ) cbTraceHandler; + + if( pCallback && hb_vmRequestReenter() ) + { + hb_vmPushEvalSym(); + hb_vmPush( pCallback ); + hb_vmPushNumInt( uType ); + switch( uType ) + { + case SQLITE_TRACE_STMT: + hb_vmPushPointer( p ); + hb_vmPushString( x ); + hb_vmSend( 3 ) + break; + case SQLITE_TRACE_PROFILE: + hb_vmPushPointer( p ); + hb_vmPushNumInt( *( sqlite3_uint64 * ) x ); + hb_vmSend( 3 ); + break; + case SQLITE_TRACE_ROW: + { + HB_SYMBOL_UNUSED( x ); + hb_vmPushPointer( p ); + hb_vmSend( 2 ); + break; + } + case SQLITE_TRACE_CLOSE: + { + PHB_ITEM pItem = hb_itemNew( NULL ); + hb_sqlite3_itemPut( pItem, p, HB_SQLITE3_DB ); + HB_SYMBOL_UNUSED( x ); + hb_vmPush( pItem ); + hb_vmSend( 2 ); + break; + } + } + iRes = hb_parni( -1 ); + hb_vmRequestRestore(); + return iRes; + } + return 0; +} + +HB_FUNC( SQLITE3_EXPANDED_SQL ) +{ + psqlite3_stmt pStmt = ( psqlite3_stmt ) hb_parptr( 1 ); + if( pStmt ) + { + char *sql = sqlite3_expanded_sql( pStmt ); + hb_retstr_utf8( sql ); + sqlite3_free( sql ); + } + else + hb_errRT_BASE_SubstR( EG_ARG, 0, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +} + +HB_FUNC( SQLITE3_TRACE_V2 ) +{ + HB_SQLITE3 * pHbSqlite3 = ( HB_SQLITE3 * ) hb_sqlite3_param( 1, HB_SQLITE3_DB, HB_TRUE ); + + if( pHbSqlite3 && pHbSqlite3->db ) + { + unsigned uMask = ( unsigned int ) hb_parnint( 2 ); + + if( pHbSqlite3->cbTraceHandler ) + { + hb_itemRelease( pHbSqlite3->cbTraceHandler ); + pHbSqlite3->cbTraceHandler = NULL; + } + + if( HB_ISEVALITEM( 3 ) ) + { + pHbSqlite3->cbTraceHandler = hb_itemNew( hb_param( 3, HB_IT_EVALITEM ) ); + hb_gcUnlock( pHbSqlite3->cbTraceHandler ); + + sqlite3_trace_v2( pHbSqlite3->db, uMask, + uMask ? trace_handler : NULL, + uMask ? ( void * ) pHbSqlite3->cbTraceHandler : NULL ); + } + else + sqlite3_trace_v2( pHbSqlite3->db, 0, NULL, NULL ); + } + else + hb_errRT_BASE_SubstR( EG_ARG, 0, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +} + +#else + static void SQL3ProfileLog( void * sFile, const char * sProfileMsg, sqlite3_uint64 uint64 ) { if( sProfileMsg ) @@ -1795,8 +1932,22 @@ HB_FUNC( SQLITE3_PROFILE ) HB_SQLITE3 * pHbSqlite3 = ( HB_SQLITE3 * ) hb_sqlite3_param( 1, HB_SQLITE3_DB, HB_TRUE ); if( pHbSqlite3 && pHbSqlite3->db ) - sqlite3_profile( pHbSqlite3->db, hb_parl( 2 ) ? SQL3ProfileLog : NULL, - HB_ISCHAR( 3 ) ? HB_UNCONST( hb_parcx( 3 ) ) : NULL ); + { + HB_BOOL bFlag = hb_parl( 2 ); + if( pHbSqlite3->sProfileFileName ) + { + hb_itemRelease( pHbSqlite3->sProfileFileName ); + pHbSqlite3->sProfileFileName = NULL; + } + if( bFlag && HB_ISCHAR( 3 ) ) + { + pHbSqlite3->sProfileFileName = hb_itemNew( hb_param( 3, HB_IT_STRING ) ); + hb_gcUnlock( pHbSqlite3->sProfileFileName ); + } + + sqlite3_profile( pHbSqlite3->db, bFlag ? SQL3ProfileLog : NULL, + pHbSqlite3->sProfileFileName ? HB_UNCONST( hb_itemGetCPtr( pHbSqlite3->sProfileFileName ) ) : NULL ); + } else hb_errRT_BASE_SubstR( EG_ARG, 0, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); } @@ -1806,12 +1957,29 @@ HB_FUNC( SQLITE3_TRACE ) HB_SQLITE3 * pHbSqlite3 = ( HB_SQLITE3 * ) hb_sqlite3_param( 1, HB_SQLITE3_DB, HB_TRUE ); if( pHbSqlite3 && pHbSqlite3->db ) - sqlite3_trace( pHbSqlite3->db, hb_parl( 2 ) ? SQL3TraceLog : NULL, - HB_ISCHAR( 3 ) ? HB_UNCONST( hb_parcx( 3 ) ) : NULL ); + { + HB_BOOL bFlag = hb_parl( 2 ); + if( pHbSqlite3->sTraceFileName ) + { + hb_itemRelease( pHbSqlite3->sTraceFileName ); + pHbSqlite3->sTraceFileName = NULL; + } + if( bFlag && HB_ISCHAR( 3 ) ) + { + pHbSqlite3->sTraceFileName = hb_itemNew( hb_param( 3, HB_IT_STRING ) ); + hb_gcUnlock( pHbSqlite3->sTraceFileName ); + } + + sqlite3_trace( pHbSqlite3->db, bFlag ? SQL3TraceLog : NULL, + pHbSqlite3->sTraceFileName ? HB_UNCONST( hb_itemGetCPtr( pHbSqlite3->sTraceFileName ) ) : NULL ); + } else hb_errRT_BASE_SubstR( EG_ARG, 0, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); } +#endif + + /** BLOB Import/export */ diff --git a/contrib/hbsqlit3/tests/backup.prg b/contrib/hbsqlit3/tests/backup.prg index e8fe11ad4c3..a4cfe4a7215 100644 --- a/contrib/hbsqlit3/tests/backup.prg +++ b/contrib/hbsqlit3/tests/backup.prg @@ -60,6 +60,10 @@ #require "hbsqlit3" +PROCEDURE init_trace( pDbDest, cPrefix ) + sqlite3_trace( pDbDest, .T., cPrefix + ".log" ) + RETURN + PROCEDURE Main() LOCAL cFileSource := ":memory:", cFileDest := "backup.db", cSQLTEXT @@ -85,7 +89,7 @@ PROCEDURE Main() RETURN ENDIF - sqlite3_trace( pDbDest, .T., "backup.log" ) + init_trace( pDbDest, "backup" ) pBackup := sqlite3_backup_init( pDbDest, "main", pDbSource, "main" ) IF Empty( pBackup )