Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for sqlite vtables as loadable extensions #631

Closed
wants to merge 52 commits into from

Conversation

gwenn
Copy link
Collaborator

@gwenn gwenn commented Feb 9, 2020

Try to fix PR #532

@gwenn
Copy link
Collaborator Author

gwenn commented Feb 11, 2020

api_routines_stub feature does not seem to be used anywhere !
Am I missing something ?

@jrandall
Copy link
Contributor

The api_routines_stub feature existed to indicate that the underlying libsqlite3 version was one that included the typedef stub in sqlite.h:

/*
** CAPI3REF: Loadable Extension Thunk
**
** A pointer to the opaque sqlite3_api_routines structure is passed as
** the third parameter to entry points of [loadable extensions].  This
** structure must be typedefed in order to work around compiler warnings
** on some platforms.
*/
typedef struct sqlite3_api_routines sqlite3_api_routines;

It was not referenced from rust code, only serving as a more semantically meaningful alias that required the earliest version of sqlite that we support that includes that stub in the header file (which is libsqlite3-sys/min_sqlite_version_3_20_0). sqlite 3.14.0 is actually the earliest version that has that code, but libsqlite3-sys does not have support for that version, so the api_routines_stub feature was aliased to the earliest version that do have support for, which is 3.20.0.

@gwenn
Copy link
Collaborator Author

gwenn commented Feb 15, 2020

Would it possible to generate only one other bindgen file instead of two ?
Because the difference between the two is only:

--- sqlite3/bindgen_bundled_version-ext.rs	2020-02-15 11:50:57.736323438 +0000
+++ sqlite3/bindgen_bundled_version-ext-embed.rs	2020-02-15 11:51:14.337342051 +0000
@@ -7895,11 +7895,13 @@
     );
 }

-// bindings were built with (non-embedded) loadable_extension:
-// we define our own sqlite_api static variable and export it
-// to C
-#[no_mangle]
-pub static mut sqlite3_api: *mut sqlite3_api_routines = 0 as *mut sqlite3_api_routines;
+// bindings were built with loadable_extension_embedded:
+// define sqlite3_api as an extern since this code will be embedded
+// within a loadable extension that defines and exports this itself
+extern "C" {
+    #[no_mangle]
+    pub static mut sqlite3_api: *mut sqlite3_api_routines;
+}

 // sqlite3 API wrappers to support loadable extensions (Note: these were generated from build.rs - not by rust-bindgen)

@gwenn
Copy link
Collaborator Author

gwenn commented Feb 15, 2020

I don't know why we cannot use loadable_extension with the bundled version ?

            if cfg!(feature = "loadable_extension") {
                panic!("Building a loadable extension bundled is not supported");
            }

Copy link
Member

@thomcc thomcc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had been meaning to take a look at this (and couldn't sleep, so...). I'm a bit concerned by the potential for data races...

Not to mention, failing to initialize this will cause a segfault from safe code (admittedly, dereferencing null is unlikely to be that problematic in practice...).

That said, I have hit this issue in the past, and understand the desire for a fix...

LICENSE Outdated
@@ -1,4 +1,5 @@
Copyright (c) 2014 John Gallagher <johnkgallagher@gmail.com>
Copyright (c) 2019 Genomics plc <info@genomicsplc.com>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should consider just making this Copyright (c) 2014-2020 Rusqlite contributors or something. Since you haven't been requesting contributors assign copyright (which would be a bit of a pain) they retain it by default, so this would be more accurate anyway.

};
use rusqlite::{to_sqlite_error, Connection, Result};

#[allow(clippy::not_unsafe_ptr_arg_deref)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This probably should be unsafe fn though. It both writes to a static mut without synchronization and dereferences the raw pointers passed in as arguments.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


