JNI: after calling a Java-side UDF, zero-out the pointer of the Java-side sqlite3_context and sqlite3_value array entries to avoid misbehavior if a client makes the mistake of holding a reference to one of those objects.

FossilOrigin-Name: 9fc3104f76a83d600beb11d91feb97bcea8bc7f7cda8cd73e7a6b81fbba879df
This commit is contained in:
stephan 2023-10-16 08:10:11 +00:00
parent f83a4d850c
commit 19179722d6
4 changed files with 51 additions and 9 deletions

View File

@ -1835,7 +1835,7 @@ typedef struct {
** final 2 arguments. Returns 0 on success, SQLITE_NOMEM on allocation
** error. On error *jCx and *jArgv will be set to 0. The output
** objects are of type org.sqlite.jni.sqlite3_context and
** array-of-org.sqlite3.jni.sqlite3_value, respectively.
** array-of-org.sqlite.jni.sqlite3_value, respectively.
*/
static int udf_args(JNIEnv *env,
sqlite3_context * const cx,
@ -1867,6 +1867,28 @@ error_oom:
return SQLITE_NOMEM;
}
/*
** Requires that jCx and jArgv are sqlite3_context
** resp. array-of-sqlite3_value values initialized by udf_args(). 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.
*/
static void udf_unargs(JNIEnv *env, jobject jCx, int argc, jobjectArray jArgv){
int i = 0;
assert(jCx);
NativePointerHolder_set(S3JniNph(sqlite3_context), jCx, 0);
for( ; i < argc; ++i ){
jobject jsv = (*env)->GetObjectArrayElement(env, jArgv, i);
assert(jsv);
NativePointerHolder_set(S3JniNph(sqlite3_value), jsv, 0);
}
}
/*
** Must be called immediately after a Java-side UDF callback throws.
** If translateToErr is true then it sets the exception's message in
@ -1926,6 +1948,7 @@ static int udf_xFSI(sqlite3_context* const pCx, int argc,
rc = udf_report_exception(env, 'F'==zFuncType[1]/*xFunc*/, pCx,
s->zFuncName, zFuncType);
}
udf_unargs(env, args.jcx, argc, args.jargv);
}
S3JniUnrefLocal(args.jcx);
S3JniUnrefLocal(args.jargv);
@ -5168,6 +5191,7 @@ static void s3jni_fts5_extension_function(Fts5ExtensionApi const *pApi,
S3JniIfThrew{
udf_report_exception(env, 1, pCx, pAux->zFuncName, "call");
}
udf_unargs(env, jpCx, argc, jArgv);
S3JniUnrefLocal(jpFts);
S3JniUnrefLocal(jpCx);
S3JniUnrefLocal(jArgv);

View File

@ -694,6 +694,8 @@ public class Tester1 implements Runnable {
// These ValueHolders are just to confirm that the func did what we want...
final ValueHolder<Boolean> xDestroyCalled = new ValueHolder<>(false);
final ValueHolder<Integer> xFuncAccum = new ValueHolder<>(0);
final ValueHolder<sqlite3_value[]> neverEverDoThisInClientCode = new ValueHolder<>(null);
final ValueHolder<sqlite3_context> neverEverDoThisInClientCode2 = new ValueHolder<>(null);
// Create an SQLFunction instance using one of its 3 subclasses:
// Scalar, Aggregate, or Window:
@ -704,6 +706,15 @@ public class Tester1 implements Runnable {
new ScalarFunction(){
public void xFunc(sqlite3_context cx, sqlite3_value[] args){
affirm(db == sqlite3_context_db_handle(cx));
if( null==neverEverDoThisInClientCode.value ){
neverEverDoThisInClientCode2.value = cx;
neverEverDoThisInClientCode.value = args
/* !!!NEVER!!! hold a reference to an sqlite3_value
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. */;
}
int result = 0;
for( sqlite3_value v : args ) result += sqlite3_value_int(v);
xFuncAccum.value += result;// just for post-run testing
@ -729,6 +740,13 @@ public class Tester1 implements Runnable {
affirm(1 == n);
affirm(6 == xFuncAccum.value);
affirm( !xDestroyCalled.value );
affirm( null!=neverEverDoThisInClientCode.value );
affirm( null!=neverEverDoThisInClientCode2.value );
affirm( 0<neverEverDoThisInClientCode.value.length );
affirm( 0==neverEverDoThisInClientCode2.value.getNativePointer() );
for( sqlite3_value sv : neverEverDoThisInClientCode.value ){
affirm( 0==sv.getNativePointer() );
}
sqlite3_close_v2(db);
affirm( xDestroyCalled.value );
}

View File

@ -1,5 +1,5 @@
C JNI:\sdo\snot\sexpose\sSQLITE_OPEN_...\sflags\swhich\sare\sspecific\sto\sVFSes.
D 2023-10-16T08:05:51.755
C JNI:\safter\scalling\sa\sJava-side\sUDF,\szero-out\sthe\spointer\sof\sthe\sJava-side\ssqlite3_context\sand\ssqlite3_value\sarray\sentries\sto\savoid\smisbehavior\sif\sa\sclient\smakes\sthe\smistake\sof\sholding\sa\sreference\sto\sone\sof\sthose\sobjects.
D 2023-10-16T08:10:11.732
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -238,7 +238,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3
F ext/jni/GNUmakefile efaf1db6e3c2bbae4067924b932ee1a0f0640e842002c0dd9f3be824c24084f5
F ext/jni/README.md ef9ac115e97704ea995d743b4a8334e23c659e5534c3b64065a5405256d5f2f4
F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa
F ext/jni/src/c/sqlite3-jni.c ff7720536758ab952ed025a0a32a0e9d467d0b9a6bc7801dbdef56d56e074ea2
F ext/jni/src/c/sqlite3-jni.c fa78ee087d493dba500c55797a77d57591d36be45e983b66222b9de6dd498ab9
F ext/jni/src/c/sqlite3-jni.h e7c19450b691aeb12a1506521432144b5d07031683e0fbaad7dfcb9a0c8d69d7
F ext/jni/src/org/sqlite/jni/AbstractCollationCallback.java 95e88ba04f4aac51ffec65693e878e234088b2f21b387f4e4285c8b72b33e436
F ext/jni/src/org/sqlite/jni/AggregateFunction.java 7312486bc65fecdb91753c0a4515799194e031f45edbe16a6373cea18f404dc4
@ -265,7 +265,7 @@ F ext/jni/src/org/sqlite/jni/ScalarFunction.java 6d387bb499fbe3bc13c53315335233d
F ext/jni/src/org/sqlite/jni/Sqlite.java 1617ea2bf3dfa493b7f031a3187cbfd6837c39bc1d406c4b3edcf9aab941639d
F ext/jni/src/org/sqlite/jni/SqliteException.java e17500e8bca2c68c260d8d0163fe4b7dc8bd0b1b90211201325c4a5566ce75ca
F ext/jni/src/org/sqlite/jni/TableColumnMetadata.java 54511b4297fa28dcb3f49b24035e34ced10e3fd44fd0e458e784f4d6b0096dab
F ext/jni/src/org/sqlite/jni/Tester1.java 034863c36b0cd98af8edadb252358213e7ed3140f2cf81e03ef0e228c45d6a3c
F ext/jni/src/org/sqlite/jni/Tester1.java 865cc3a23f9aeec5c70b362633037d166697f97f88eccd4024085814c4c95dbd
F ext/jni/src/org/sqlite/jni/Tester2.java 70e005d41060e398ec0f69bd39a8e1c376fd51f81629cf25e877889ec9cb6ec6
F ext/jni/src/org/sqlite/jni/TesterFts5.java d60fe9944a81156b3b5325dd1b0e8e92a1547468f39fd1266d06f7bb6a95fa70
F ext/jni/src/org/sqlite/jni/TraceV2Callback.java f157edd9c72e7d2243c169061487cd7bb51a0d50f3ac976dbcbbacf748ab1fc2
@ -2128,8 +2128,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P dd766eeb59fec71627dd8ad8f120875b96fda455c6401e5671e086b785e2b2bc
R a3abea6aaad96691a9fd3e93b233f3d2
P 2b4e53d8be42a3bc098317abd8bb58b8ddc25094d80787f784bbc896f4f7b976
R e7086cafa37f4c64d76125756c139d05
U stephan
Z 11a2a6974b07b2c488e6e36a5cdd602e
Z 15d3510e65023cee802402770fb1df4a
# Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
2b4e53d8be42a3bc098317abd8bb58b8ddc25094d80787f784bbc896f4f7b976
9fc3104f76a83d600beb11d91feb97bcea8bc7f7cda8cd73e7a6b81fbba879df