From: stephan Date: Fri, 13 Oct 2023 12:48:35 +0000 (+0000) Subject: Round one of an audit for SQLITE_ENABLE_API_ARMOR for functions exposed by JNI and... X-Git-Tag: version-3.44.0~112^2~12 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=7dc0cc45f30a310dba12dcd3aaa554a146a5bf8e;p=thirdparty%2Fsqlite.git Round one of an audit for SQLITE_ENABLE_API_ARMOR for functions exposed by JNI and those functions missing armor, as [forum:5e3fc453a69b49ca|reported in several forum posts]. FossilOrigin-Name: 8c25c4b18ad07861bf0e47f99f3db04b569b9b859ad0690602f748ddf3576939 --- diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 7afc9a0f0f..1465c9e651 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -972,6 +972,7 @@ static jstring s3jni__utf8_to_jstring(JNIEnv * const env, hypothetically do this for any strings where n<4 and z is NUL-terminated and none of z[0..3] are NUL bytes. */ rv = (*env)->NewStringUTF(env, ""); + s3jni_oom_check( rv ); }else if( z ){ jbyteArray jba; if( n<0 ) n = sqlite3Strlen30(z); @@ -985,8 +986,8 @@ static jstring s3jni__utf8_to_jstring(JNIEnv * const env, } S3JniUnrefLocal(jba); } + s3jni_oom_check( rv ); } - s3jni_oom_check( rv ); return rv; } #define s3jni_utf8_to_jstring(CStr,n) s3jni__utf8_to_jstring(env, CStr, n) @@ -1468,12 +1469,14 @@ static void * NativePointerHolder__get(JNIEnv * env, jobject jNph, #define PtrGet_sqlite3_stmt(OBJ) PtrGet_T(sqlite3_stmt, OBJ) #define PtrGet_sqlite3_value(OBJ) PtrGet_T(sqlite3_value, OBJ) /* -** S3JniLongPtr_T(X,Y) expects X to be an unqualified sqlite3 -** struct type name and Y to be a native pointer to such an object in -** the form of a jlong value. The jlong is simply cast to (X*). This +** S3JniLongPtr_T(X,Y) expects X to be an unqualified sqlite3 struct +** type name and Y to be a native pointer to such an object in the +** form of a jlong value. The jlong is simply cast to (X*). This ** approach is, as of 2023-09-27, supplanting the former approach. We ** now do the native pointer extraction in the Java side, rather than -** the C side, because it's reportedly significantly faster. +** the C side, because it's reportedly significantly faster. The +** intptr_t part here is necessary for compatibility with (at least) +** ARM32. */ #define S3JniLongPtr_T(T,JLongAsPtr) (T*)((intptr_t)(JLongAsPtr)) #define S3JniLongPtr_sqlite3(JLongAsPtr) S3JniLongPtr_T(sqlite3,JLongAsPtr) @@ -2616,6 +2619,10 @@ S3JniApi(sqlite3_cancel_auto_extension(),jboolean,1cancel_1auto_1extension)( S3JniAutoExtension * ax; jboolean rc = JNI_FALSE; int i; + + if( !jAutoExt ){ + return rc; + } S3JniAutoExt_mutex_enter; /* This algo corresponds to the one in the core. */ for( i = SJG.autoExt.nExt-1; i >= 0; --i ){ @@ -2778,8 +2785,8 @@ S3JniApi(sqlite3_column_text(),jbyteArray,1column_1text)( JniArgsEnvClass, jobject jpStmt, jint ndx ){ sqlite3_stmt * const stmt = PtrGet_sqlite3_stmt(jpStmt); - const unsigned char * const p = sqlite3_column_text(stmt, (int)ndx); - const int n = sqlite3_column_bytes(stmt, (int)ndx); + const unsigned char * const p = stmt ? sqlite3_column_text(stmt, (int)ndx) : 0; + const int n = p ? sqlite3_column_bytes(stmt, (int)ndx) : 0; return p ? s3jni_new_jbyteArray(p, n) : NULL; } @@ -2789,8 +2796,8 @@ S3JniApi(sqlite3_column_text(),jstring,1column_1text)( JniArgsEnvClass, jobject jpStmt, jint ndx ){ sqlite3_stmt * const stmt = PtrGet_sqlite3_stmt(jpStmt); - const unsigned char * const p = sqlite3_column_text(stmt, (int)ndx); - const int n = sqlite3_column_bytes(stmt, (int)ndx); + const unsigned char * const p = stmt ? sqlite3_column_text(stmt, (int)ndx) : 0; + const int n = p ? sqlite3_column_bytes(stmt, (int)ndx) : 0; return p ? s3jni_utf8_to_jstring( (const char *)p, n) : 0; } #endif @@ -2799,8 +2806,8 @@ S3JniApi(sqlite3_column_text16(),jstring,1column_1text16)( JniArgsEnvClass, jobject jpStmt, jint ndx ){ sqlite3_stmt * const stmt = PtrGet_sqlite3_stmt(jpStmt); - const void * const p = sqlite3_column_text16(stmt, (int)ndx); - const int n = sqlite3_column_bytes16(stmt, (int)ndx); + const void * const p = stmt ? sqlite3_column_text16(stmt, (int)ndx) : 0; + const int n = p ? sqlite3_column_bytes16(stmt, (int)ndx) : 0; return s3jni_text16_to_jstring(env, p, n); } @@ -2808,7 +2815,8 @@ S3JniApi(sqlite3_column_value(),jobject,1column_1value)( JniArgsEnvClass, jobject jpStmt, jint ndx ){ sqlite3_value * const sv = - sqlite3_column_value(PtrGet_sqlite3_stmt(jpStmt), (int)ndx); + sqlite3_column_value(PtrGet_sqlite3_stmt(jpStmt), (int)ndx) + /* reminder: returns an SQL NULL if jpStmt==NULL */; return new_java_sqlite3_value(env, sv); } @@ -2919,6 +2927,18 @@ S3JniApi(sqlite3_compileoption_get(),jstring,1compileoption_1get)( return rv; } +S3JniApi(sqlite3_compileoption_used(),jboolean,1compileoption_1used)( + JniArgsEnvClass, jstring name +){ + const char *zUtf8 = s3jni_jstring_to_mutf8(name) + /* We know these to be ASCII, so MUTF-8 is fine (and + hypothetically faster to convert). */; + const jboolean rc = + 0==sqlite3_compileoption_used(zUtf8) ? JNI_FALSE : JNI_TRUE; + s3jni_mutf8_release(name, zUtf8); + return rc; +} + S3JniApi(sqlite3_complete(),int,1complete)( JniArgsEnvClass, jbyteArray jSql ){ @@ -2935,18 +2955,6 @@ S3JniApi(sqlite3_complete(),int,1complete)( return rc; } -S3JniApi(sqlite3_compileoption_used(),jboolean,1compileoption_1used)( - JniArgsEnvClass, jstring name -){ - const char *zUtf8 = s3jni_jstring_to_mutf8(name) - /* We know these to be ASCII, so MUTF-8 is fine (and - hypothetically faster to convert). */; - const jboolean rc = - 0==sqlite3_compileoption_used(zUtf8) ? JNI_FALSE : JNI_TRUE; - s3jni_mutf8_release(name, zUtf8); - return rc; -} - S3JniApi(sqlite3_config() /*for a small subset of options.*/, jint,1config__I)(JniArgsEnvClass, jint n){ switch( n ){ @@ -3151,6 +3159,9 @@ S3JniApi(sqlite3_create_collation() sqlite3_create_collation_v2(), int rc; S3JniDb * ps; + if( !jDb || !name || !encodingTypeIsValid(eTextRep) ){ + return (jint)SQLITE_MISUSE; + } S3JniDb_mutex_enter; ps = S3JniDb_from_java(jDb); jclass const klazz = (*env)->GetObjectClass(env, oCollation); @@ -3574,6 +3585,7 @@ static int s3jni_open_pre(JNIEnv * const env, S3JniEnv **jc, S3JniDb ** ps){ int rc = 0; jobject jDb = 0; + *jc = S3JniEnv_get(); if( !*jc ){ rc = SQLITE_NOMEM; @@ -3650,6 +3662,8 @@ S3JniApi(sqlite3_open(),jint,1open)( S3JniDb * ps = 0; S3JniEnv * jc = 0; int rc; + + if( 0==jOut ) return SQLITE_MISUSE; rc = s3jni_open_pre(env, &jc, strName, &zName, &ps); if( 0==rc ){ rc = s3jni_open_post(env, jc, ps, &pOut, jOut, @@ -3662,14 +3676,17 @@ S3JniApi(sqlite3_open(),jint,1open)( S3JniApi(sqlite3_open_v2(),jint,1open_1v2)( JniArgsEnvClass, jstring strName, - jobject jOut, jint flags, jstring strVfs + jobject jOut, jint flags, jstring strVfs ){ sqlite3 * pOut = 0; char *zName = 0; S3JniDb * ps = 0; S3JniEnv * jc = 0; char *zVfs = 0; - int rc = s3jni_open_pre(env, &jc, strName, &zName, &ps); + int rc; + + if( 0==jOut ) return SQLITE_MISUSE; + rc = s3jni_open_pre(env, &jc, strName, &zName, &ps); if( 0==rc ){ if( strVfs ){ zVfs = s3jni_jstring_to_utf8( strVfs, 0); @@ -4469,7 +4486,6 @@ static int s3jni_strlike_glob(int isLike, JNIEnv *const env, jbyte * const pG = s3jni_jbyteArray_bytes(baG); jbyte * const pT = pG ? s3jni_jbyteArray_bytes(baT) : 0; - s3jni_oom_fatal(pT); /* Note that we're relying on the byte arrays having been NUL-terminated on the Java side. */ rc = isLike @@ -4509,12 +4525,8 @@ S3JniApi(sqlite3_sql(),jstring,1sql)( S3JniApi(sqlite3_step(),jint,1step)( JniArgsEnvClass,jobject jStmt ){ - int rc = SQLITE_MISUSE; sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jStmt); - if( pStmt ){ - rc = sqlite3_step(pStmt); - } - return rc; + return pStmt ? (jint)sqlite3_step(pStmt) : (jint)SQLITE_MISUSE; } S3JniApi(sqlite3_table_column_metadata(),int,1table_1column_1metadata)( diff --git a/manifest b/manifest index 667e06fa71..f9baa60087 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Avoid\spotential\soverflow\sin\shex().\s[forum:/forumpost/7ac0c9c5ea|See\sforum\spost\s7ac0c9c5ea.] -D 2023-10-13T01:39:47.068 +C Round\sone\sof\san\saudit\sfor\sSQLITE_ENABLE_API_ARMOR\sfor\sfunctions\sexposed\sby\sJNI\sand\sthose\sfunctions\smissing\sarmor,\sas\s[forum:5e3fc453a69b49ca|reported\sin\sseveral\sforum\sposts]. +D 2023-10-13T12:48:35.298 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 6da240c9a11701f3ed569384cd15ef611e8b3c5e3897d265923b14bf0e1eb272 F ext/jni/README.md ef9ac115e97704ea995d743b4a8334e23c659e5534c3b64065a5405256d5f2f4 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c bf432771fbc84da2b5f4037e0bcf20757548aac2fa782b272c4894a9c279964a +F ext/jni/src/c/sqlite3-jni.c eabd48ced29954fb4f6173c4f350f7f1fce218ce19f48a75367cb560cf2da77e F ext/jni/src/c/sqlite3-jni.h be1fdff7ab3a2bb357197271c8ac5d2bf6ff59380c106dde3a13be88724bad22 F ext/jni/src/org/sqlite/jni/AbstractCollationCallback.java 95e88ba04f4aac51ffec65693e878e234088b2f21b387f4e4285c8b72b33e436 F ext/jni/src/org/sqlite/jni/AggregateFunction.java 7312486bc65fecdb91753c0a4515799194e031f45edbe16a6373cea18f404dc4 @@ -676,8 +676,8 @@ F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 F src/json.c 799430345d16fed21713416318e6ca616f1264224f708555f387bfa2ab2496c7 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa -F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 -F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 +F src/loadext.c 7d56c6f28aaf9b42e2772289e0d0a12a77b57c2baed021dbfd1fcafec69c156a +F src/main.c 7a91b372dd1bada07c5b5e36082dc0d695161c3807efe2386f563a5b32446ae6 F src/malloc.c f016922435dc7d1f1f5083a03338a3e91f8c67ce2c5bdcfa4cdef62e612f5fcc F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c 3bb59158c38e05f6270e761a9f435bf19827a264c13d1631c58b84bdc96d73b2 @@ -692,7 +692,7 @@ F src/mutex.h a7b2293c48db5f27007c3bdb21d438873637d12658f5a0bf8ad025bb96803c4a F src/mutex_noop.c 9d4309c075ba9cc7249e19412d3d62f7f94839c4 F src/mutex_unix.c bd52ec50e44a41fe1e3deb5a6e3fe98edb6f2059da3e46d196363d0fa3192cda F src/mutex_w32.c 38b56d0bc8d54c17c20cbaaad3719b0c36b92fd07a7e34360d0c6a18d5589912 -F src/notify.c 89a97dc854c3aa62ad5f384ef50c5a4a11d70fcc69f86de3e991573421130ed6 +F src/notify.c 57c2d1a2805d6dee32acd5d250d928ab94e02d76369ae057dee7d445fd64e878 F src/os.c 509452169d5ea739723e213b8e2481cf0e587f0e88579a912d200db5269f5f6d F src/os.h 1ff5ae51d339d0e30d8a9d814f4b8f8e448169304d83a7ed9db66a65732f3e63 F src/os_common.h 6c0eb8dd40ef3e12fe585a13e709710267a258e2c8dd1c40b1948a1d14582e06 @@ -787,7 +787,7 @@ F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104 F src/vdbe.c cd112eb00d20fc5cc44f631d0e713838602637328b0f127c2f3c2aa8cea3cc91 F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0 F src/vdbeInt.h 949669dfd8a41550d27dcb905b494f2ccde9a2e6c1b0b04daa1227e2e74c2b2c -F src/vdbeapi.c 4184402246172220418c0ef49ff4cf1a19ced9a4ac6c843c2f0773fb5c543f37 +F src/vdbeapi.c be7d88a05df51cb3940304f74e4b0534817b51213b3406143a67eecd6f82fe21 F src/vdbeaux.c 5b415e09b5b9d5be6c0f4fcbf18ea9d7d16f6a29ced2f14a3b2041020f63e9c1 F src/vdbeblob.c 4cf5aa130e96e3b52ba3fb54b7f9606c942ab988dbb32cb19cff4db24e06aeec F src/vdbemem.c 317b9f48708139db6239ade40c7980b4bc8233168383690d588dad6d8437f722 @@ -2128,8 +2128,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 4a4eccb681741917c6771101d4e329f91eae9b035f4d345218e522bb55fff6da -R b8db52c4af481f11a25564ae1b4169f0 -U larrybr -Z e40ad0deba084d2d357f307fa10fdd72 +P 2e968114fd0e60eebcc088dec02684e10b06ac1ec42d6bedb5287362f5cbf032 +R 64d7e862f198f312cf375ff0e1b4824a +T *branch * api-armor-audit +T *sym-api-armor-audit * +T -sym-trunk * Cancelled\sby\sbranch. +U stephan +Z fb0f8b82a9c86be7bcd5823447ca13df # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 1d7dc6eb7c..7b111488b3 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -2e968114fd0e60eebcc088dec02684e10b06ac1ec42d6bedb5287362f5cbf032 \ No newline at end of file +8c25c4b18ad07861bf0e47f99f3db04b569b9b859ad0690602f748ddf3576939 \ No newline at end of file diff --git a/src/loadext.c b/src/loadext.c index e792fa5a98..cfa8ba4ca8 100644 --- a/src/loadext.c +++ b/src/loadext.c @@ -730,6 +730,9 @@ void sqlite3CloseExtensions(sqlite3 *db){ ** default so as not to open security holes in older applications. */ int sqlite3_enable_load_extension(sqlite3 *db, int onoff){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; +#endif sqlite3_mutex_enter(db->mutex); if( onoff ){ db->flags |= SQLITE_LoadExtension|SQLITE_LoadExtFunc; @@ -751,7 +754,7 @@ int sqlite3_enable_load_extension(sqlite3 *db, int onoff){ */ typedef struct sqlite3AutoExtList sqlite3AutoExtList; static SQLITE_WSD struct sqlite3AutoExtList { - u32 nExt; /* Number of entries in aExt[] */ + u32 nExt; /* Number of entries in aExt[] */ void (**aExt)(void); /* Pointers to the extension init functions */ } sqlite3Autoext = { 0, 0 }; @@ -779,6 +782,9 @@ int sqlite3_auto_extension( void (*xInit)(void) ){ int rc = SQLITE_OK; +#ifdef SQLITE_ENABLE_API_ARMOR + if( xInit==0 ) return SQLITE_MISUSE_BKPT; +#endif #ifndef SQLITE_OMIT_AUTOINIT rc = sqlite3_initialize(); if( rc ){ @@ -831,6 +837,9 @@ int sqlite3_cancel_auto_extension( int i; int n = 0; wsdAutoextInit; +#ifdef SQLITE_ENABLE_API_ARMOR + if( xInit==0 ) return 0; +#endif sqlite3_mutex_enter(mutex); for(i=(int)wsdAutoext.nExt-1; i>=0; i--){ if( wsdAutoext.aExt[i]==xInit ){ diff --git a/src/main.c b/src/main.c index a43afab168..4ddf7e24d3 100644 --- a/src/main.c +++ b/src/main.c @@ -954,6 +954,10 @@ int sqlite3_db_cacheflush(sqlite3 *db){ int sqlite3_db_config(sqlite3 *db, int op, ...){ va_list ap; int rc; + +#ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; +#endif sqlite3_mutex_enter(db->mutex); va_start(ap, op); switch( op ){ @@ -2365,6 +2369,12 @@ void *sqlite3_preupdate_hook( void *pArg /* First callback argument */ ){ void *pRet; + +#ifdef SQLITE_ENABLE_API_ARMOR + if( db==0 || xCallback==0 ){ + return; + } +#endif sqlite3_mutex_enter(db->mutex); pRet = db->pPreUpdateArg; db->xPreUpdateCallback = xCallback; diff --git a/src/notify.c b/src/notify.c index 4960ab76b1..6a4cab8755 100644 --- a/src/notify.c +++ b/src/notify.c @@ -152,6 +152,9 @@ int sqlite3_unlock_notify( ){ int rc = SQLITE_OK; +#ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; +#endif sqlite3_mutex_enter(db->mutex); enterMutex(); diff --git a/src/vdbeapi.c b/src/vdbeapi.c index 1213dbe6d1..f8e66df87a 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -2029,10 +2029,16 @@ static UnpackedRecord *vdbeUnpackRecord( ** a field of the row currently being updated or deleted. */ int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){ - PreUpdate *p = db->pPreUpdate; + PreUpdate *p; Mem *pMem; int rc = SQLITE_OK; +#ifdef SQLITE_ENABLE_API_ARMOR + if( db==0 || ppValue==0 ){ + return SQLITE_MISUSE_BKPT; + } +#endif + p = db->pPreUpdate; /* Test that this call is being made from within an SQLITE_DELETE or ** SQLITE_UPDATE pre-update callback, and that iIdx is within range. */ if( !p || p->op==SQLITE_INSERT ){ @@ -2093,7 +2099,12 @@ int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){ ** the number of columns in the row being updated, deleted or inserted. */ int sqlite3_preupdate_count(sqlite3 *db){ - PreUpdate *p = db->pPreUpdate; + PreUpdate *p; +#ifdef SQLITE_ENABLE_API_ARMOR + p = db!=0 ? db->pPreUpdate : 0; +#else + p = db->pPreUpdate; +#endif return (p ? p->keyinfo.nKeyField : 0); } #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ @@ -2111,7 +2122,12 @@ int sqlite3_preupdate_count(sqlite3 *db){ ** or SET DEFAULT action is considered a trigger. */ int sqlite3_preupdate_depth(sqlite3 *db){ - PreUpdate *p = db->pPreUpdate; + PreUpdate *p; +#ifdef SQLITE_ENABLE_API_ARMOR + p = db!=0 ? db->pPreUpdate : 0; +#else + p = db->pPreUpdate; +#endif return (p ? p->v->nFrame : 0); } #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ @@ -2122,7 +2138,12 @@ int sqlite3_preupdate_depth(sqlite3 *db){ ** only. */ int sqlite3_preupdate_blobwrite(sqlite3 *db){ - PreUpdate *p = db->pPreUpdate; + PreUpdate *p; +#ifdef SQLITE_ENABLE_API_ARMOR + p = db!=0 ? db->pPreUpdate : 0; +#else + p = db->pPreUpdate; +#endif return (p ? p->iBlobWrite : -1); } #endif @@ -2133,10 +2154,16 @@ int sqlite3_preupdate_blobwrite(sqlite3 *db){ ** a field of the row currently being updated or inserted. */ int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppValue){ - PreUpdate *p = db->pPreUpdate; + PreUpdate *p; int rc = SQLITE_OK; Mem *pMem; +#ifdef SQLITE_ENABLE_API_ARMOR + if( db==0 || ppValue==0 ){ + return SQLITE_MISUSE_BKPT; + } +#endif + p = db->pPreUpdate; if( !p || p->op==SQLITE_DELETE ){ rc = SQLITE_MISUSE_BKPT; goto preupdate_new_out;