// handle variadic api functions
if bare_fn.variadic.is_some() {
// until rust c_variadic support exists, we can't
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't really safe. Vararg calling convention isn't guaranteed to be the same as the fixed arg calling convention on all platforms / compilers (https://www.gnu.org/software/libc/manual/html_node/Calling-Variadics.html mentions this).

That said I guess it should be fine in practice, but still is worrisome...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// within a loadable extension that defines and exports this itself
extern "C" {
#[no_mangle]
pub static mut sqlite3_api: *mut sqlite3_api_routines;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This worries me. I'd feel better if we synchronized access to it somehow...

At a minimum I guess we should document that this may be read from without synchronization by any call into rusqlite, and so the safety contract for use is that it must be initialized strictly before any thread could possibly read from it.

This goes for both declarations of sqlite3_api.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sqlite3_api symbol is the interface provided by sqlite to its run-time loadable extensions. The wrapper functions we generate (see

if sqlite3_api.is_null() {
) do a test for it not being null.

When we are compiled as an embedded extension, the contract with the extension we are embedded within is that the sqlite3_api symbol is initialised before our entrypoint is called.

When we are a non-embedded extension, it is our responsibility to set sqlite3_api as the first thing we do within our extension entrypoint (e.g.

ffi::sqlite3_api = p_api;
). Even so, we initialise the pointer to null in the declaration and the wrapper function protection should protect us until our entrypoint sets it.

I suppose there is no reason to expose the sqlite3_api symbol directly outside of libsqlite3-sys -- the only appropriate direct interactions with it are from the generated wrapper functions and to set it (once) at the start of the extension entrypoint.

Rather than asking the entrypoint to set the symbol directly, we could expose a pub function to initialise the extension, in which case the symbol could be private to the libsqlite3-sys crate, and I suppose we could also make sure that it is only initialised once.

I.e. something like:

use std::sync::Once;
static SQLITE_EXTENSION_INIT: Once = Once::new();

#[cfg(feature = "loadable_extension_embedded")]
pub fn sqlite_extension_init(_p_api: *mut ffi::sqlite3_api_routines) {}

#[cfg(all(
    feature = "loadable_extension",
    not(feature = "loadable_extension_embedded")
))]
pub fn sqlite_extension_init(p_api: *mut ffi::sqlite3_api_routines) {
    SQLITE_EXTENSION_INIT.call_once(|| {
        unsafe {
            ffi::sqlite3_api = p_api;
        }
    });
}

Would that alleviate some of your concerns?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More or less. Of course, sqlite_extension_init should be an unsafe fn.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussion moved to #910 (comment)

@jrandall
Copy link
Contributor

Extension API function wrappers will be generated for all API functions in the sqlite bindings present at build time. It seems there is a trend towards encouraging building rusqlite against a very recent version of sqlite when any non-ancient features are requested (gating them all with the modern_sqlite feature - see #639 ). If this is the case, vtable authors will have little choice but to disable the version check in order to make it possible for end users to use their extension in a variety of environments. This presents a serious safety risk as an attempt to call an API function froma newer version (i.e. from the wrapper at

((*sqlite3_api).#field_ident
) would result in reading past the end of that struct into memory we don't own, and then executing what we find at the target of a pointer we find there.

If we are going to encourage people to disable the version check by making it difficult to build against a minimum required version of sqlite, then we should probably protect the wrappers by including a runtime version check in them (probably using Once) and refusing to attempt to execute any belonging to a version newer than the runtime environment supports.

@jrandall
Copy link
Contributor

jrandall commented Apr 28, 2020

Version requirements for the API routines are documented in comments in sqlite3ext.h (e.g. https://github.com/rusqlite/rusqlite/blob/master/libsqlite3-sys/sqlite3/sqlite3ext.h#L287) but I am not sure what the best way would be to capture this and pass it through to the wrapper function generator so we can populate a required version test in each of them.

@codecov
Copy link

codecov bot commented Jun 13, 2020

Codecov Report

Merging #631 into master will decrease coverage by 0.27%.
The diff coverage is 18.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #631      +/-   ##
==========================================
- Coverage   76.97%   76.70%   -0.28%     
==========================================
  Files          46       47       +1     
  Lines        5325     5344      +19     
==========================================
  Hits         4099     4099              
- Misses       1226     1245      +19     
Impacted Files Coverage Δ
dummy-extension/src/lib.rs 0.00% <0.00%> (ø)
libsqlite3-sys/build.rs 0.00% <0.00%> (ø)
libsqlite3-sys/src/lib.rs 85.71% <ø> (ø)
src/error.rs 10.85% <0.00%> (-0.82%) ⬇️
src/inner_connection.rs 65.83% <ø> (ø)
src/raw_statement.rs 86.30% <ø> (ø)
src/statement.rs 90.53% <ø> (ø)
src/vtab/mod.rs 61.37% <0.00%> (+2.91%) ⬆️
tests/config_log.rs 100.00% <ø> (ø)
tests/deny_single_threaded_sqlite_config.rs 66.66% <ø> (ø)
... and 4 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 1c9e381...944ad5e. Read the comment docs.

@gwenn
Copy link
Collaborator Author

gwenn commented Oct 4, 2020

Sorry, last commits (on Jun 13) are wrong: at least changes on bindgen files...

@phiresky
Copy link
Contributor

I've updated this PR to work with the current version of rusqlite here: https://github.com/phiresky/rusqlite/tree/gwenntest
Seems to work, just needs recreation of the bindgen files for the non-bundled versions.

@gwenn
Copy link
Collaborator Author

gwenn commented Dec 16, 2020

I've updated this PR to work with the current version of rusqlite here: https://github.com/phiresky/rusqlite/tree/gwenntest
Seems to work, just needs recreation of the bindgen files for the non-bundled versions.

Thanks. Maybe I should close this PR/branch ?

@phiresky
Copy link
Contributor

phiresky commented Dec 16, 2020

Uh probably not, I'm just a random person, I don't have access to this branch and am not enough knowledge to actually champion these changes (e.g. I might have broken Windows builds with this change phiresky@c409018#diff-c1c013e096409a5aea1f123c04254379057791785963eba5ef253fdd686570b8L526 )

@phiresky phiresky mentioned this pull request Dec 18, 2020
@phiresky
Copy link
Contributor

I have opened a new PR here: #868

@jrandall
Copy link
Contributor

jrandall commented Mar 5, 2021

See a note in #910 regarding this PR not adhering to the conditions of the license under which code contributed in #532 was provided: #910 (comment)

The issue here appears to have arisen out of a commit in which the "Genomics plc" copyright notice was removed and not (as far as I can tell) recreated elsewhere: 68bea40

I am sure this was not done maliciously, but these contributions are not owned by me and I only have permission from the owner (my employer) to contribute it under the terms of the license, including the specific copyright notice.

Please stop distributing this code without including the required copyright notice. The easiest way to do that might be to revert (or rebase out of existence) the commit 68bea40

@gwenn gwenn added the invalid label Mar 5, 2021
@gwenn
Copy link
Collaborator Author

gwenn commented Mar 5, 2021

See a note in #910 regarding this PR not adhering to the conditions of the license under which code contributed in #532 was provided: #910 (comment)

The issue here appears to have arisen out of a commit in which the "Genomics plc" copyright notice was removed and not (as far as I can tell) recreated elsewhere: 68bea40

I am sure this was not done maliciously, but these contributions are not owned by me and I only have permission from the owner (my employer) to contribute it under the terms of the license, including the specific copyright notice.

Please stop distributing this code without including the required copyright notice. The easiest way to do that might be to revert (or rebase out of existence) the commit 68bea40

See https://github.com/rusqlite/rusqlite/blob/68bea40e806df992d11ff8d12480738fc34bb611/LICENSE

Copyright (c) 2019 Genomics plc info@genomicsplc.com

So [68bea40] explicitly removed Genomics plc from build.rs but kept it untouched in global LICENCE file (there is no file with copyright header in this repo)...
And #631 (comment)
Genomics plc was removed from global LICENSE file when master branch was merged.

@jrandall
Copy link
Contributor

jrandall commented Mar 6, 2021

See a note in #910 regarding this PR not adhering to the conditions of the license under which code contributed in #532 was provided: #910 (comment)
The issue here appears to have arisen out of a commit in which the "Genomics plc" copyright notice was removed and not (as far as I can tell) recreated elsewhere: 68bea40
I am sure this was not done maliciously, but these contributions are not owned by me and I only have permission from the owner (my employer) to contribute it under the terms of the license, including the specific copyright notice.
Please stop distributing this code without including the required copyright notice. The easiest way to do that might be to revert (or rebase out of existence) the commit 68bea40

See https://github.com/rusqlite/rusqlite/blob/68bea40e806df992d11ff8d12480738fc34bb611/LICENSE

Copyright (c) 2019 Genomics plc info@genomicsplc.com

So [68bea40] explicitly removed Genomics plc from build.rs but kept it untouched in global LICENCE file (there is no file with copyright header in this repo)...
And #631 (comment)
Genomics plc was removed from global LICENSE file when master branch was merged.

@gwenn apologies for missing the move of our notice from build.rs to LICENSE had already happened by the time of 68bea40 and we are happy with that move.

The problem actually occurred as you point out when master was merged: cb9e702#diff-c693279643b8cd5d248172d9c22cb7cf4ed163a3c98c8a3f69c2717edd3eacb7

@jrandall
Copy link
Contributor

jrandall commented Dec 8, 2021

Extension API function wrappers will be generated for all API functions in the sqlite bindings present at build time. It seems there is a trend towards encouraging building rusqlite against a very recent version of sqlite when any non-ancient features are requested (gating them all with the modern_sqlite feature - see #639 ). If this is the case, vtable authors will have little choice but to disable the version check in order to make it possible for end users to use their extension in a variety of environments. This presents a serious safety risk as an attempt to call an API function froma newer version (i.e. from the wrapper at

((*sqlite3_api).#field_ident

) would result in reading past the end of that struct into memory we don't own, and then executing what we find at the target of a pointer we find there.
If we are going to encourage people to disable the version check by making it difficult to build against a minimum required version of sqlite, then we should probably protect the wrappers by including a runtime version check in them (probably using Once) and refusing to attempt to execute any belonging to a version newer than the runtime environment supports.

Discussion moved to: https://github.com/rusqlite/rusqlite/pull/910/files#r765327393

@gwenn gwenn closed this Jul 18, 2023
@gwenn gwenn deleted the pr/loadable-extensions branch July 18, 2023 16:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants