]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libmount: use shared dl-utils for cryptsetup dlopen
authorKarel Zak <kzak@redhat.com>
Mon, 13 Jul 2026 13:42:14 +0000 (15:42 +0200)
committerKarel Zak <kzak@redhat.com>
Tue, 14 Jul 2026 12:39:33 +0000 (14:39 +0200)
Move the cryptsetup dlopen wrapper out of hook_veritydev.c into
lib/dl-cryptsetup.c with a global cached load function
ul_dlopen_libcryptsetup().  The function pointers are resolved once
and shared across all callers.

This removes the CRYPTSETUP_VIA_DLOPEN conditional -- dlopen is now the
only supported way to use libcryptsetup.  The library is never linked
directly.

An ELF .note.dlopen metadata note is emitted so that packaging tools
can automatically derive the optional runtime dependency.

Also fix the CFLAGS order in libmount Makemodule.am to ensure local
include paths (-I for blkid, libmount) come before external pkg-config
flags that may pull in system headers.

Signed-off-by: Karel Zak <kzak@redhat.com>
Makefile.am
configure.ac
include/Makemodule.am
include/dl-cryptsetup.h [new file with mode: 0644]
lib/dl-cryptsetup.c [new file with mode: 0644]
lib/meson.build
libmount/meson.build
libmount/src/Makemodule.am
libmount/src/hook_veritydev.c
meson.build
meson_options.txt

index 07ac717b72934c4c2ecd1b5b17fba89b6b0fc5de..4531d23a528faa968d7c0583a91ae7c3544a8a1d 100644 (file)
@@ -218,14 +218,9 @@ edit_cmd += -e 's|@LIBSELINUX[@]||g'
 endif
 
 if HAVE_CRYPTSETUP
-if CRYPTSETUP_VIA_DLOPEN
 edit_cmd += -e 's|@LIBCRYPTSETUP[@]||g'
 edit_cmd += -e 's|@LIBDL[@]|-ldl|g'
 else
-edit_cmd += -e 's|@LIBCRYPTSETUP[@]|libcryptsetup|g'
-edit_cmd += -e 's|@LIBDL[@]||g'
-endif
-else
 edit_cmd += -e 's|@LIBCRYPTSETUP[@]||g'
 edit_cmd += -e 's|@LIBDL[@]||g'
 endif
