aboutsummaryrefslogtreecommitdiff
path: root/ext/jni/src
diff options
context:
space:
mode:
Diffstat (limited to 'ext/jni/src')
-rw-r--r--ext/jni/src/c/sqlite3-jni.c37
-rw-r--r--ext/jni/src/org/sqlite/jni/capi/Tester1.java17
-rw-r--r--ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java19
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;