diff options
Diffstat (limited to 'ext/jni/src')
-rw-r--r-- | ext/jni/src/c/sqlite3-jni.c | 37 | ||||
-rw-r--r-- | ext/jni/src/org/sqlite/jni/capi/Tester1.java | 17 | ||||
-rw-r--r-- | ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java | 19 |
3 files changed, 67 insertions, 6 deletions
diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index aa0c15c12..14c447acd 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -1995,13 +1995,16 @@ error_oom: /* ** Requires that jCx and jArgv are sqlite3_context -** resp. array-of-sqlite3_value values initialized by udf_args(). This +** resp. array-of-sqlite3_value values initialized by udf_args(). The +** latter will be 0-and-NULL for UDF types with no arguments. This ** function zeroes out the nativePointer member of jCx and each entry ** in jArgv. This is a safety-net precaution to avoid undefined -** behavior if a Java-side UDF holds a reference to one of its -** arguments. This MUST be called from any function which successfully -** calls udf_args(), after calling the corresponding UDF and checking -** its exception status. It MUST NOT be called in any other case. +** behavior if a Java-side UDF holds a reference to its context or one +** of its arguments. This MUST be called from any function which +** successfully calls udf_args(), after calling the corresponding UDF +** and checking its exception status, or which Java-wraps a +** sqlite3_context for use with a UDF(ish) call. It MUST NOT be called +** in any other case. */ static void udf_unargs(JNIEnv *env, jobject jCx, int argc, jobjectArray jArgv){ int i = 0; @@ -2009,8 +2012,29 @@ static void udf_unargs(JNIEnv *env, jobject jCx, int argc, jobjectArray jArgv){ NativePointerHolder_set(S3JniNph(sqlite3_context), jCx, 0); for( ; i < argc; ++i ){ jobject jsv = (*env)->GetObjectArrayElement(env, jArgv, i); + /* + ** There is a potential Java-triggerable case of Undefined + ** Behavior here, but it would require intentional misuse of the + ** API: + ** + ** If a Java UDF grabs an sqlite3_value from its argv and then + ** assigns that element to null, it becomes unreachable to us so + ** we cannot clear out its pointer. That Java-side object's + ** getNativePointer() will then refer to a stale value, so passing + ** it into (e.g.) sqlite3_value_SOMETHING() would invoke UB. + ** + ** High-level wrappers can avoid that possibility if they do not + ** expose sqlite3_value directly to clients (as is the case in + ** org.sqlite.jni.wrapper1.SqlFunction). + ** + ** One potential (but expensive) workaround for this would be to + ** privately store a duplicate argv array in each sqlite3_context + ** wrapper object, and clear the native pointers from that copy. + */ assert(jsv && "Someone illegally modified a UDF argument array."); - NativePointerHolder_set(S3JniNph(sqlite3_value), jsv, 0); + if( jsv ){ + NativePointerHolder_set(S3JniNph(sqlite3_value), jsv, 0); + } } } @@ -2099,6 +2123,7 @@ static int udf_xFV(sqlite3_context* cx, S3JniUdf * s, rc = udf_report_exception(env, isFinal, cx, s->zFuncName, zFuncType); } + udf_unargs(env, jcx, 0, 0); S3JniUnrefLocal(jcx); }else{ if( isFinal ) sqlite3_result_error_nomem(cx); diff --git a/ext/jni/src/org/sqlite/jni/capi/Tester1.java b/ext/jni/src/org/sqlite/jni/capi/Tester1.java index d21d75e3b..05b1cfeae 100644 --- a/ext/jni/src/org/sqlite/jni/capi/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/capi/Tester1.java @@ -928,15 +928,28 @@ public class Tester1 implements Runnable { // To confirm that xFinal() is called with no aggregate state // when the corresponding result set is empty. new ValueHolder<>(false); + final ValueHolder<sqlite3_value[]> neverEverDoThisInClientCode = new ValueHolder<>(null); + final ValueHolder<sqlite3_context> neverEverDoThisInClientCode2 = new ValueHolder<>(null); SQLFunction func = new AggregateFunction<Integer>(){ @Override public void xStep(sqlite3_context cx, sqlite3_value[] args){ + if( null==neverEverDoThisInClientCode.value ){ + /* !!!NEVER!!! hold a reference to an sqlite3_value or + sqlite3_context object like this in client code! They + are ONLY legal for the duration of their single + call. We do it here ONLY to test that the defenses + against clients doing this are working. */ + neverEverDoThisInClientCode.value = args; + } final ValueHolder<Integer> agg = this.getAggregateState(cx, 0); agg.value += sqlite3_value_int(args[0]); affirm( agg == this.getAggregateState(cx, 0) ); } @Override public void xFinal(sqlite3_context cx){ + if( null==neverEverDoThisInClientCode2.value ){ + neverEverDoThisInClientCode2.value = cx; + } final Integer v = this.takeAggregateState(cx); if(null == v){ xFinalNull.value = true; @@ -961,6 +974,10 @@ public class Tester1 implements Runnable { } affirm( 1==n ); affirm(!xFinalNull.value); + affirm( null!=neverEverDoThisInClientCode.value ); + affirm( null!=neverEverDoThisInClientCode2.value ); + affirm( 0<neverEverDoThisInClientCode.value.length ); + affirm( 0==neverEverDoThisInClientCode2.value.getNativePointer() ); sqlite3_reset(stmt); affirm( 1==sqlite3_stmt_status(stmt, SQLITE_STMTSTATUS_RUN, false) ); // Ensure that the accumulator is reset on subsequent calls... diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java index 5dbf79466..de131e854 100644 --- a/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java +++ b/ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java @@ -33,6 +33,7 @@ public final class Sqlite implements AutoCloseable { private static final boolean JNI_SUPPORTS_NIO = CApi.sqlite3_jni_supports_nio(); + // Result codes public static final int OK = CApi.SQLITE_OK; public static final int ERROR = CApi.SQLITE_ERROR; public static final int INTERNAL = CApi.SQLITE_INTERNAL; @@ -138,14 +139,17 @@ public final class Sqlite implements AutoCloseable { public static final int AUTH_USER = CApi.SQLITE_AUTH_USER; public static final int OK_LOAD_PERMANENTLY = CApi.SQLITE_OK_LOAD_PERMANENTLY; + // sqlite3_open() flags public static final int OPEN_READWRITE = CApi.SQLITE_OPEN_READWRITE; public static final int OPEN_CREATE = CApi.SQLITE_OPEN_CREATE; public static final int OPEN_EXRESCODE = CApi.SQLITE_OPEN_EXRESCODE; + // transaction state public static final int TXN_NONE = CApi.SQLITE_TXN_NONE; public static final int TXN_READ = CApi.SQLITE_TXN_READ; public static final int TXN_WRITE = CApi.SQLITE_TXN_WRITE; + // sqlite3_status() ops public static final int STATUS_MEMORY_USED = CApi.SQLITE_STATUS_MEMORY_USED; public static final int STATUS_PAGECACHE_USED = CApi.SQLITE_STATUS_PAGECACHE_USED; public static final int STATUS_PAGECACHE_OVERFLOW = CApi.SQLITE_STATUS_PAGECACHE_OVERFLOW; @@ -154,6 +158,7 @@ public final class Sqlite implements AutoCloseable { public static final int STATUS_PAGECACHE_SIZE = CApi.SQLITE_STATUS_PAGECACHE_SIZE; public static final int STATUS_MALLOC_COUNT = CApi.SQLITE_STATUS_MALLOC_COUNT; + // sqlite3_db_status() ops public static final int DBSTATUS_LOOKASIDE_USED = CApi.SQLITE_DBSTATUS_LOOKASIDE_USED; public static final int DBSTATUS_CACHE_USED = CApi.SQLITE_DBSTATUS_CACHE_USED; public static final int DBSTATUS_SCHEMA_USED = CApi.SQLITE_DBSTATUS_SCHEMA_USED; @@ -168,6 +173,7 @@ public final class Sqlite implements AutoCloseable { public static final int DBSTATUS_CACHE_USED_SHARED = CApi.SQLITE_DBSTATUS_CACHE_USED_SHARED; public static final int DBSTATUS_CACHE_SPILL = CApi.SQLITE_DBSTATUS_CACHE_SPILL; + // Limits public static final int LIMIT_LENGTH = CApi.SQLITE_LIMIT_LENGTH; public static final int LIMIT_SQL_LENGTH = CApi.SQLITE_LIMIT_SQL_LENGTH; public static final int LIMIT_COLUMN = CApi.SQLITE_LIMIT_COLUMN; @@ -181,15 +187,18 @@ public final class Sqlite implements AutoCloseable { public static final int LIMIT_TRIGGER_DEPTH = CApi.SQLITE_LIMIT_TRIGGER_DEPTH; public static final int LIMIT_WORKER_THREADS = CApi.SQLITE_LIMIT_WORKER_THREADS; + // sqlite3_prepare_v3() flags public static final int PREPARE_PERSISTENT = CApi.SQLITE_PREPARE_PERSISTENT; public static final int PREPARE_NO_VTAB = CApi.SQLITE_PREPARE_NO_VTAB; + // sqlite3_trace_v2() flags public static final int TRACE_STMT = CApi.SQLITE_TRACE_STMT; public static final int TRACE_PROFILE = CApi.SQLITE_TRACE_PROFILE; public static final int TRACE_ROW = CApi.SQLITE_TRACE_ROW; public static final int TRACE_CLOSE = CApi.SQLITE_TRACE_CLOSE; public static final int TRACE_ALL = TRACE_STMT | TRACE_PROFILE | TRACE_ROW | TRACE_CLOSE; + // sqlite3_db_config() ops public static final int DBCONFIG_ENABLE_FKEY = CApi.SQLITE_DBCONFIG_ENABLE_FKEY; public static final int DBCONFIG_ENABLE_TRIGGER = CApi.SQLITE_DBCONFIG_ENABLE_TRIGGER; public static final int DBCONFIG_ENABLE_FTS3_TOKENIZER = CApi.SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER; @@ -209,10 +218,12 @@ public final class Sqlite implements AutoCloseable { public static final int DBCONFIG_STMT_SCANSTATUS = CApi.SQLITE_DBCONFIG_STMT_SCANSTATUS; public static final int DBCONFIG_REVERSE_SCANORDER = CApi.SQLITE_DBCONFIG_REVERSE_SCANORDER; + // sqlite3_config() ops public static final int CONFIG_SINGLETHREAD = CApi.SQLITE_CONFIG_SINGLETHREAD; public static final int CONFIG_MULTITHREAD = CApi.SQLITE_CONFIG_MULTITHREAD; public static final int CONFIG_SERIALIZED = CApi.SQLITE_CONFIG_SERIALIZED; + // Encodings public static final int UTF8 = CApi.SQLITE_UTF8; public static final int UTF16 = CApi.SQLITE_UTF16; public static final int UTF16LE = CApi.SQLITE_UTF16LE; @@ -220,6 +231,14 @@ public final class Sqlite implements AutoCloseable { /* We elide the UTF16_ALIGNED from this interface because it is irrelevant for the Java interface. */ + // SQL data type IDs + public static final int INTEGER = CApi.SQLITE_INTEGER; + public static final int FLOAT = CApi.SQLITE_FLOAT; + public static final int TEXT = CApi.SQLITE_TEXT; + public static final int BLOB = CApi.SQLITE_BLOB; + public static final int NULL = CApi.SQLITE_NULL; + + // Authorizer codes. public static final int DENY = CApi.SQLITE_DENY; public static final int IGNORE = CApi.SQLITE_IGNORE; public static final int CREATE_INDEX = CApi.SQLITE_CREATE_INDEX; |