index f3661932d26818c5cab4f331405a0724f1c0068e..5544fd3d19de9067a02f4ad676953d87358f31ef 100644 (file)
@@ -3076,7 +3076,6 @@ AC_ARG_WITH([cryptsetup],
 
 AS_IF([test "x$with_cryptsetup" = xno], [
   AM_CONDITIONAL([HAVE_CRYPTSETUP], [false])
-  AM_CONDITIONAL([CRYPTSETUP_VIA_DLOPEN], [false])
 ], [
   PKG_CHECK_MODULES([CRYPTSETUP], [libcryptsetup],
        [AC_DEFINE([HAVE_CRYPTSETUP], [1], [Define if cryptsetup is available])
@@ -3089,21 +3088,11 @@ AS_IF([test "x$with_cryptsetup" = xno], [
         AC_CHECK_LIB([cryptsetup], [crypt_activate_by_signed_key], [
          AC_DEFINE([HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY], [1], [Define if crypt_activate_by_signed_key exist in -lcryptsetup])
         ])
-        AS_IF([test "x$with_cryptsetup" = xdlopen], [
-         LIBS="-ldl $LIBS"
-         AC_CHECK_LIB([dl], [dlsym], [
-           AC_DEFINE([CRYPTSETUP_VIA_DLOPEN], [1], [Define if cryptsetup is to be loaded via dlopen])
-           AM_CONDITIONAL([CRYPTSETUP_VIA_DLOPEN], [true])
-          ], [AC_MSG_ERROR([libdl required to build with cryptsetup support])])
-        ], [
-         AM_CONDITIONAL([CRYPTSETUP_VIA_DLOPEN], [false])
-        ])
         CFLAGS="$SAVE_CFLAGS"
         LIBS="$SAVE_LIBS"
         have_cryptsetup=yes],
        [have_cryptsetup=no
    AM_CONDITIONAL([HAVE_CRYPTSETUP], [false])
-   AM_CONDITIONAL([CRYPTSETUP_VIA_DLOPEN], [false])
   ])
 
   AS_CASE([$with_cryptsetup:$have_cryptsetup],
index 70a6cca21c80e8fd5126472b85d4593f449b217f..a0627a56a41c5948eaf8ea707b0f9a6d01cc01d5 100644 (file)
@@ -22,6 +22,7 @@ dist_noinst_HEADERS += \
        include/c_strtod.h \
        include/debug.h \
        include/default_shell.h \
+       include/dl-cryptsetup.h \
        include/dl-utils.h \
        include/encode.h \
        include/env.h \
diff --git a/include/dl-cryptsetup.h b/include/dl-cryptsetup.h
new file mode 100644 (file)
index 0000000..f0d0b41
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * No copyright is claimed.  This code is in the public domain; do with
+ * it what you wish.
+ */
+#ifndef UTIL_LINUX_DL_CRYPTSETUP_H
+#define UTIL_LINUX_DL_CRYPTSETUP_H
+
+#ifdef HAVE_CRYPTSETUP
+
+#include <libcryptsetup.h>
+
+#include "dl-utils.h"
+
+/* Pointers to libcryptsetup functions (initialized by dlsym()) */
+struct ul_cryptsetup_opers {
+       void (*crypt_set_debug_level)(int);
+       void (*crypt_set_log_callback)(struct crypt_device *,
+                       void (*log)(int, const char *, void *), void *);
+       int (*crypt_init_data_device)(struct crypt_device **,
+                       const char *, const char *);
+       int (*crypt_load)(struct crypt_device *, const char *, void *);
+       int (*crypt_get_volume_key_size)(struct crypt_device *);
+#ifdef HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
+       int (*crypt_activate_by_signed_key)(struct crypt_device *,
+                       const char *, const char *, size_t,
+                       const char *, size_t, uint32_t);
+#endif
+       int (*crypt_activate_by_volume_key)(struct crypt_device *,
+                       const char *, const char *, size_t, uint32_t);
+       void (*crypt_free)(struct crypt_device *);
+       int (*crypt_init_by_name)(struct crypt_device **, const char *);
+       int (*crypt_get_verity_info)(struct crypt_device *,
+                       struct crypt_params_verity *);
+       int (*crypt_volume_key_get)(struct crypt_device *, int,
+                       char *, size_t *, const char *, size_t);
+       int (*crypt_deactivate_by_name)(struct crypt_device *,
+                       const char *, uint32_t);
+};
+
+typedef struct ul_cryptsetup_opers ul_cryptsetup_opers;
+
+extern struct ul_cryptsetup_opers ul_cryptsetup;
+
+extern int ul_dlopen_libcryptsetup(void);
+
+#define cryptsetup_call(_func) (ul_cryptsetup._func)
+
+#endif /* HAVE_CRYPTSETUP */
+#endif /* UTIL_LINUX_DL_CRYPTSETUP_H */
diff --git a/lib/dl-cryptsetup.c b/lib/dl-cryptsetup.c
new file mode 100644 (file)
index 0000000..d5c20e8
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * No copyright is claimed.  This code is in the public domain; do with
+ * it what you wish.
+ */
+#include <dlfcn.h>
+
+#include "c.h"
+#include "dl-cryptsetup.h"
+
+#ifdef HAVE_CRYPTSETUP
+
+UL_ELF_NOTE_DLOPEN("cryptsetup",
+                   "Support for dm-verity devices",
+                   UL_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
+                   "libcryptsetup.so.12");
+
+struct ul_cryptsetup_opers ul_cryptsetup;
+
+static const struct ul_dlsym ul_cryptsetup_symbols[] =
+{
+       UL_DLSYM( ul_cryptsetup_opers, crypt_set_debug_level ),
+       UL_DLSYM( ul_cryptsetup_opers, crypt_set_log_callback ),
+       UL_DLSYM( ul_cryptsetup_opers, crypt_init_data_device ),
+       UL_DLSYM( ul_cryptsetup_opers, crypt_load ),
+       UL_DLSYM( ul_cryptsetup_opers, crypt_get_volume_key_size ),
+#ifdef HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
+       UL_DLSYM( ul_cryptsetup_opers, crypt_activate_by_signed_key ),
+#endif
+       UL_DLSYM( ul_cryptsetup_opers, crypt_activate_by_volume_key ),
+       UL_DLSYM( ul_cryptsetup_opers, crypt_free ),
+       UL_DLSYM( ul_cryptsetup_opers, crypt_init_by_name ),
+       UL_DLSYM( ul_cryptsetup_opers, crypt_get_verity_info ),
+       UL_DLSYM( ul_cryptsetup_opers, crypt_volume_key_get ),
+
+       UL_DLSYM( ul_cryptsetup_opers, crypt_deactivate_by_name ),
+};
+
+int ul_dlopen_libcryptsetup(void)
+{
+       /* 0 = not tried, 1 = loaded, -1 = failed */
+       static int status = 0;
+       static void *dl = NULL;
+       int flags = RTLD_LAZY | RTLD_LOCAL;
+
+       if (status)
+               return status > 0 ? 0 : -ENOSYS;
+
+#ifdef RTLD_NODELETE
+       flags |= RTLD_NODELETE;
+#endif
+#ifdef RTLD_DEEPBIND
+       flags |= RTLD_DEEPBIND;
+#endif
+       status = ul_dlopen_symbols("libcryptsetup.so.12", flags,
+                                  ul_cryptsetup_symbols,
+                                  ARRAY_SIZE(ul_cryptsetup_symbols),
+                                  &ul_cryptsetup, &dl) == 0 ? 1 : -1;
+
+       return status > 0 ? 0 : -ENOSYS;
+}
+
+#endif /* HAVE_CRYPTSETUP */
index c193aeda71c2f9f2aed4c1558b61c9f000d43b1b..88b3fd639526e63e0de76a7ed5618372b47280e4 100644 (file)
@@ -63,6 +63,9 @@ lib_common_shells = static_library('common_shells',
   dependencies : lib_econf,
 )
 
+dl_utils_c = files('dl-utils.c')
+dl_cryptsetup_c = files('dl-cryptsetup.c')
+
 selinux_utils_c = files('selinux-utils.c')
 
 caputils_c = files('caputils.c')
index 233e0ac6525edd7303ececf85de91ae1fc4d2366..884a28cdc0f095e14dfa01659123f596016b2e6d 100644 (file)
@@ -71,6 +71,10 @@ if enable_btrfs
   lib_mount_sources += 'src/btrfs.c'
 endif
 
+if lib_cryptsetup.found()
+  lib_mount_sources += [dl_utils_c, dl_cryptsetup_c]
+endif
+
 libmount_sym = 'src/libmount.sym'
 libmount_sym_path = '@0@/@1@'.format(meson.current_source_dir(), libmount_sym)
 
@@ -78,12 +82,17 @@ if build_libmount and not have_dirfd and not have_ddfd
   error('neither dirfd nor ddfd are available')
 endif
 
+lib__mount_compile_deps = [blkid_dep]
+if lib_cryptsetup.found()
+  lib__mount_compile_deps += lib_cryptsetup.partial_dependency(compile_args : true, includes : true)
+endif
+
 lib__mount = static_library(
   '_mount',
   lib_mount_sources,
   include_directories : [dir_include,
                          dir_libmount],
-  dependencies : [blkid_dep])
+  dependencies : lib__mount_compile_deps)
 
 lib_mount_static = static_library(
   'mount_static',
@@ -95,7 +104,7 @@ mount_static_dep = declare_dependency(link_with: lib_mount_static, include_direc
 
 lib__mount_deps = [
   lib_selinux,
-  cryptsetup_dlopen ? lib_dl : lib_cryptsetup,
+  lib_dl,
   realtime_libs
 ]
 
index a017543b74e108cc07ff3d3c320fd4cee11d55cb..c854a0bc059c679bef2ace545327ba940154acb2 100644 (file)
@@ -62,11 +62,12 @@ libmount_la_LIBADD = \
        $(REALTIME_LIBS)
 
 if HAVE_CRYPTSETUP
-if CRYPTSETUP_VIA_DLOPEN
+libmount_la_SOURCES += \
+       lib/dl-utils.c \
+       include/dl-utils.h \
+       lib/dl-cryptsetup.c \
+       include/dl-cryptsetup.h
 libmount_la_LIBADD += -ldl
-else
-libmount_la_LIBADD += $(CRYPTSETUP_LIBS)
-endif
 endif
 
 if USE_LIBMOUNT_UDEV_SUPPORT
@@ -75,11 +76,11 @@ endif
 
 libmount_la_CFLAGS = \
        $(AM_CFLAGS) \
-       $(SOLIB_CFLAGS) \
-       $(CRYPTSETUP_CFLAGS) \
        -I$(ul_libblkid_incdir) \
        -I$(ul_libmount_incdir) \
-       -I$(top_srcdir)/libmount/src
+       -I$(top_srcdir)/libmount/src \
+       $(SOLIB_CFLAGS) \
+       $(CRYPTSETUP_CFLAGS)
 
 EXTRA_libmount_la_DEPENDENCIES = \
        libmount/src/libmount.sym
@@ -120,11 +121,7 @@ libmount_tests_ldadd += $(SELINUX_LIBS)
 endif
 
 if HAVE_CRYPTSETUP
-if CRYPTSETUP_VIA_DLOPEN
 libmount_tests_ldadd += -ldl
-else
-libmount_tests_ldadd += $(CRYPTSETUP_LIBS)
-endif
 endif
 
 test_mount_cache_SOURCES = libmount/src/cache.c
index 9a8d9f6fe55e0d3ff2257d877d7513cc1c2b3c5a..92c89b0bec88207d2739f3d135be5d9549e90c7f 100644 (file)
 
 #ifdef HAVE_CRYPTSETUP
 
-#include <libcryptsetup.h>
+#include "dl-cryptsetup.h"
 #include "path.h"
 #include "strutils.h"
 #include "fileutils.h"
 #include "pathnames.h"
 
-#ifdef CRYPTSETUP_VIA_DLOPEN
-# include <dlfcn.h>
-
-/* Pointers to libcryptsetup functions (initialized by dlsym()) */
-struct verity_opers {
-       void (*crypt_set_debug_level)(int);
-       void (*crypt_set_log_callback)(struct crypt_device *, void (*log)(int, const char *, void *), void *);
-       int (*crypt_init_data_device)(struct crypt_device **, const char *, const char *);
-       int (*crypt_load)(struct crypt_device *, const char *, void *);
-       int (*crypt_get_volume_key_size)(struct crypt_device *);
-# ifdef HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
-       int (*crypt_activate_by_signed_key)(struct crypt_device *, const char *, const char *, size_t, const char *, size_t, uint32_t);
-# endif
-       int (*crypt_activate_by_volume_key)(struct crypt_device *, const char *, const char *, size_t, uint32_t);
-       void (*crypt_free)(struct crypt_device *);
-       int (*crypt_init_by_name)(struct crypt_device **, const char *);
-       int (*crypt_get_verity_info)(struct crypt_device *, struct crypt_params_verity *);
-       int (*crypt_volume_key_get)(struct crypt_device *, int, char *, size_t *, const char *, size_t);
-
-       int (*crypt_deactivate_by_name)(struct crypt_device *, const char *, uint32_t);
-};
-
-/* libcryptsetup functions names and offsets in 'struct verity_opers' */
-struct verity_sym {
-       const char *name;
-       size_t offset;          /* offset of the symbol in verity_opers */
-};
-
-# define DEF_VERITY_SYM(_name) \
-       { \
-               .name = # _name, \
-               .offset = offsetof(struct verity_opers, _name), \
-       }
-
-/* All required symbols */
-static const struct verity_sym verity_symbols[] =
-{
-       DEF_VERITY_SYM( crypt_set_debug_level ),
-       DEF_VERITY_SYM( crypt_set_log_callback ),
-       DEF_VERITY_SYM( crypt_init_data_device ),
-       DEF_VERITY_SYM( crypt_load ),
-       DEF_VERITY_SYM( crypt_get_volume_key_size ),
-# ifdef HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
-       DEF_VERITY_SYM( crypt_activate_by_signed_key ),
-# endif
-       DEF_VERITY_SYM( crypt_activate_by_volume_key ),
-       DEF_VERITY_SYM( crypt_free ),
-       DEF_VERITY_SYM( crypt_init_by_name ),
-       DEF_VERITY_SYM( crypt_get_verity_info ),
-       DEF_VERITY_SYM( crypt_volume_key_get ),
-
-       DEF_VERITY_SYM( crypt_deactivate_by_name ),
-};
-#endif /* CRYPTSETUP_VIA_DLOPEN */
-
-
 /* Data used by all verity hooks */
 struct hookset_data {
        char *devname;                  /* the device */
-#ifdef CRYPTSETUP_VIA_DLOPEN
-       void *dl;                       /* dlopen() */
-       struct verity_opers dl_funcs;   /* dlsym() */
-#endif
 };
 
-/* libcryptsetup call -- dlopen version requires 'struct hookset_data *hsd' */
-#ifdef CRYPTSETUP_VIA_DLOPEN
-# define verity_call(_func)    (hsd->dl_funcs._func)
-#else
-# define verity_call(_func)    (_func)
-#endif
+#define verity_call(_func)     cryptsetup_call(_func)
 
 
 static void delete_veritydev(struct libmnt_context *cxt,
@@ -101,63 +36,6 @@ static void delete_veritydev(struct libmnt_context *cxt,
                         struct hookset_data *hsd);
 
 
-#ifdef CRYPTSETUP_VIA_DLOPEN
-static int load_libcryptsetup_symbols(struct libmnt_context *cxt,
-                                     struct hookset_data *hsd)
-{
-       size_t i;
-       const char *errmsg = NULL;
-       int flags = RTLD_LAZY | RTLD_LOCAL;
-
-       assert(cxt);
-       assert(hsd);
-       assert(hsd->dl == NULL);
-
-       /* glibc extension: mnt_context_deferred_delete_veritydev is called immediately after, don't unload on dl_close */
-#ifdef RTLD_NODELETE
-       flags |= RTLD_NODELETE;
-#endif
-       /* glibc extension: might help to avoid further symbols clashes */
-#ifdef RTLD_DEEPBIND
-       flags |= RTLD_DEEPBIND;
-#endif
-       /* Don't rely on errno; the dlopen API may not use it. Also, note that
-        * dlerror() provides complete messages that do not need any additional
-        * introduction when printed to end users. */
-       errno = 0;
-
-       hsd->dl = dlopen("libcryptsetup.so.12", flags);
-       if (!hsd->dl) {
-               errmsg = dlerror();
-               if (errmsg)
-                       mnt_context_sprintf_mesg(cxt, "e %s", errmsg);
-               goto failed;
-       }
-
-       /* clear errors first, then load all the libcryptsetup symbols */
-       dlerror();
-
-       /* dlsym()  */
-       for (i = 0; i < ARRAY_SIZE(verity_symbols); i++) {
-               const struct verity_sym *def = &verity_symbols[i];
-               void **sym;
-
-               sym = (void **) ((char *) (&hsd->dl_funcs) + def->offset);
-               *sym = dlsym(hsd->dl, def->name);
-
-               errmsg = dlerror();
-               if (errmsg) {
-                       mnt_context_sprintf_mesg(cxt, "e %s", errmsg);
-                       goto failed;
-               }
-       }
-       return 0;
-failed:
-       if (!errno)
-               errno = ENOTSUP;
-       return -errno;
-}
-#endif
 
 /* libcryptsetup callback */
 static void libcryptsetup_log(int level __attribute__((__unused__)),
@@ -177,10 +55,6 @@ static void free_hookset_data(      struct libmnt_context *cxt,
                return;
        if (hsd->devname)
                delete_veritydev(cxt, hs, hsd);
-#ifdef CRYPTSETUP_VIA_DLOPEN
-       if (hsd->dl)
-               dlclose(hsd->dl);
-#endif
        free(hsd);
        mnt_context_set_hookset_data(cxt, hs, NULL);
 }
@@ -192,6 +66,9 @@ static struct hookset_data *new_hookset_data(
 {
        struct hookset_data *hsd;
 
+       if (ul_dlopen_libcryptsetup() != 0)
+               return NULL;
+
        hsd = calloc(1, sizeof(struct hookset_data));
        if (!hsd)
                return NULL;
@@ -199,10 +76,6 @@ static struct hookset_data *new_hookset_data(
        if (mnt_context_set_hookset_data(cxt, hs, hsd) != 0)
                goto failed;
 
-#ifdef CRYPTSETUP_VIA_DLOPEN
-       if (load_libcryptsetup_symbols(cxt, hsd) != 0)
-               goto failed;
-#endif
        if (mnt_context_is_verbose(cxt))
                verity_call( crypt_set_debug_level(CRYPT_DEBUG_ALL) );
 
index 5f556ebced140e24f0c67b48d4e04d915521f3ff..c4fe2c6f329ff0828362d453d340680c21c0dbb1 100644 (file)
@@ -459,23 +459,16 @@ lib_cryptsetup = dependency(
   required : get_option('cryptsetup'))
 conf.set('HAVE_CRYPTSETUP', lib_cryptsetup.found() ? 1 : false)
 
-cryptsetup_dlopen = get_option('cryptsetup').allowed() and get_option('cryptsetup-dlopen').enabled()
-if cryptsetup_dlopen
-  if meson.version().version_compare('>= 0.62.0')
-    lib_dl = dependency('dl')
-  else
-    lib_dl = cc.find_library('dl')
-  endif
-  conf.set('CRYPTSETUP_VIA_DLOPEN', 1)
-  summary('cryptsetup support (dlopen)',
-          'enabled',
-          section : 'components')
+if meson.version().version_compare('>= 0.62.0')
+  lib_dl = dependency('dl')
 else
-  summary('cryptsetup support',
-          lib_cryptsetup.found()  ? 'enabled' : 'disabled',
-          section : 'components')
+  lib_dl = cc.find_library('dl')
 endif
 
+summary('cryptsetup support (dlopen)',
+        lib_cryptsetup.found() ? 'enabled' : 'disabled',
+        section : 'components')
+
 have = cc.has_function(
   'crypt_activate_by_signed_key',
   dependencies : lib_cryptsetup)
index 527246fd49ce76c49f70874b9a4c9228cbad2d6a..5ce19ce5b29a3069a2c8fa538c3698dbc8924fe0 100644 (file)
@@ -6,7 +6,9 @@ option('ncurses',     type : 'feature')
 option('slang',       type : 'feature', value : 'disabled',
        description : 'compile cfdisk with slang rather than ncurses')
 option('cryptsetup',  type : 'feature')
-option('cryptsetup-dlopen',  type : 'feature')
+option('cryptsetup-dlopen',  type : 'feature',
+       deprecated : true,
+       description : 'Deprecated, dlopen is now always used')
 option('zlib',        type : 'feature')
 option('readline',    type : 'feature')
 option('nls',         type : 'feature')