mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-30 19:03:16 +03:00
Resolve two assertions in the auto-extension JNI which were triggered via new SQLTester infrastructure. Move SQLTester's db-init SQL injection into an auto-extension.
FossilOrigin-Name: 2952906c30bc2b7987f2c39837d56bd121f5817dc094e6ccdb6d4eea5e9b8d17
This commit is contained in:
@ -1877,13 +1877,14 @@ WRAP_INT_SVALUE(1value_1type, sqlite3_value_type)
|
||||
|
||||
#if S3JNI_ENABLE_AUTOEXT
|
||||
/* Central auto-extension handler. */
|
||||
FIXME_THREADING
|
||||
static int s3jni_auto_extension(sqlite3 *pDb, const char **pzErr,
|
||||
const struct sqlite3_api_routines *ignored){
|
||||
S3JniAutoExtension const * pAX = S3JniGlobal.autoExt.pHead;
|
||||
int rc;
|
||||
JNIEnv * env = 0;
|
||||
S3JniDb * const ps = S3JniGlobal.autoExt.psOpening;
|
||||
|
||||
//MARKER(("auto-extension on open()ing ps@%p db@%p\n", ps, pDb));
|
||||
S3JniGlobal.autoExt.psOpening = 0;
|
||||
if( !pAX ){
|
||||
assert( 0==S3JniGlobal.autoExt.isRunning );
|
||||
@ -2089,6 +2090,7 @@ static jint s3jni_close_db(JNIEnv * const env, jobject jDb, int version){
|
||||
assert(version == 1 || version == 2);
|
||||
ps = S3JniDb_for_db(env, jDb, 0, 0);
|
||||
if(ps){
|
||||
//MARKER(("close()ing db@%p\n", ps->pDb));
|
||||
rc = 1==version ? (jint)sqlite3_close(ps->pDb) : (jint)sqlite3_close_v2(ps->pDb);
|
||||
S3JniDb_set_aside(ps)
|
||||
/* MUST come after close() because of ps->trace. */;
|
||||
@ -2566,6 +2568,7 @@ static int s3jni_open_pre(JNIEnv * const env, S3JniEnvCache **jc,
|
||||
S3JniGlobal.autoExt.psOpening = *ps;
|
||||
}
|
||||
#endif
|
||||
//MARKER(("pre-open ps@%p\n", *ps));
|
||||
return *ps ? 0 : SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
@ -2582,14 +2585,17 @@ static int s3jni_open_pre(JNIEnv * const env, S3JniEnvCache **jc,
|
||||
*/
|
||||
static int s3jni_open_post(JNIEnv * const env, S3JniDb * ps,
|
||||
sqlite3 **ppDb, jobject jOut, int theRc){
|
||||
//MARKER(("post-open() ps@%p db@%p\n", ps, *ppDb));
|
||||
#if S3JNI_ENABLE_AUTOEXT
|
||||
assert( S3JniGlobal.autoExt.pHead ? 0==S3JniGlobal.autoExt.psOpening : 1 );
|
||||
assert( S3JniGlobal.autoExt.pHead ? ps!=S3JniGlobal.autoExt.psOpening : 1 );
|
||||
S3JniGlobal.autoExt.psOpening = 0;
|
||||
#endif
|
||||
if(*ppDb){
|
||||
assert(ps->jDb);
|
||||
#if S3JNI_ENABLE_AUTOEXT
|
||||
assert( S3JniGlobal.autoExt.pHead ? *ppDb==ps->pDb : 0==ps->pDb );
|
||||
//MARKER(("*autoExt.pHead=%p, ppDb=%p, ps->pDb=%p\n", S3JniGlobal.autoExt.pHead, *ppDb, ps->pDb));
|
||||
// invalid when an autoext triggers another open():
|
||||
// assert( S3JniGlobal.autoExt.pHead ? *ppDb==ps->pDb : 0==ps->pDb );
|
||||
#endif
|
||||
ps->pDb = *ppDb;
|
||||
NativePointerHolder_set(env, ps->jDb, *ppDb, S3JniClassNames.sqlite3);
|
||||
@ -2607,13 +2613,17 @@ JDECL(jint,1open)(JENV_CSELF, jstring strName, jobject jOut){
|
||||
jobject jDb = 0;
|
||||
S3JniDb * ps = 0;
|
||||
S3JniEnvCache * jc = 0;
|
||||
S3JniDb * const prevOpening = S3JniGlobal.autoExt.psOpening;
|
||||
int rc = s3jni_open_pre(env, &jc, strName, &zName, &ps, &jDb);
|
||||
if( rc ) return rc;
|
||||
rc = sqlite3_open(zName, &pOut);
|
||||
//MARKER(("env=%p, *env=%p\n", env, *env));
|
||||
rc = s3jni_open_post(env, ps, &pOut, jOut, rc);
|
||||
assert(rc==0 ? pOut!=0 : 1);
|
||||
sqlite3_free(zName);
|
||||
if( 0==rc ){
|
||||
rc = sqlite3_open(zName, &pOut);
|
||||
//MARKER(("env=%p, *env=%p\n", env, *env));
|
||||
//MARKER(("open() ps@%p db@%p\n", ps, pOut));
|
||||
rc = s3jni_open_post(env, ps, &pOut, jOut, rc);
|
||||
assert(rc==0 ? pOut!=0 : 1);
|
||||
sqlite3_free(zName);
|
||||
}
|
||||
S3JniGlobal.autoExt.psOpening = prevOpening;
|
||||
return (jint)rc;
|
||||
}
|
||||
|
||||
@ -2625,6 +2635,7 @@ JDECL(jint,1open_1v2)(JENV_CSELF, jstring strName,
|
||||
S3JniDb * ps = 0;
|
||||
S3JniEnvCache * jc = 0;
|
||||
char *zVfs = 0;
|
||||
S3JniDb * const prevOpening = S3JniGlobal.autoExt.psOpening;
|
||||
int rc = s3jni_open_pre(env, &jc, strName, &zName, &ps, &jDb);
|
||||
if( 0==rc && strVfs ){
|
||||
zVfs = s3jni_jstring_to_utf8(jc, strVfs, 0);
|
||||
@ -2635,12 +2646,14 @@ JDECL(jint,1open_1v2)(JENV_CSELF, jstring strName,
|
||||
if( 0==rc ){
|
||||
rc = sqlite3_open_v2(zName, &pOut, (int)flags, zVfs);
|
||||
}
|
||||
//MARKER(("open_v2() ps@%p db@%p\n", ps, pOut));
|
||||
/*MARKER(("zName=%s, zVfs=%s, pOut=%p, flags=%d, nrc=%d\n",
|
||||
zName, zVfs, pOut, (int)flags, nrc));*/
|
||||
rc = s3jni_open_post(env, ps, &pOut, jOut, rc);
|
||||
assert(rc==0 ? pOut!=0 : 1);
|
||||
sqlite3_free(zName);
|
||||
sqlite3_free(zVfs);
|
||||
S3JniGlobal.autoExt.psOpening = prevOpening;
|
||||
return (jint)rc;
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,8 @@ public interface AutoExtension {
|
||||
As an exception (as it were) to the callbacks-must-not-throw
|
||||
rule, AutoExtensions may do so and the exception's error message
|
||||
will be set as the db's error string.
|
||||
|
||||
Results are undefined if db is closed by an auto-extension.
|
||||
*/
|
||||
int xEntryPoint(sqlite3 db);
|
||||
}
|
||||
|
@ -188,22 +188,25 @@ public final class SQLite3Jni {
|
||||
*/
|
||||
public static synchronized native int sqlite3_auto_extension(@NotNull AutoExtension callback);
|
||||
|
||||
public static int sqlite3_bind_blob(@NotNull sqlite3_stmt stmt, int ndx,
|
||||
@Nullable byte[] data){
|
||||
public static int sqlite3_bind_blob(
|
||||
@NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data
|
||||
){
|
||||
return (null == data)
|
||||
? sqlite3_bind_null(stmt, ndx)
|
||||
: sqlite3_bind_blob(stmt, ndx, data, data.length);
|
||||
}
|
||||
|
||||
private static native int sqlite3_bind_blob(@NotNull sqlite3_stmt stmt,
|
||||
int ndx, @Nullable byte[] data,
|
||||
int n);
|
||||
private static native int sqlite3_bind_blob(
|
||||
@NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data, int n
|
||||
);
|
||||
|
||||
public static native int sqlite3_bind_double(@NotNull sqlite3_stmt stmt,
|
||||
int ndx, double v);
|
||||
public static native int sqlite3_bind_double(
|
||||
@NotNull sqlite3_stmt stmt, int ndx, double v
|
||||
);
|
||||
|
||||
public static native int sqlite3_bind_int(@NotNull sqlite3_stmt stmt,
|
||||
int ndx, int v);
|
||||
public static native int sqlite3_bind_int(
|
||||
@NotNull sqlite3_stmt stmt, int ndx, int v
|
||||
);
|
||||
|
||||
public static native int sqlite3_bind_int64(@NotNull sqlite3_stmt stmt,
|
||||
int ndx, long v);
|
||||
@ -257,10 +260,10 @@ public final class SQLite3Jni {
|
||||
to clear the busy handler. Calling this multiple times with the
|
||||
same object is a no-op on the second and subsequent calls.
|
||||
*/
|
||||
public static native int sqlite3_busy_handler(@NotNull sqlite3 db,
|
||||
public static native synchronized int sqlite3_busy_handler(@NotNull sqlite3 db,
|
||||
@Nullable BusyHandler handler);
|
||||
|
||||
public static native int sqlite3_busy_timeout(@NotNull sqlite3 db, int ms);
|
||||
public static native synchronized int sqlite3_busy_timeout(@NotNull sqlite3 db, int ms);
|
||||
|
||||
/**
|
||||
Works like the C API except that it returns false, without side
|
||||
@ -489,12 +492,12 @@ public final class SQLite3Jni {
|
||||
or sqlite3_open_v2() so that they have a predictible object to
|
||||
pass to, e.g., the sqlite3_collation_needed() callback.
|
||||
*/
|
||||
public static native int sqlite3_open(@Nullable String filename,
|
||||
@NotNull OutputPointer.sqlite3 ppDb);
|
||||
public static native synchronized int sqlite3_open(@Nullable String filename,
|
||||
@NotNull OutputPointer.sqlite3 ppDb);
|
||||
|
||||
public static native int sqlite3_open_v2(@Nullable String filename,
|
||||
@NotNull OutputPointer.sqlite3 ppDb,
|
||||
int flags, @Nullable String zVfs);
|
||||
public static native synchronized int sqlite3_open_v2(@Nullable String filename,
|
||||
@NotNull OutputPointer.sqlite3 ppDb,
|
||||
int flags, @Nullable String zVfs);
|
||||
|
||||
/**
|
||||
The sqlite3_prepare() family of functions require slightly
|
||||
|
@ -227,7 +227,9 @@ public class SQLTester {
|
||||
//verbose("Added file ",filename);
|
||||
}
|
||||
|
||||
private void setupInitialDb() throws Exception {
|
||||
private void setupInitialDb() throws DbException {
|
||||
outln("setupInitialDb()");
|
||||
closeDb(0);
|
||||
Util.unlink(initialDbName);
|
||||
openDb(0, initialDbName, true);
|
||||
}
|
||||
@ -245,7 +247,6 @@ public class SQLTester {
|
||||
public void runTests() throws Exception {
|
||||
for(String f : listInFiles){
|
||||
reset();
|
||||
setupInitialDb();
|
||||
++nTestFile;
|
||||
final TestScript ts = new TestScript(f);
|
||||
outln(nextStartEmoji(), " starting [",f,"]");
|
||||
@ -289,13 +290,14 @@ public class SQLTester {
|
||||
if(addNL) resultBuffer.append('\n');
|
||||
}
|
||||
|
||||
void appendDbInitSql(String n) throws SQLTesterException {
|
||||
void appendDbInitSql(String n) throws DbException {
|
||||
dbInitSql.append(n).append('\n');
|
||||
if( null!=getCurrentDb() ){
|
||||
//outln("RUNNING DB INIT CODE: ",n);
|
||||
execSql(null, true, ResultBufferMode.NONE, null, n);
|
||||
}
|
||||
}
|
||||
String getDbInitSql(){ return dbInitSql.toString(); }
|
||||
|
||||
String getInputText(){ return inputBuffer.toString(); }
|
||||
|
||||
@ -330,7 +332,7 @@ public class SQLTester {
|
||||
return affirmDbId(id).aDb[id];
|
||||
}
|
||||
|
||||
void closeDb(int id) throws Exception{
|
||||
void closeDb(int id) {
|
||||
final sqlite3 db = affirmDbId(id).aDb[id];
|
||||
if( null != db ){
|
||||
sqlite3_close_v2(db);
|
||||
@ -338,7 +340,7 @@ public class SQLTester {
|
||||
}
|
||||
}
|
||||
|
||||
void closeDb() throws Exception { closeDb(iCurrentDb); }
|
||||
void closeDb() { closeDb(iCurrentDb); }
|
||||
|
||||
void closeAllDbs(){
|
||||
for(int i = 0; i<aDb.length; ++i){
|
||||
@ -347,7 +349,7 @@ public class SQLTester {
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3 openDb(String name, boolean createIfNeeded) throws Exception {
|
||||
sqlite3 openDb(String name, boolean createIfNeeded) throws DbException {
|
||||
closeDb();
|
||||
int flags = SQLITE_OPEN_READWRITE;
|
||||
if( createIfNeeded ) flags |= SQLITE_OPEN_CREATE;
|
||||
@ -365,7 +367,7 @@ public class SQLTester {
|
||||
return aDb[iCurrentDb] = db;
|
||||
}
|
||||
|
||||
sqlite3 openDb(int slot, String name, boolean createIfNeeded) throws Exception {
|
||||
sqlite3 openDb(int slot, String name, boolean createIfNeeded) throws DbException {
|
||||
affirmDbId(slot);
|
||||
iCurrentDb = slot;
|
||||
return openDb(name, createIfNeeded);
|
||||
@ -384,6 +386,7 @@ public class SQLTester {
|
||||
nullView = "nil";
|
||||
emitColNames = false;
|
||||
iCurrentDb = 0;
|
||||
dbInitSql.append("SELECT 1;");
|
||||
}
|
||||
|
||||
void setNullValue(String v){nullView = v;}
|
||||
@ -459,12 +462,19 @@ public class SQLTester {
|
||||
|
||||
appendMode specifies how/whether to append results to the result
|
||||
buffer. lineMode specifies whether to output all results in a
|
||||
single line or one line per row.
|
||||
single line or one line per row. If appendMode is
|
||||
ResultBufferMode.NONE then lineMode is ignored and may be null.
|
||||
*/
|
||||
public int execSql(sqlite3 db, boolean throwOnError,
|
||||
ResultBufferMode appendMode,
|
||||
ResultRowMode lineMode,
|
||||
ResultBufferMode appendMode, ResultRowMode lineMode,
|
||||
String sql) throws SQLTesterException {
|
||||
if( null==db && null==aDb[0] ){
|
||||
// Delay opening of the initial db to enable tests to change its
|
||||
// name and inject on-connect code via, e.g., the MEMDB
|
||||
// directive. this setup as the potential to misinteract with
|
||||
// auto-extension timing and must be done carefully.
|
||||
setupInitialDb();
|
||||
}
|
||||
final OutputPointer.Int32 oTail = new OutputPointer.Int32();
|
||||
final OutputPointer.sqlite3_stmt outStmt = new OutputPointer.sqlite3_stmt();
|
||||
final byte[] sqlUtf8 = sql.getBytes(StandardCharsets.UTF_8);
|
||||
@ -568,7 +578,6 @@ public class SQLTester {
|
||||
public static void main(String[] argv) throws Exception{
|
||||
installCustomExtensions();
|
||||
final SQLTester t = new SQLTester();
|
||||
boolean v2 = false;
|
||||
for(String a : argv){
|
||||
if(a.startsWith("-")){
|
||||
final String flag = a.replaceFirst("-+","");
|
||||
@ -582,9 +591,22 @@ public class SQLTester {
|
||||
}
|
||||
t.addTestScript(a);
|
||||
}
|
||||
final AutoExtension ax = new AutoExtension() {
|
||||
private final SQLTester tester = t;
|
||||
public int xEntryPoint(sqlite3 db){
|
||||
tester.outln("AutoExtension running db init code on ",db);
|
||||
final String init = tester.getDbInitSql();
|
||||
if( !init.isEmpty() ){
|
||||
tester.execSql(db, true, ResultBufferMode.NONE, null, init);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
sqlite3_auto_extension(ax);
|
||||
try {
|
||||
t.runTests();
|
||||
}finally{
|
||||
sqlite3_cancel_auto_extension(ax);
|
||||
t.outln("Processed ",t.nTotalTest," test(s) in ",t.nTestFile," file(s).");
|
||||
if( t.nAbortedScript > 0 ){
|
||||
t.outln("Aborted ",t.nAbortedScript," script(s).");
|
||||
|
Reference in New Issue
Block a user