fpi-device: Get the emulation mode only through compile-time defined libs

In test mode, dynamically load libraries in well known locations that can
allow to override the libfprint behavior.

It would still be possible to potentially inject code by replicating the
distro build directory and adding a library there, but if one is able to
access there, they would already be able to access any path.

Plus the env variable check is still there, so again they would need to
be able to change the fprintd environment
This commit is contained in:
Marco Trevisan (Treviño)
2026-06-19 15:55:13 +02:00
parent 3933e5d468
commit f800bbd485
7 changed files with 59 additions and 11 deletions
+1 -1
View File
@@ -2217,7 +2217,7 @@ dev_change_state (FpImageDevice *dev,
case FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON:
fp_dbg ("Awaiting finger (image-based detection)");
/* Under FP_DEVICE_EMULATION (set by the umockdev test harness so
/* Under fpi_device_emulation (set by the umockdev test harness so
* drivers can adapt to replay; see tests/meson.build) skip the
* timer-driven detect poll: each poll consumes a full ~658KB frame,
* so exercising it in replay would require recording an unbounded
+43 -2
View File
@@ -22,10 +22,12 @@
#include <math.h>
#include <fcntl.h>
#include <errno.h>
#include <gmodule.h>
#include "fpi-log.h"
#include "fp-device-private.h"
#include "tests/fpi-test-emulation.h"
/**
* SECTION: fpi-device
@@ -50,19 +52,58 @@ fp_device_get_instance_private (FpDevice *self)
g_type_class_get_instance_private_offset (dev_class));
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GModule, g_module_close)
/**
* fpi_device_emulation_mode_enabled:
* @device: The #FpDevice to check
*
* Checks if the device is running in emulation mode, which is enabled by
* setting the FP_DEVICE_EMULATION environment variable to a '1' value but
* only when the test emulation library is pre-loaded.
* only when the test emulation library is loaded.
* This is used by some drivers to enable special behavior for testing
* and development purposes.
*/
__attribute__((weak)) gboolean
gboolean
(fpi_device_emulation_mode_enabled) (FpDevice *device)
{
static gboolean (*real_fn)(FpDevice *) = NULL;
static gsize emulation_mode = 0;
if (g_once_init_enter (&emulation_mode))
{
if (g_strcmp0 (g_getenv (FPI_EMULATION_ENV_VAR), "1") == 0)
{
const char *dirs[] = {
FPI_EMULATION_HELPER_BUILDDIR,
FPI_EMULATION_HELPER_INSTALLDIR,
};
for (size_t i = 0; i < G_N_ELEMENTS (dirs); ++i)
{
g_autofree char *path = NULL;
g_autoptr(GModule) mod = NULL;
gpointer sym;
path = g_build_filename (dirs[i], FPI_EMULATION_HELPER_MODULE, NULL);
if (!(mod = g_module_open (path, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL)))
continue;
if (!g_module_symbol (mod, "fpi_device_emulation_mode_enabled", &sym))
continue;
real_fn = (gboolean (*)(FpDevice *)) sym;
g_steal_pointer (&mod);
break;
}
}
g_once_init_leave (&emulation_mode, real_fn ? TRUE : G_MAXSIZE);
}
if (real_fn)
return real_fn (device);
return FALSE;
}
-1
View File
@@ -1,7 +1,6 @@
LIBFPRINT_2.0.0 {
global:
fp_*;
fpi_device_emulation_mode_enabled;
local:
*;
};
+1
View File
@@ -210,6 +210,7 @@ deps = [
enums_dep,
gio_dep,
glib_dep,
gmodule_dep,
gobject_dep,
gusb_dep,
mathlib_dep,
+1
View File
@@ -93,6 +93,7 @@ versioned_libname = meson.project_name() + '-' + soversion.to_string()
glib_dep = dependency('glib-2.0', version: '>=' + glib_min_version)
gio_dep = dependency('gio-unix-2.0', version: '>=' + glib_min_version)
gobject_dep = dependency('gobject-2.0', version: '>=' + glib_min_version)
gmodule_dep = dependency('gmodule-2.0', version: '>=' + glib_min_version)
gusb_dep = dependency('gusb', version: '>= 0.2.0')
mathlib_dep = cc.find_library('m', required: false)
+11 -6
View File
@@ -80,11 +80,18 @@ test_emulation_lib = shared_library('fprint-test-emulation',
install: installed_tests,
install_dir: installed_tests_execdir,
)
test_emulation_lib_name = fs.name(test_emulation_lib.full_path())
# Preload the test-emulation library so that the weak NULL implementation
# in libfprint is overridden with one that honours FP_DEVICE_EMULATION.
envs.prepend('LD_PRELOAD', test_emulation_lib.full_path())
# Paths where the test-emulation helper may be loaded via dlopen
test_emulation = configuration_data()
test_emulation.set_quoted('FPI_EMULATION_ENV_VAR',
'FP_DEVICE_EMULATION')
test_emulation.set_quoted('FPI_EMULATION_HELPER_BUILDDIR',
meson.project_build_root() / 'tests')
test_emulation.set_quoted('FPI_EMULATION_HELPER_INSTALLDIR',
libexecdir / 'installed-tests' / versioned_libname)
test_emulation.set_quoted('FPI_EMULATION_HELPER_MODULE',
fs.name(test_emulation_lib.full_path()))
configure_file(output: 'fpi-test-emulation.h', configuration: test_emulation)
env_parser_cmd = '''
import os;
@@ -98,7 +105,6 @@ envs_str = run_command(python3, '-c', env_parser_cmd,
envs_str = ' '.join([
envs_str,
'LD_PRELOAD=' + (installed_tests_execdir / test_emulation_lib_name),
'G_TEST_SRCDIR=' + installed_tests_testdir,
'G_TEST_BUILDDIR=' + installed_tests_execdir,
])
@@ -225,7 +231,6 @@ if get_option('introspection')
'driver_test': driver_test,
'driver_env': ' '.join([
driver_envs_str,
'LD_PRELOAD=' + (installed_tests_execdir / test_emulation_lib_name),
'LD_LIBRARY_PATH=' + installed_tests_libdir,
# FIXME: Adding this requires gnome-desktop-testing!12
# 'GI_TYPELIB_PATH=' + installed_tests_libdir / 'girepository-1.0',
+2 -1
View File
@@ -17,6 +17,7 @@
*/
#include "fpi-device.h"
#include "fpi-test-emulation.h"
gboolean
(fpi_device_emulation_mode_enabled) (FpDevice *device)
@@ -25,7 +26,7 @@ gboolean
if (g_once_init_enter (&emulation_mode))
g_once_init_leave (&emulation_mode,
g_strcmp0 (g_getenv ("FP_DEVICE_EMULATION"), "1") == 0 ?
g_strcmp0 (g_getenv (FPI_EMULATION_ENV_VAR), "1") == 0 ?
TRUE : G_MAXSIZE);
return emulation_mode == TRUE;