Subject: Re: bug#63590: 29.0.90; can't load sqlite extension

From: Eli Zaretskii <[email-masked]>
Date: Sat, 20 May 2023 12:53:12 +0300

> Cc: [email-masked]
> Date: Fri, 19 May 2023 19:36:21 +0300
> From: Eli Zaretskii <[email-masked]>
> 
> Are you saying that the SQL-related tests in the Emacs test suite
> don't work for you?  See test/src/sqlite-tests.el.

Answering myself: the relevant test considers it a "success" if
sqlite-load-extension returns nil, even if the extension does exist on
the system.  So it doesn't really test whether the extension was
successfully loaded.  I will tweak the test to be more useful in this
regard.

Anyway, does the patch to sqlite.c below fix your problem?  I decided
to enable loading extensions only temporarily, while we call
sqlite3_load_extension, so that no extension could be accidentally
loaded out of our control, not even if some Emacs module uses the
sqlite3 library on its own using the C APIs, thus bypassing
sqlite-load-extension.

diff --git a/src/sqlite.c b/src/sqlite.c
index 0361514..2b0bc02 100644
--- a/src/sqlite.c
+++ b/src/sqlite.c
@@ -23,6 +23,8 @@ Copyright (C) 2021-2023 Free Software Foundation, Inc.
    https://github.com/syohex/emacs-sqlite3  */
 
 #include <config.h>
+
+#include <c-strcase.h>
 #include "lisp.h"
 #include "coding.h"
 
@@ -686,7 +692,8 @@ DEFUN ("sqlite-load-extension", Fsqlite_load_extension,
   /* Add names of useful and free modules here.  */
   const char *allowlist[3] = { "pcre", "csvtable", NULL };
   char *name = SSDATA (Ffile_name_nondirectory (module));
-  /* Possibly skip past a common prefix.  */
+  /* Possibly skip past a common prefix (libsqlite3_mod_ is used by
+     Debian, see https://packages.debian.org/source/sid/sqliteodbc).  */
   const char *prefix = "libsqlite3_mod_";
   if (!strncmp (name, prefix, strlen (prefix)))
     name += strlen (prefix);
@@ -697,7 +704,7 @@ DEFUN ("sqlite-load-extension", Fsqlite_load_extension,
       if (strlen (*allow) < strlen (name)
 	  && !strncmp (*allow, name, strlen (*allow))
 	  && (!strcmp (name + strlen (*allow), ".so")
-	      || !strcmp (name + strlen (*allow), ".DLL")))
+	      || !strcasecmp (name + strlen (*allow), ".dll")))
 	{
 	  do_allow = true;
 	  break;
@@ -707,12 +714,25 @@ DEFUN ("sqlite-load-extension", Fsqlite_load_extension,
   if (!do_allow)
     xsignal1 (Qsqlite_error, build_string ("Module name not on allowlist"));
 
-  int result = sqlite3_load_extension
-		       (XSQLITE (db)->db,
-			SSDATA (ENCODE_FILE (Fexpand_file_name (module, Qnil))),
-			NULL, NULL);
-  if (result ==  SQLITE_OK)
-    return Qt;
+  /* Expand all Lisp data explicitly, so as to avoid signaling an
+     error while extension loading is enabled -- we don't want to
+     "leak" this outside this function.  */
+  sqlite3 *sdb = XSQLITE (db)->db;
+  char *ext_fn = SSDATA (ENCODE_FILE (Fexpand_file_name (module, Qnil)));
+  /* Temporarily enable loading extensions via the C API.  */
+  int result = sqlite3_db_config (sdb, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 1,
+				  NULL);
+  if (result == SQLITE_OK)
+    {
+      result = sqlite3_load_extension (sdb, ext_fn, NULL, NULL);
+      if (result ==  SQLITE_OK)
+	{
+	  /* Disable loading extensions via C API.  */
+	  sqlite3_db_config (sdb, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 0,
+			     NULL);
+	  return Qt;
+	}
+    }
   return Qnil;
 }
 #endif /* HAVE_SQLITE3_LOAD_EXTENSION */